From b05e84ddf8cc5213dbf2939be511ccef268072b8 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:03:05 +0100 Subject: [PATCH] Interpreter GenerationInputs (#1455) * Add initialization with GenerationInputs to the interpreter * Add new_with_generation_inputs_and_kernel * Apply comments --- evm/src/cpu/kernel/interpreter.rs | 151 ++++++++++++++++++++++++++- evm/src/cpu/kernel/tests/add11.rs | 167 +++++++++++++----------------- evm/src/generation/state.rs | 2 +- 3 files changed, 222 insertions(+), 98 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index b99ccaeb..227df021 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -5,7 +5,8 @@ use std::collections::HashMap; use std::ops::Range; use anyhow::bail; -use ethereum_types::{U256, U512}; +use eth_trie_utils::partial_trie::PartialTrie; +use ethereum_types::{BigEndianHash, H160, H256, U256, U512}; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -16,11 +17,13 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; use crate::cpu::stack::MAX_USER_STACK_SIZE; use crate::extension_tower::BN_BASE; +use crate::generation::mpt::load_all_mpts; use crate::generation::prover_input::ProverInputFn; -use crate::generation::state::GenerationState; +use crate::generation::rlp::all_rlp_prover_inputs_reversed; +use crate::generation::state::{all_withdrawals_prover_inputs_reversed, GenerationState}; use crate::generation::GenerationInputs; use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::util::u256_to_usize; +use crate::util::{h2u, u256_to_usize}; use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::gas::gas_to_charge; use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; @@ -150,6 +153,18 @@ impl<'a> Interpreter<'a> { result } + /// Returns an instance of `Interpreter` given `GenerationInputs`, and assuming we are + /// initializing with the `KERNEL` code. + pub(crate) fn new_with_generation_inputs_and_kernel( + initial_offset: usize, + initial_stack: Vec, + inputs: GenerationInputs, + ) -> Self { + let mut result = Self::new_with_kernel(initial_offset, initial_stack); + result.initialize_interpreter_state_with_kernel(inputs); + result + } + pub(crate) fn new( code: &'a [u8], initial_offset: usize, @@ -181,6 +196,136 @@ impl<'a> Interpreter<'a> { result } + /// Initializes the interpreter state given `GenerationInputs`, using the KERNEL code. + pub(crate) fn initialize_interpreter_state_with_kernel(&mut self, inputs: GenerationInputs) { + self.initialize_interpreter_state(inputs, KERNEL.code_hash, KERNEL.code.len()); + } + + /// Initializes the interpreter state given `GenerationInputs`. + pub(crate) fn initialize_interpreter_state( + &mut self, + inputs: GenerationInputs, + kernel_hash: H256, + kernel_code_len: usize, + ) { + let tries = &inputs.tries; + + // Set state's inputs. + self.generation_state.inputs = inputs.clone(); + + // Initialize the MPT's pointers. + let (trie_root_ptrs, trie_data) = + load_all_mpts(tries).expect("Invalid MPT data for preinitialization"); + let trie_roots_after = &inputs.trie_roots_after; + self.generation_state.trie_root_ptrs = trie_root_ptrs; + + // Initialize the `TrieData` segment. + for (i, data) in trie_data.iter().enumerate() { + let trie_addr = MemoryAddress::new(0, Segment::TrieData, i); + self.generation_state.memory.set(trie_addr, data.into()); + } + + // Update the RLP and withdrawal prover inputs. + let rlp_prover_inputs = + all_rlp_prover_inputs_reversed(inputs.clone().signed_txn.as_ref().unwrap_or(&vec![])); + let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); + self.generation_state.rlp_prover_inputs = rlp_prover_inputs; + self.generation_state.withdrawal_prover_inputs = withdrawal_prover_inputs; + + // Set `GlobalMetadata` values. + let metadata = &inputs.block_metadata; + let global_metadata_to_set = [ + ( + GlobalMetadata::BlockBeneficiary, + U256::from_big_endian(&metadata.block_beneficiary.0), + ), + (GlobalMetadata::BlockTimestamp, metadata.block_timestamp), + (GlobalMetadata::BlockNumber, metadata.block_number), + (GlobalMetadata::BlockDifficulty, metadata.block_difficulty), + ( + GlobalMetadata::BlockRandom, + metadata.block_random.into_uint(), + ), + (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), + (GlobalMetadata::BlockChainId, metadata.block_chain_id), + (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), + ( + GlobalMetadata::BlockCurrentHash, + h2u(inputs.block_hashes.cur_hash), + ), + (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), + (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), + (GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after), + (GlobalMetadata::TxnNumberBefore, inputs.txn_number_before), + ( + GlobalMetadata::TxnNumberAfter, + inputs.txn_number_before + if inputs.signed_txn.is_some() { 1 } else { 0 }, + ), + ( + GlobalMetadata::StateTrieRootDigestBefore, + h2u(tries.state_trie.hash()), + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + h2u(tries.transactions_trie.hash()), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + h2u(tries.receipts_trie.hash()), + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + h2u(trie_roots_after.state_root), + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + h2u(trie_roots_after.transactions_root), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + h2u(trie_roots_after.receipts_root), + ), + (GlobalMetadata::KernelHash, h2u(kernel_hash)), + (GlobalMetadata::KernelLen, kernel_code_len.into()), + ]; + + self.set_global_metadata_multi_fields(&global_metadata_to_set); + + // Set final block bloom values. + let final_block_bloom_fields = (0..8) + .map(|i| { + ( + MemoryAddress::new_u256s( + U256::zero(), + (Segment::GlobalBlockBloom.unscale()).into(), + i.into(), + ) + .unwrap(), + metadata.block_bloom[i], + ) + }) + .collect::>(); + + self.set_memory_multi_addresses(&final_block_bloom_fields); + + // Set previous block hash. + let block_hashes_fields = (0..256) + .map(|i| { + ( + MemoryAddress::new_u256s( + U256::zero(), + (Segment::BlockHashes.unscale()).into(), + i.into(), + ) + .unwrap(), + h2u(inputs.block_hashes.prev_hashes[i]), + ) + }) + .collect::>(); + + self.set_memory_multi_addresses(&block_hashes_fields); + } + fn checkpoint(&self) -> InterpreterCheckpoint { let registers = InterpreterRegistersState { kernel_mode: self.is_kernel(), diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 1e71d60d..89cdf82d 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use anyhow::{anyhow, Result}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; -use ethereum_types::{Address, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; @@ -17,56 +17,9 @@ use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::proof::TrieRoots; +use crate::proof::{BlockHashes, BlockMetadata, TrieRoots}; use crate::util::h2u; - -// Stolen from `tests/mpt/insert.rs` -// Prepare the interpreter by loading the initial MPTs and -// by setting all `GlobalMetadata` and necessary code into memory. -fn prepare_interpreter( - interpreter: &mut Interpreter, - trie_inputs: TrieInputs, - transaction: &[u8], - contract_code: HashMap>, -) { - initialize_mpts(interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Set necessary `GlobalMetadata`. - let global_metadata_to_set = [ - ( - GlobalMetadata::StateTrieRootDigestBefore, - h2u(trie_inputs.state_trie.hash()), - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - h2u(trie_inputs.transactions_trie.hash()), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - h2u(trie_inputs.receipts_trie.hash()), - ), - (GlobalMetadata::TxnNumberAfter, 1.into()), - (GlobalMetadata::BlockGasUsedAfter, 0xa868u64.into()), - (GlobalMetadata::BlockGasLimit, 1_000_000.into()), - (GlobalMetadata::BlockBaseFee, 10.into()), - ( - GlobalMetadata::BlockBeneficiary, - U256::from_big_endian( - &Address::from(hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba")).0, - ), - ), - ]; - - interpreter.set_global_metadata_multi_fields(&global_metadata_to_set); - - // Set contract code and transaction. - interpreter.generation_state.inputs.contract_code = contract_code; - - interpreter.generation_state.inputs.signed_txn = Some(transaction.to_vec()); - let rlp_prover_inputs = all_rlp_prover_inputs_reversed(transaction); - interpreter.generation_state.rlp_prover_inputs = rlp_prover_inputs; -} +use crate::GenerationInputs; #[test] fn test_add11_yml() { @@ -120,10 +73,8 @@ fn test_add11_yml() { let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); - let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let gas_used = 0xa868u64.into(); - prepare_interpreter(&mut interpreter, tries_before.clone(), &txn, contract_code); let expected_state_trie_after = { let beneficiary_account_after = AccountRlp { nonce: 1.into(), @@ -158,7 +109,7 @@ fn test_add11_yml() { }; let receipt_0 = LegacyReceiptRlp { status: true, - cum_gas_used: 0xa868u64.into(), + cum_gas_used: gas_used, bloom: vec![0; 256].into(), logs: vec![], }; @@ -179,24 +130,41 @@ fn test_add11_yml() { receipts_root: receipts_trie.hash(), }; - // Set trie roots after the transaction was executed. - let metadata_to_set = [ - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(trie_roots_after.receipts_root), - ), - ]; - interpreter.set_global_metadata_multi_fields(&metadata_to_set); + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: gas_used, + block_bloom: [0.into(); 8], + }; - let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; + let tries_inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code: contract_code.clone(), + block_metadata, + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used, + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + let initial_stack = vec![]; + let mut interpreter = + Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); + + let route_txn_label = KERNEL.global_labels["main"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); @@ -259,10 +227,6 @@ fn test_add11_yml_with_exception() { let txn_gas_limit = 400_000; let gas_price = 10; - let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); - - prepare_interpreter(&mut interpreter, tries_before.clone(), &txn, contract_code); // Here, since the transaction fails, it consumes its gas limit, and does nothing else. let expected_state_trie_after = { let beneficiary_account_after = beneficiary_account_before; @@ -308,26 +272,41 @@ fn test_add11_yml_with_exception() { receipts_root: receipts_trie.hash(), }; - // Set trie roots after the transaction was executed. - let metadata_to_set = [ - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(trie_roots_after.receipts_root), - ), - // The gas used in this case is the transaction gas limit - (GlobalMetadata::BlockGasUsedAfter, txn_gas_limit.into()), - ]; - interpreter.set_global_metadata_multi_fields(&metadata_to_set); + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: txn_gas_limit.into(), + block_bloom: [0.into(); 8], + }; - let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; + let tries_inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code: contract_code.clone(), + block_metadata, + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: txn_gas_limit.into(), + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + let initial_stack = vec![]; + let mut interpreter = + Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); + + let route_txn_label = KERNEL.global_labels["main"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index fec2e11c..c2822d1c 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -169,7 +169,7 @@ impl GenerationState { /// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. /// Returns the reversed array. -fn all_withdrawals_prover_inputs_reversed(withdrawals: &[(Address, U256)]) -> Vec { +pub(crate) fn all_withdrawals_prover_inputs_reversed(withdrawals: &[(Address, U256)]) -> Vec { let mut withdrawal_prover_inputs = withdrawals .iter() .flat_map(|w| [U256::from((w.0).0.as_slice()), w.1])