From 42f7038031a3fb4c52947b5a21491c31ef1acc8d Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Mon, 21 Aug 2023 23:32:53 +0100 Subject: [PATCH 1/7] Add blockhash sys opcode --- evm/src/cpu/kernel/asm/core/syscall_stubs.asm | 8 -- evm/src/cpu/kernel/asm/memory/metadata.asm | 39 ++++++ .../cpu/kernel/constants/global_metadata.rs | 49 ++++---- evm/src/cpu/kernel/tests/block_hash.rs | 97 +++++++++++++++ evm/src/cpu/kernel/tests/mod.rs | 1 + evm/src/fixed_recursive_verifier.rs | 55 ++++++++- evm/src/generation/mod.rs | 24 +++- evm/src/generation/prover_input.rs | 5 + evm/src/get_challenges.rs | 33 ++++++ evm/src/memory/segments.rs | 7 +- evm/src/proof.rs | 90 ++++++++++++++ evm/src/recursive_verifier.rs | 111 ++++++++++++++---- evm/src/verifier.rs | 12 ++ evm/tests/add11_yml.rs | 8 +- evm/tests/basic_smart_contract.rs | 8 +- evm/tests/empty_txn_list.rs | 7 +- evm/tests/log_opcode.rs | 21 +++- evm/tests/self_balance_gas_cost.rs | 8 +- evm/tests/simple_transfer.rs | 8 +- 19 files changed, 520 insertions(+), 71 deletions(-) create mode 100644 evm/src/cpu/kernel/tests/block_hash.rs diff --git a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm index 88124256..70a64b87 100644 --- a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm +++ b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm @@ -1,14 +1,6 @@ // Labels for unimplemented syscalls to make the kernel assemble. // Each label should be removed from this file once it is implemented. -// This is a temporary version that returns 0 on all inputs. -// TODO: Fix this. -global sys_blockhash: - // stack: kexit_info, block_number - %charge_gas_const(@GAS_BLOCKHASH) - %stack (kexit_info, block_number) -> (kexit_info, 0) - EXIT_KERNEL - // This is a temporary version that returns the block difficulty (i.e. the old version of this opcode). // TODO: Fix this. // TODO: What semantics will this have for Edge? diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index 8d8eadf3..f00fbce6 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -235,6 +235,45 @@ global sys_basefee: SWAP1 EXIT_KERNEL +global sys_blockhash: + // stack: kexit_info, block_number + %charge_gas_const(@GAS_BLOCKHASH) + SWAP1 + // stack: block_number, kexit_info + %blockhash + EXIT_KERNEL + +global blockhash: + // stack: block_number, retdest + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_NUMBER) + // stack: cur_block_number, block_number, retdest + DUP1 DUP3 %increment GT %jumpi(zero_hash) // if block_number >= cur_block_number + // stack: cur_block_number, block_number, retdest + DUP2 PUSH 256 ADD + // stack: block_number+256, cur_block_number, block_number, retdest + DUP2 GT %jumpi(zero_hash) // if cur_block_number > block_number + 256 + // If we are here, the provided block number is correct + SUB + // stack: cur_block_number - block_number, retdest + PUSH 256 SUB + // stack: block_hash_number, retdest + %mload_kernel(@SEGMENT_BLOCK_HASHES) + SWAP1 JUMP + JUMP + +%macro blockhash + // stack: block_number + %stack (block_number) -> (block_number, %%after) + %jump(blockhash) +%%after: +%endmacro + +zero_hash: + // stack: cur_block_number, block_number, retdest + %pop2 + PUSH 0 SWAP1 + JUMP + %macro update_mem_words // stack: num_words, kexit_info %mem_words diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 4130fcac..01f7d0dc 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -47,44 +47,47 @@ pub(crate) enum GlobalMetadata { BlockGasUsedBefore = 22, /// After current transactions block values. BlockGasUsedAfter = 23, + /// Current block header hash + BlockCurrentHash = 24, + /// Gas to refund at the end of the transaction. - RefundCounter = 24, + RefundCounter = 25, /// Length of the addresses access list. - AccessedAddressesLen = 25, + AccessedAddressesLen = 26, /// Length of the storage keys access list. - AccessedStorageKeysLen = 26, + AccessedStorageKeysLen = 27, /// Length of the self-destruct list. - SelfDestructListLen = 27, + SelfDestructListLen = 28, /// Length of the bloom entry buffer. - BloomEntryLen = 28, + BloomEntryLen = 29, /// Length of the journal. - JournalLen = 29, + JournalLen = 30, /// Length of the `JournalData` segment. - JournalDataLen = 30, + JournalDataLen = 31, /// Current checkpoint. - CurrentCheckpoint = 31, - TouchedAddressesLen = 32, + CurrentCheckpoint = 32, + TouchedAddressesLen = 33, // Gas cost for the access list in type-1 txns. See EIP-2930. - AccessListDataCost = 33, + AccessListDataCost = 34, // Start of the access list in the RLP for type-1 txns. - AccessListRlpStart = 34, + AccessListRlpStart = 35, // Length of the access list in the RLP for type-1 txns. - AccessListRlpLen = 35, + AccessListRlpLen = 36, // Boolean flag indicating if the txn is a contract creation txn. - ContractCreation = 36, - IsPrecompileFromEoa = 37, - CallStackDepth = 38, - /// Transaction logs list length. - LogsLen = 39, - LogsDataLen = 40, - LogsPayloadLen = 41, - TxnNumberBefore = 42, - TxnNumberAfter = 43, + ContractCreation = 37, + IsPrecompileFromEoa = 38, + CallStackDepth = 39, + /// Transaction logs list length + LogsLen = 40, + LogsDataLen = 41, + LogsPayloadLen = 42, + TxnNumberBefore = 43, + TxnNumberAfter = 44, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 44; + pub(crate) const COUNT: usize = 45; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -130,6 +133,7 @@ impl GlobalMetadata { Self::LogsLen, Self::LogsDataLen, Self::LogsPayloadLen, + Self::BlockCurrentHash, Self::TxnNumberBefore, Self::TxnNumberAfter, ] @@ -162,6 +166,7 @@ impl GlobalMetadata { Self::BlockGasUsed => "GLOBAL_METADATA_BLOCK_GAS_USED", Self::BlockGasUsedBefore => "GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE", Self::BlockGasUsedAfter => "GLOBAL_METADATA_BLOCK_GAS_USED_AFTER", + Self::BlockCurrentHash => "GLOBAL_METADATA_BLOCK_CURRENT_HASH", Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER", Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN", Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN", diff --git a/evm/src/cpu/kernel/tests/block_hash.rs b/evm/src/cpu/kernel/tests/block_hash.rs new file mode 100644 index 00000000..98d23108 --- /dev/null +++ b/evm/src/cpu/kernel/tests/block_hash.rs @@ -0,0 +1,97 @@ +use anyhow::Result; +use ethereum_types::{H256, U256}; +use rand::{thread_rng, Rng}; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; + +#[test] +fn test_correct_block_hash() -> Result<()> { + let mut rng = rand::thread_rng(); + + let blockhash_label = KERNEL.global_labels["blockhash"]; + let retdest = 0xDEADBEEFu32.into(); + + let block_number: u8 = rng.gen(); + let initial_stack = vec![retdest, block_number.into()]; + + let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; + + let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); + interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); + interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, 256.into()); + interpreter.run()?; + + let result = interpreter.stack(); + assert_eq!( + result[0], hashes[block_number as usize], + "Resulting block hash {:?} different from expected hash {:?}", + result[0], hashes[block_number as usize] + ); + + Ok(()) +} + +#[test] +fn test_big_index_block_hash() -> Result<()> { + let mut rng = rand::thread_rng(); + + let blockhash_label = KERNEL.global_labels["blockhash"]; + let retdest = 0xDEADBEEFu32.into(); + let cur_block_number = 3; + let block_number: usize = rng.gen::() as usize; + let actual_block_number = block_number + cur_block_number; + let initial_stack = vec![retdest, actual_block_number.into()]; + + let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; + + let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); + interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); + interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); + interpreter.run()?; + + let result = interpreter.stack(); + assert_eq!( + result[0], + 0.into(), + "Resulting block hash {:?} different from expected hash {:?}", + result[0], + 0 + ); + + Ok(()) +} + +#[test] +fn test_small_index_block_hash() -> Result<()> { + let mut rng = rand::thread_rng(); + + let blockhash_label = KERNEL.global_labels["blockhash"]; + let retdest = 0xDEADBEEFu32.into(); + let cur_block_number = 512; + let block_number = rng.gen::() as usize; + let initial_stack = vec![retdest, block_number.into()]; + + let hashes: Vec = (20..277).map(|elt| elt.into()).collect(); + + let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); + interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); + interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); + interpreter.run()?; + + let result = interpreter.stack(); + assert_eq!( + result[0], + 0.into(), + "Resulting block hash {:?} different from expected hash {:?}", + result[0], + 0 + ); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 938a60bd..b66c0162 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -2,6 +2,7 @@ mod account_code; mod balance; mod bignum; mod blake2_f; +mod block_hash; mod bls381; mod bn254; mod core; diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 59b60263..72844db6 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -40,15 +40,15 @@ use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::{get_grand_product_challenge_set_target, GrandProductChallengeSet}; use crate::proof::{ - BlockMetadataTarget, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, + BlockHashesTarget, BlockMetadataTarget, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRootsTarget, }; use crate::prover::prove; use crate::recursive_verifier::{ add_common_recursion_gates, add_virtual_public_values, - get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_block_metadata_target, - set_extra_public_values_target, set_public_value_targets, set_trie_roots_target, - PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, + get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_block_hashes_target, + set_block_metadata_target, set_extra_public_values_target, set_public_value_targets, + set_trie_roots_target, PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::stark::Stark; use crate::util::h256_limbs; @@ -571,6 +571,17 @@ where let lhs_public_values = lhs.public_values(&mut builder); let rhs_public_values = rhs.public_values(&mut builder); + // Connect all block hash values + BlockHashesTarget::connect( + &mut builder, + public_values.block_hashes, + lhs_public_values.block_hashes, + ); + BlockHashesTarget::connect( + &mut builder, + public_values.block_hashes, + rhs_public_values.block_hashes, + ); // Connect all block metadata values. BlockMetadataTarget::connect( &mut builder, @@ -694,6 +705,9 @@ where let parent_block_proof = builder.add_virtual_proof_with_pis(&expected_common_data); let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common); + // Connect block hashes + Self::connect_block_hashes(&mut builder, &parent_block_proof, &agg_root_proof); + let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs); @@ -723,6 +737,29 @@ where } } + /// Connect the 256 block hashes between two blocks + pub fn connect_block_hashes( + builder: &mut CircuitBuilder, + lhs: &ProofWithPublicInputsTarget, + rhs: &ProofWithPublicInputsTarget, + ) { + let lhs_public_values = PublicValuesTarget::from_public_inputs(&lhs.public_inputs); + let rhs_public_values = PublicValuesTarget::from_public_inputs(&rhs.public_inputs); + for i in 0..255 { + for j in 0..8 { + builder.connect( + lhs_public_values.block_hashes.prev_hashes[8 * (i + 1) + j], + rhs_public_values.block_hashes.prev_hashes[8 * i + j], + ); + } + } + let expected_hash = lhs_public_values.block_hashes.cur_hash; + let prev_block_hash = &rhs_public_values.block_hashes.prev_hashes[255 * 8..256 * 8]; + for i in 0..expected_hash.len() { + builder.connect(expected_hash[i], prev_block_hash[i]); + } + } + fn connect_block_proof( builder: &mut CircuitBuilder, has_parent_block: BoolTarget, @@ -886,6 +923,11 @@ where &self.aggregation.circuit.verifier_only, ); + set_block_hashes_target( + &mut agg_inputs, + &self.aggregation.public_values.block_hashes, + &public_values.block_hashes, + ); set_block_metadata_target( &mut agg_inputs, &self.aggregation.public_values.block_metadata, @@ -964,6 +1006,11 @@ where block_inputs .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); + set_block_hashes_target( + &mut block_inputs, + &self.block.public_values.block_hashes, + &public_values.block_hashes, + ); set_extra_public_values_target( &mut block_inputs, &self.block.public_values.extra_block_data, diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index edae12e2..7c69b8dd 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -21,9 +21,9 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::generation::outputs::{get_outputs, GenerationOutputs}; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; -use crate::proof::{BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; +use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::util::h2u; -use crate::witness::memory::{MemoryAddress, MemoryChannel}; +use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp}; use crate::witness::transition::transition; pub mod mpt; @@ -55,6 +55,8 @@ pub struct GenerationInputs { pub block_metadata: BlockMetadata, + pub block_hashes: BlockHashes, + /// A list of known addresses in the input state trie (which itself doesn't hold addresses, /// only state keys). This is only useful for debugging, so that we can return addresses in the /// post-state rather than state keys. (See `GenerationOutputs`, and in particular @@ -100,6 +102,10 @@ fn apply_metadata_and_tries_memops, const D: usize> (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), (GlobalMetadata::BlockChainId, metadata.block_chain_id), (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), + ( + GlobalMetadata::BlockCurrentHash, + U256::from_big_endian(&inputs.block_hashes.cur_hash.0), + ), (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), (GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after), @@ -181,6 +187,19 @@ fn apply_metadata_and_tries_memops, const D: usize> }) .collect::>(), ); + // Write previous block hashes. + ops.extend( + (0..256) + .map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::BlockHashes, i), + state, + h2u(inputs.block_hashes.prev_hashes[i]), + ) + }) + .collect::>(), + ); state.memory.apply_ops(&ops); state.traces.memory_ops.extend(ops); @@ -244,6 +263,7 @@ pub fn generate_traces, const D: usize>( trie_roots_before, trie_roots_after, block_metadata: inputs.block_metadata, + block_hashes: inputs.block_hashes, extra_block_data, }; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 774e85b0..14293289 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -39,6 +39,7 @@ impl GenerationState { "ffe" => self.run_ffe(input_fn), "mpt" => self.run_mpt(), "rlp" => self.run_rlp(), + "current_hash" => self.run_current_hash(), "account_code" => self.run_account_code(input_fn), "bignum_modmul" => self.run_bignum_modmul(), _ => panic!("Unrecognized prover input function."), @@ -118,6 +119,10 @@ impl GenerationState { .unwrap_or_else(|| panic!("Out of RLP data")) } + fn run_current_hash(&mut self) -> U256 { + U256::from_big_endian(&self.inputs.block_hashes.cur_hash.0) + } + /// Account code. fn run_account_code(&mut self, input_fn: &ProverInputFn) -> U256 { match input_fn.0[1].as_str() { diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index 40b511df..c32b8964 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -146,6 +146,37 @@ fn observe_extra_block_data_target< challenger.observe_elements(&extra_data.block_bloom_after); } +fn observe_block_hashes< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + challenger: &mut Challenger, + block_hashes: &BlockHashes, +) { + for i in 0..256 { + challenger.observe_elements( + &u256_limbs::(U256::from_big_endian(&block_hashes.prev_hashes[i].0))[0..8], + ); + } + challenger + .observe_elements(&u256_limbs::(U256::from_big_endian(&block_hashes.cur_hash.0))[0..8]) +} + +fn observe_block_hashes_target< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + challenger: &mut RecursiveChallenger, + block_hashes: &BlockHashesTarget, +) where + C::Hasher: AlgebraicHasher, +{ + challenger.observe_elements(&block_hashes.prev_hashes); + challenger.observe_elements(&block_hashes.cur_hash); +} + pub(crate) fn observe_public_values< F: RichField + Extendable, C: GenericConfig, @@ -157,6 +188,7 @@ pub(crate) fn observe_public_values< observe_trie_roots::(challenger, &public_values.trie_roots_before); observe_trie_roots::(challenger, &public_values.trie_roots_after); observe_block_metadata::(challenger, &public_values.block_metadata); + observe_block_hashes::(challenger, &public_values.block_hashes); observe_extra_block_data::(challenger, &public_values.extra_block_data); } @@ -173,6 +205,7 @@ pub(crate) fn observe_public_values_target< observe_trie_roots_target::(challenger, &public_values.trie_roots_before); observe_trie_roots_target::(challenger, &public_values.trie_roots_after); observe_block_metadata_target::(challenger, &public_values.block_metadata); + observe_block_hashes_target::(challenger, &public_values.block_hashes); observe_extra_block_data_target::(challenger, &public_values.extra_block_data); } diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 6a8fa2d6..afaaf503 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -68,10 +68,12 @@ pub enum Segment { TouchedAddresses = 34, /// List of checkpoints for the current context. Length in `ContextMetadata`. ContextCheckpoints = 35, + /// List of 256 previous block hashes. + BlockHashes = 36, } impl Segment { - pub(crate) const COUNT: usize = 36; + pub(crate) const COUNT: usize = 37; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -111,6 +113,7 @@ impl Segment { Self::JournalCheckpoints, Self::TouchedAddresses, Self::ContextCheckpoints, + Self::BlockHashes, ] } @@ -153,6 +156,7 @@ impl Segment { Segment::JournalCheckpoints => "SEGMENT_JOURNAL_CHECKPOINTS", Segment::TouchedAddresses => "SEGMENT_TOUCHED_ADDRESSES", Segment::ContextCheckpoints => "SEGMENT_CONTEXT_CHECKPOINTS", + Segment::BlockHashes => "SEGMENT_BLOCK_HASHES", } } @@ -195,6 +199,7 @@ impl Segment { Segment::JournalCheckpoints => 256, Segment::TouchedAddresses => 256, Segment::ContextCheckpoints => 256, + Segment::BlockHashes => 256, } } } diff --git a/evm/src/proof.rs b/evm/src/proof.rs index a0ee3517..9dc6140d 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -53,6 +53,7 @@ pub struct PublicValues { pub trie_roots_before: TrieRoots, pub trie_roots_after: TrieRoots, pub block_metadata: BlockMetadata, + pub block_hashes: BlockHashes, pub extra_block_data: ExtraBlockData, } @@ -63,6 +64,22 @@ pub struct TrieRoots { pub receipts_root: H256, } +// There should be 256 previous hashes stored, so the default should also contain 256 values. +impl Default for BlockHashes { + fn default() -> Self { + Self { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockHashes { + pub prev_hashes: Vec, + pub cur_hash: H256, +} + #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct BlockMetadata { pub block_beneficiary: Address, @@ -93,6 +110,7 @@ pub struct PublicValuesTarget { pub trie_roots_before: TrieRootsTarget, pub trie_roots_after: TrieRootsTarget, pub block_metadata: BlockMetadataTarget, + pub block_hashes: BlockHashesTarget, pub extra_block_data: ExtraBlockDataTarget, } @@ -140,6 +158,13 @@ impl PublicValuesTarget { buffer.write_target(block_gas_used)?; buffer.write_target_array(&block_bloom)?; + let BlockHashesTarget { + prev_hashes, + cur_hash, + } = self.block_hashes; + buffer.write_target_array(&prev_hashes)?; + buffer.write_target_array(&cur_hash)?; + let ExtraBlockDataTarget { txn_number_before, txn_number_after, @@ -183,6 +208,11 @@ impl PublicValuesTarget { block_bloom: buffer.read_target_array()?, }; + let block_hashes = BlockHashesTarget { + prev_hashes: buffer.read_target_array()?, + cur_hash: buffer.read_target_array()?, + }; + let extra_block_data = ExtraBlockDataTarget { txn_number_before: buffer.read_target()?, txn_number_after: buffer.read_target()?, @@ -196,6 +226,7 @@ impl PublicValuesTarget { trie_roots_before, trie_roots_after, block_metadata, + block_hashes, extra_block_data, }) } @@ -205,6 +236,7 @@ impl PublicValuesTarget { pis.len() > TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + + BlockHashesTarget::BLOCK_HASHES_SIZE + ExtraBlockDataTarget::SIZE - 1 ); @@ -218,10 +250,17 @@ impl PublicValuesTarget { &pis[TrieRootsTarget::SIZE * 2 ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE], ), + block_hashes: BlockHashesTarget::from_public_inputs( + &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + ..TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + BlockHashesTarget::BLOCK_HASHES_SIZE], + ), extra_block_data: ExtraBlockDataTarget::from_public_inputs( &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + + BlockHashesTarget::BLOCK_HASHES_SIZE + ExtraBlockDataTarget::SIZE], ), } @@ -252,6 +291,12 @@ impl PublicValuesTarget { pv0.block_metadata, pv1.block_metadata, ), + block_hashes: BlockHashesTarget::select( + builder, + condition, + pv0.block_hashes, + pv1.block_hashes, + ), extra_block_data: ExtraBlockDataTarget::select( builder, condition, @@ -411,6 +456,51 @@ impl BlockMetadataTarget { } } +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub struct BlockHashesTarget { + pub prev_hashes: [Target; 2048], + pub cur_hash: [Target; 8], +} + +impl BlockHashesTarget { + const BLOCK_HASHES_SIZE: usize = 2056; + pub fn from_public_inputs(pis: &[Target]) -> Self { + Self { + prev_hashes: pis[0..2048].try_into().unwrap(), + cur_hash: pis[2048..2056].try_into().unwrap(), + } + } + + pub fn select, const D: usize>( + builder: &mut CircuitBuilder, + condition: BoolTarget, + bm0: Self, + bm1: Self, + ) -> Self { + Self { + prev_hashes: core::array::from_fn(|i| { + builder.select(condition, bm0.prev_hashes[i], bm1.prev_hashes[i]) + }), + cur_hash: core::array::from_fn(|i| { + builder.select(condition, bm0.cur_hash[i], bm1.cur_hash[i]) + }), + } + } + + pub fn connect, const D: usize>( + builder: &mut CircuitBuilder, + bm0: Self, + bm1: Self, + ) { + for i in 0..2048 { + builder.connect(bm0.prev_hashes[i], bm1.prev_hashes[i]); + } + for i in 0..8 { + builder.connect(bm0.cur_hash[i], bm1.cur_hash[i]); + } + } +} + #[derive(Eq, PartialEq, Debug, Copy, Clone)] pub struct ExtraBlockDataTarget { pub txn_number_before: Target, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 04037637..320b4341 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -37,9 +37,10 @@ use crate::permutation::{ PermutationCheckDataTarget, }; use crate::proof::{ - BlockMetadata, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, - PublicValuesTarget, StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget, - StarkProofTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, + BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData, + ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget, StarkProof, + StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots, + TrieRootsTarget, }; use crate::stark::Stark; use crate::util::u256_limbs; @@ -587,6 +588,27 @@ pub(crate) fn get_memory_extra_looking_products_circuit< ); }); + // Add block hashes writes. + product = add_data_write( + builder, + challenge, + product, + metadata_segment, + GlobalMetadata::BlockCurrentHash as usize, + &public_values.block_hashes.cur_hash, + ); + let block_hashes_segment = builder.constant(F::from_canonical_u32(Segment::BlockHashes as u32)); + for i in 0..256 { + product = add_data_write( + builder, + challenge, + product, + block_hashes_segment, + i, + &public_values.block_hashes.prev_hashes[8 * i..8 * (i + 1)], + ); + } + // Add block bloom filters writes. let bloom_segment = builder.constant(F::from_canonical_u32(Segment::GlobalBlockBloom as u32)); for i in 0..8 { @@ -598,28 +620,28 @@ pub(crate) fn get_memory_extra_looking_products_circuit< i, &public_values.block_metadata.block_bloom[i * 8..(i + 1) * 8], ); - } - for i in 0..8 { - product = add_data_write( - builder, - challenge, - product, - bloom_segment, - i + 8, - &public_values.extra_block_data.block_bloom_before[i * 8..(i + 1) * 8], - ); - } + for i in 0..8 { + product = add_data_write( + builder, + challenge, + product, + bloom_segment, + i + 8, + &public_values.extra_block_data.block_bloom_before[i * 8..(i + 1) * 8], + ); + } - for i in 0..8 { - product = add_data_write( - builder, - challenge, - product, - bloom_segment, - i + 16, - &public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8], - ); + for i in 0..8 { + product = add_data_write( + builder, + challenge, + product, + bloom_segment, + i + 16, + &public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8], + ); + } } // Add trie roots writes. @@ -729,11 +751,13 @@ pub(crate) fn add_virtual_public_values, const D: u let trie_roots_before = add_virtual_trie_roots(builder); let trie_roots_after = add_virtual_trie_roots(builder); let block_metadata = add_virtual_block_metadata(builder); + let block_hashes = add_virtual_block_hashes(builder); let extra_block_data = add_virtual_extra_block_data(builder); PublicValuesTarget { trie_roots_before, trie_roots_after, block_metadata, + block_hashes, extra_block_data, } } @@ -775,6 +799,17 @@ pub(crate) fn add_virtual_block_metadata, const D: block_bloom, } } + +pub(crate) fn add_virtual_block_hashes, const D: usize>( + builder: &mut CircuitBuilder, +) -> BlockHashesTarget { + let prev_hashes = builder.add_virtual_public_input_arr(); + let cur_hash = builder.add_virtual_public_input_arr(); + BlockHashesTarget { + prev_hashes, + cur_hash, + } +} pub(crate) fn add_virtual_extra_block_data, const D: usize>( builder: &mut CircuitBuilder, ) -> ExtraBlockDataTarget { @@ -894,6 +929,11 @@ pub(crate) fn set_public_value_targets( &public_values_target.block_metadata, &public_values.block_metadata, ); + set_block_hashes_target( + witness, + &public_values_target.block_hashes, + &public_values.block_hashes, + ); set_extra_public_values_target( witness, &public_values_target.extra_block_data, @@ -1008,6 +1048,31 @@ pub(crate) fn set_block_metadata_target( witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs); } +pub(crate) fn set_block_hashes_target( + witness: &mut W, + block_hashes_target: &BlockHashesTarget, + block_hashes: &BlockHashes, +) where + F: RichField + Extendable, + W: Witness, +{ + for i in 0..256 { + let block_hash_limbs: [F; 8] = + u256_limbs::(U256::from_big_endian(&block_hashes.prev_hashes[i].0))[..8] + .try_into() + .unwrap(); + witness.set_target_arr( + &block_hashes_target.prev_hashes[8 * i..8 * (i + 1)], + &block_hash_limbs, + ); + } + let cur_block_hash_limbs: [F; 8] = + u256_limbs::(U256::from_big_endian(&block_hashes.cur_hash.0))[..8] + .try_into() + .unwrap(); + witness.set_target_arr(&block_hashes_target.cur_hash, &cur_block_hash_limbs); +} + pub(crate) fn set_extra_public_values_target( witness: &mut W, ed_target: &ExtraBlockDataTarget, diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 218f41ad..1d93670a 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -176,6 +176,10 @@ where GlobalMetadata::BlockBaseFee, public_values.block_metadata.block_base_fee, ), + ( + GlobalMetadata::BlockCurrentHash, + U256::from_big_endian(&public_values.block_hashes.cur_hash.0), + ), ( GlobalMetadata::BlockGasUsed, public_values.block_metadata.block_gas_used, @@ -242,6 +246,13 @@ where prod = add_data_write(challenge, bloom_segment, prod, index + 16, val); } + // Add Blockhashes writes. + let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32); + for index in 0..256 { + let val = h2u(public_values.block_hashes.prev_hashes[index]); + prod = add_data_write(challenge, block_hashes_segment, prod, index, val); + } + prod } @@ -267,6 +278,7 @@ where row[12] = F::ONE; // timestamp running_product * challenge.combine(row.iter()) } + pub(crate) fn verify_stark_proof_with_challenges< F: RichField + Extendable, C: GenericConfig, diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index 91ddb14e..f628e944 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -5,7 +5,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::Address; +use ethereum_types::{Address, H256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -15,7 +15,7 @@ use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, TrieRoots}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -154,6 +154,10 @@ fn add11_yml() -> anyhow::Result<()> { gas_used_after: 0xa868u64.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index f6d27ac3..4d0a2090 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -5,7 +5,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, U256}; +use ethereum_types::{Address, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -16,7 +16,7 @@ use plonky2_evm::config::StarkConfig; use plonky2_evm::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, TrieRoots}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -171,6 +171,10 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { gas_used_after: gas_used.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 7ad04bdc..806726fc 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -4,6 +4,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; +use ethereum_types::H256; use keccak_hash::keccak; use log::info; use plonky2::field::goldilocks_field::GoldilocksField; @@ -14,7 +15,7 @@ use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, TrieRoots}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::Node; type F = GoldilocksField; @@ -62,6 +63,10 @@ fn test_empty_txn_list() -> anyhow::Result<()> { gas_used_after: 0.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index 4b748cec..271ab945 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -8,7 +8,7 @@ use bytes::Bytes; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, U256}; +use ethereum_types::{Address, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -19,7 +19,7 @@ use plonky2_evm::config::StarkConfig; use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LegacyTransactionRlp, LogRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -232,6 +232,10 @@ fn test_log_opcodes() -> anyhow::Result<()> { block_bloom_before: [0.into(); 8], block_bloom_after, + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; @@ -425,6 +429,10 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { gas_used_after: 21000u64.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; @@ -562,6 +570,10 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { gas_used_after: receipt.cum_gas_used, block_bloom_before: block_bloom_second, block_bloom_after: block_bloom_final, + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; @@ -585,6 +597,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { block_bloom_after: public_values.extra_block_data.block_bloom_after, }, block_metadata: public_values.block_metadata, + block_hashes: public_values.block_hashes, }; // We can duplicate the proofs here because the state hasn't mutated. @@ -857,6 +870,10 @@ fn test_two_txn() -> anyhow::Result<()> { gas_used_after: 42000u64.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index daeccfc6..d3461647 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -5,7 +5,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::Address; +use ethereum_types::{Address, H256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -15,7 +15,7 @@ use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, TrieRoots}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -160,6 +160,10 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { gas_used_after: gas_used.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index f3e2e839..b8c47fe9 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -5,7 +5,7 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, U256}; +use ethereum_types::{Address, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -15,7 +15,7 @@ use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, TrieRoots}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -141,6 +141,10 @@ fn test_simple_transfer() -> anyhow::Result<()> { gas_used_after: 21032.into(), block_bloom_before: [0.into(); 8], block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, addresses: vec![], }; From 4e0fe74a74963a4fd1e8e72e4f75ba18f6102aad Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Tue, 5 Sep 2023 09:53:40 +0100 Subject: [PATCH 2/7] Apply comments --- evm/src/cpu/kernel/asm/memory/metadata.asm | 2 +- evm/src/cpu/kernel/tests/block_hash.rs | 20 +++++++++++++++++++- evm/src/generation/mod.rs | 2 +- evm/src/get_challenges.rs | 9 +++------ evm/src/recursive_verifier.rs | 16 +++++++--------- evm/src/verifier.rs | 2 +- 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index f00fbce6..caa76051 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -249,7 +249,7 @@ global blockhash: // stack: cur_block_number, block_number, retdest DUP1 DUP3 %increment GT %jumpi(zero_hash) // if block_number >= cur_block_number // stack: cur_block_number, block_number, retdest - DUP2 PUSH 256 ADD + DUP2 PUSH 256 %add_or_fault // stack: block_number+256, cur_block_number, block_number, retdest DUP2 GT %jumpi(zero_hash) // if cur_block_number > block_number + 256 // If we are here, the provided block number is correct diff --git a/evm/src/cpu/kernel/tests/block_hash.rs b/evm/src/cpu/kernel/tests/block_hash.rs index 98d23108..bf1932a0 100644 --- a/evm/src/cpu/kernel/tests/block_hash.rs +++ b/evm/src/cpu/kernel/tests/block_hash.rs @@ -76,7 +76,7 @@ fn test_small_index_block_hash() -> Result<()> { let block_number = rng.gen::() as usize; let initial_stack = vec![retdest, block_number.into()]; - let hashes: Vec = (20..277).map(|elt| elt.into()).collect(); + let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); @@ -95,3 +95,21 @@ fn test_small_index_block_hash() -> Result<()> { Ok(()) } + +#[test] +#[should_panic] +fn test_block_hash_with_overflow() { + let blockhash_label = KERNEL.global_labels["blockhash"]; + let retdest = 0xDEADBEEFu32.into(); + let cur_block_number = 1; + let block_number = U256::MAX; + let initial_stack = vec![retdest, block_number]; + + let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; + + let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); + interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); + interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); + let _ = interpreter.run(); +} diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 7c69b8dd..d495f342 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -104,7 +104,7 @@ fn apply_metadata_and_tries_memops, const D: usize> (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), ( GlobalMetadata::BlockCurrentHash, - U256::from_big_endian(&inputs.block_hashes.cur_hash.0), + h2u(inputs.block_hashes.cur_hash), ), (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index c32b8964..d2405b8b 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -13,7 +13,7 @@ use crate::permutation::{ get_n_grand_product_challenge_sets_target, }; use crate::proof::*; -use crate::util::u256_limbs; +use crate::util::{h256_limbs, u256_limbs}; fn observe_root, C: GenericConfig, const D: usize>( challenger: &mut Challenger, @@ -155,12 +155,9 @@ fn observe_block_hashes< block_hashes: &BlockHashes, ) { for i in 0..256 { - challenger.observe_elements( - &u256_limbs::(U256::from_big_endian(&block_hashes.prev_hashes[i].0))[0..8], - ); + challenger.observe_elements(&h256_limbs::(block_hashes.prev_hashes[i])[0..8]); } - challenger - .observe_elements(&u256_limbs::(U256::from_big_endian(&block_hashes.cur_hash.0))[0..8]) + challenger.observe_elements(&h256_limbs::(block_hashes.cur_hash)[0..8]) } fn observe_block_hashes_target< diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 320b4341..66708318 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -43,7 +43,7 @@ use crate::proof::{ TrieRootsTarget, }; use crate::stark::Stark; -use crate::util::u256_limbs; +use crate::util::{h256_limbs, u256_limbs}; use crate::vanishing_poly::eval_vanishing_poly_circuit; use crate::vars::StarkEvaluationTargets; @@ -1057,19 +1057,17 @@ pub(crate) fn set_block_hashes_target( W: Witness, { for i in 0..256 { - let block_hash_limbs: [F; 8] = - u256_limbs::(U256::from_big_endian(&block_hashes.prev_hashes[i].0))[..8] - .try_into() - .unwrap(); + let block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.prev_hashes[i])[..8] + .try_into() + .unwrap(); witness.set_target_arr( &block_hashes_target.prev_hashes[8 * i..8 * (i + 1)], &block_hash_limbs, ); } - let cur_block_hash_limbs: [F; 8] = - u256_limbs::(U256::from_big_endian(&block_hashes.cur_hash.0))[..8] - .try_into() - .unwrap(); + let cur_block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.cur_hash)[..8] + .try_into() + .unwrap(); witness.set_target_arr(&block_hashes_target.cur_hash, &cur_block_hash_limbs); } diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 1d93670a..ff3a246a 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -178,7 +178,7 @@ where ), ( GlobalMetadata::BlockCurrentHash, - U256::from_big_endian(&public_values.block_hashes.cur_hash.0), + h2u(public_values.block_hashes.cur_hash), ), ( GlobalMetadata::BlockGasUsed, From c30b18346f20f006b03131f1a12eaf650d392175 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Tue, 5 Sep 2023 11:33:19 +0100 Subject: [PATCH 3/7] Change h256_ulimbs --- evm/src/get_challenges.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index d2405b8b..d2c8e815 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -157,7 +157,7 @@ fn observe_block_hashes< for i in 0..256 { challenger.observe_elements(&h256_limbs::(block_hashes.prev_hashes[i])[0..8]); } - challenger.observe_elements(&h256_limbs::(block_hashes.cur_hash)[0..8]) + challenger.observe_elements(&h256_limbs::(block_hashes.cur_hash)[0..8]); } fn observe_block_hashes_target< From 1c01d682aad36c37a4f294297ed8fc6499f920c0 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Tue, 5 Sep 2023 15:24:33 +0100 Subject: [PATCH 4/7] Fix overflow check and test. Remove [..8] when using h256_limbs. --- evm/src/cpu/kernel/asm/memory/metadata.asm | 5 ++++- evm/src/cpu/kernel/tests/block_hash.rs | 16 +++++++++++++--- evm/src/get_challenges.rs | 4 ++-- evm/src/recursive_verifier.rs | 6 ++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index caa76051..5b1417da 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -247,9 +247,12 @@ global blockhash: // stack: block_number, retdest %mload_global_metadata(@GLOBAL_METADATA_BLOCK_NUMBER) // stack: cur_block_number, block_number, retdest + // Check for an overflow, since we're incrementing `block_number` afterwards. + DUP2 %eq_const(@U256_MAX) %jumpi(zero_hash) + // stack: cur_block_number, block_number, retdest DUP1 DUP3 %increment GT %jumpi(zero_hash) // if block_number >= cur_block_number // stack: cur_block_number, block_number, retdest - DUP2 PUSH 256 %add_or_fault + DUP2 PUSH 256 ADD // stack: block_number+256, cur_block_number, block_number, retdest DUP2 GT %jumpi(zero_hash) // if cur_block_number > block_number + 256 // If we are here, the provided block number is correct diff --git a/evm/src/cpu/kernel/tests/block_hash.rs b/evm/src/cpu/kernel/tests/block_hash.rs index bf1932a0..23ba2337 100644 --- a/evm/src/cpu/kernel/tests/block_hash.rs +++ b/evm/src/cpu/kernel/tests/block_hash.rs @@ -97,8 +97,7 @@ fn test_small_index_block_hash() -> Result<()> { } #[test] -#[should_panic] -fn test_block_hash_with_overflow() { +fn test_block_hash_with_overflow() -> Result<()> { let blockhash_label = KERNEL.global_labels["blockhash"]; let retdest = 0xDEADBEEFu32.into(); let cur_block_number = 1; @@ -111,5 +110,16 @@ fn test_block_hash_with_overflow() { interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); - let _ = interpreter.run(); + interpreter.run()?; + + let result = interpreter.stack(); + assert_eq!( + result[0], + 0.into(), + "Resulting block hash {:?} different from expected hash {:?}", + result[0], + 0 + ); + + Ok(()) } diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index d2c8e815..59be8439 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -155,9 +155,9 @@ fn observe_block_hashes< block_hashes: &BlockHashes, ) { for i in 0..256 { - challenger.observe_elements(&h256_limbs::(block_hashes.prev_hashes[i])[0..8]); + challenger.observe_elements(&h256_limbs::(block_hashes.prev_hashes[i])); } - challenger.observe_elements(&h256_limbs::(block_hashes.cur_hash)[0..8]); + challenger.observe_elements(&h256_limbs::(block_hashes.cur_hash)); } fn observe_block_hashes_target< diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 66708318..903a9b4d 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -1057,7 +1057,7 @@ pub(crate) fn set_block_hashes_target( W: Witness, { for i in 0..256 { - let block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.prev_hashes[i])[..8] + let block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.prev_hashes[i]) .try_into() .unwrap(); witness.set_target_arr( @@ -1065,9 +1065,7 @@ pub(crate) fn set_block_hashes_target( &block_hash_limbs, ); } - let cur_block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.cur_hash)[..8] - .try_into() - .unwrap(); + let cur_block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.cur_hash).try_into().unwrap(); witness.set_target_arr(&block_hashes_target.cur_hash, &cur_block_hash_limbs); } From ddf2b81733a79e93570c847a9210368cad19c4f0 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Tue, 5 Sep 2023 15:48:30 +0100 Subject: [PATCH 5/7] Clippy --- evm/src/recursive_verifier.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 903a9b4d..8ba49ecf 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -1057,15 +1057,13 @@ pub(crate) fn set_block_hashes_target( W: Witness, { for i in 0..256 { - let block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.prev_hashes[i]) - .try_into() - .unwrap(); + let block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.prev_hashes[i]); witness.set_target_arr( &block_hashes_target.prev_hashes[8 * i..8 * (i + 1)], &block_hash_limbs, ); } - let cur_block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.cur_hash).try_into().unwrap(); + let cur_block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.cur_hash); witness.set_target_arr(&block_hashes_target.cur_hash, &cur_block_hash_limbs); } From 9a06fc9b95cab6231f7d54a5fc350f403711e271 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Thu, 7 Sep 2023 12:15:17 +0100 Subject: [PATCH 6/7] Fix memop reads, from_prover_inputs and cleanup. --- evm/src/proof.rs | 4 ++- evm/src/recursive_verifier.rs | 61 ++++++++++++++++------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 9dc6140d..14f22b67 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -257,7 +257,9 @@ impl PublicValuesTarget { + BlockHashesTarget::BLOCK_HASHES_SIZE], ), extra_block_data: ExtraBlockDataTarget::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + &pis[TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + BlockHashesTarget::BLOCK_HASHES_SIZE ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::BLOCK_HASHES_SIZE diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 8ba49ecf..67a95c95 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -510,7 +510,7 @@ pub(crate) fn get_memory_extra_looking_products_circuit< let mut product = builder.one(); // Add metadata writes. - let block_fields_without_beneficiary_and_basefee_and_bloom = [ + let block_fields_scalars = [ ( GlobalMetadata::BlockTimestamp as usize, public_values.block_metadata.block_timestamp, @@ -553,7 +553,7 @@ pub(crate) fn get_memory_extra_looking_products_circuit< ), ]; - let beneficiary_base_fee_fields: [(usize, &[Target]); 2] = [ + let beneficiary_base_fee_cur_hash_fields: [(usize, &[Target]); 3] = [ ( GlobalMetadata::BlockBeneficiary as usize, &public_values.block_metadata.block_beneficiary, @@ -562,10 +562,14 @@ pub(crate) fn get_memory_extra_looking_products_circuit< GlobalMetadata::BlockBaseFee as usize, &public_values.block_metadata.block_base_fee, ), + ( + GlobalMetadata::BlockCurrentHash as usize, + &public_values.block_hashes.cur_hash, + ), ]; let metadata_segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32)); - block_fields_without_beneficiary_and_basefee_and_bloom.map(|(field, target)| { + block_fields_scalars.map(|(field, target)| { // Each of those fields fit in 32 bits, hence in a single Target. product = add_data_write( builder, @@ -577,7 +581,7 @@ pub(crate) fn get_memory_extra_looking_products_circuit< ); }); - beneficiary_base_fee_fields.map(|(field, targets)| { + beneficiary_base_fee_cur_hash_fields.map(|(field, targets)| { product = add_data_write( builder, challenge, @@ -589,14 +593,6 @@ pub(crate) fn get_memory_extra_looking_products_circuit< }); // Add block hashes writes. - product = add_data_write( - builder, - challenge, - product, - metadata_segment, - GlobalMetadata::BlockCurrentHash as usize, - &public_values.block_hashes.cur_hash, - ); let block_hashes_segment = builder.constant(F::from_canonical_u32(Segment::BlockHashes as u32)); for i in 0..256 { product = add_data_write( @@ -620,28 +616,27 @@ pub(crate) fn get_memory_extra_looking_products_circuit< i, &public_values.block_metadata.block_bloom[i * 8..(i + 1) * 8], ); + } + for i in 0..8 { + product = add_data_write( + builder, + challenge, + product, + bloom_segment, + i + 8, + &public_values.extra_block_data.block_bloom_before[i * 8..(i + 1) * 8], + ); + } - for i in 0..8 { - product = add_data_write( - builder, - challenge, - product, - bloom_segment, - i + 8, - &public_values.extra_block_data.block_bloom_before[i * 8..(i + 1) * 8], - ); - } - - for i in 0..8 { - product = add_data_write( - builder, - challenge, - product, - bloom_segment, - i + 16, - &public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8], - ); - } + for i in 0..8 { + product = add_data_write( + builder, + challenge, + product, + bloom_segment, + i + 16, + &public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8], + ); } // Add trie roots writes. From 170f7d838a9a792b7a62f46cd83fa0b87e7c3f57 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Thu, 7 Sep 2023 12:35:36 +0100 Subject: [PATCH 7/7] Fix Clippy --- evm/src/generation/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d495f342..9b5219be 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -23,7 +23,7 @@ use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::util::h2u; -use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp}; +use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; pub mod mpt;