From b07644368f549e9f90b931c8ba028999c63a1aee Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Wed, 23 Aug 2023 23:29:58 +0100 Subject: [PATCH 1/3] Add missing links between public values --- evm/src/cpu/kernel/asm/main.asm | 73 +++- .../cpu/kernel/constants/global_metadata.rs | 58 ++- evm/src/fixed_recursive_verifier.rs | 165 +++++++- evm/src/generation/mod.rs | 75 +++- evm/src/generation/mpt.rs | 9 - evm/src/get_challenges.rs | 48 +++ evm/src/memory/segments.rs | 25 +- evm/src/proof.rs | 153 ++++++- evm/src/recursive_verifier.rs | 207 +++++++-- evm/src/verifier.rs | 76 +++- evm/tests/add11_yml.rs | 6 + evm/tests/basic_smart_contract.rs | 5 + evm/tests/empty_txn_list.rs | 5 + evm/tests/log_opcode.rs | 396 +++++++++++++++++- evm/tests/self_balance_gas_cost.rs | 5 + evm/tests/simple_transfer.rs | 6 + 16 files changed, 1208 insertions(+), 104 deletions(-) diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index 39f259e3cc..612b4bcecd 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -2,6 +2,9 @@ global main: // First, initialise the shift table %shift_table_init + // Initialize the block bloom filter + %initialize_block_bloom + // Second, load all MPT data from the prover. PUSH hash_initial_tries %jump(load_all_mpts) @@ -13,9 +16,8 @@ global hash_initial_tries: global start_txns: // stack: (empty) - // Last mpt input is txn_nb. - PROVER_INPUT(mpt) - PUSH 0 + %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE) + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE) // stack: init_used_gas, txn_nb txn_loop: @@ -37,8 +39,71 @@ global txn_loop_after: global hash_final_tries: // stack: cum_gas, txn_nb - %pop2 + // Check that we end up with the correct `cum_gas`, `txn_nb` and bloom filter. + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_AFTER) %assert_eq + %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_AFTER) %assert_eq + %check_metadata_block_bloom %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER) %assert_eq %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER) %assert_eq %mpt_hash_receipt_trie %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER) %assert_eq %jump(halt) + +initialize_block_bloom: + // stack: retdest + PUSH 0 PUSH 8 PUSH 0 + +initialize_bloom_loop: + // stack: i, len, offset, retdest + DUP2 DUP2 EQ %jumpi(initialize_bloom_loop_end) + PUSH 32 // Bloom word length + // stack: word_len, i, len, offset, retdest + // Load the next `block_bloom_before` word. + DUP2 %add_const(8) %mload_kernel(@SEGMENT_GLOBAL_BLOCK_BLOOM) + // stack: bloom_word, word_len, i, len, offset, retdest + DUP5 PUSH @SEGMENT_BLOCK_BLOOM PUSH 0 // Bloom word address in SEGMENT_BLOCK_BLOOM + %mstore_unpacking + // stack: new_offset, i, len, old_offset, retdest + SWAP3 POP %increment + // stack: i, len, new_offset, retdest + %jump(initialize_bloom_loop) + +initialize_bloom_loop_end: + // stack: len, len, offset, retdest + %pop3 + JUMP + +%macro initialize_block_bloom + // stack: (empty) + PUSH %%after + %jump(initialize_block_bloom) +%%after: +%endmacro + +check_metadata_block_bloom: + // stack: retdest + PUSH 0 PUSH 8 PUSH 0 + +check_bloom_loop: + // stack: i, len, offset, retdest + DUP2 DUP2 EQ %jumpi(check_bloom_loop_end) + PUSH 32 // Bloom word length + // stack: word_len, i, len, offset, retdest + DUP4 PUSH @SEGMENT_BLOCK_BLOOM PUSH 0 + %mload_packing + // stack: bloom_word, i, len, offset, retdest + DUP2 %add_const(16) %mload_kernel(@SEGMENT_GLOBAL_BLOCK_BLOOM) %assert_eq + // stack: i, len, offset, retdest + %increment SWAP2 %add_const(32) SWAP2 + // stack: i+1, len, new_offset, retdest + %jump(check_bloom_loop) + +check_bloom_loop_end: + // stack: len, len, offset, retdest + %pop3 + JUMP + +%macro check_metadata_block_bloom + PUSH %%after + %jump(check_metadata_block_bloom) +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index f33fe7a17c..4130fcac90 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -42,43 +42,49 @@ pub(crate) enum GlobalMetadata { BlockGasLimit = 18, BlockChainId = 19, BlockBaseFee = 20, - + BlockGasUsed = 21, + /// Before current transactions block values. + BlockGasUsedBefore = 22, + /// After current transactions block values. + BlockGasUsedAfter = 23, /// Gas to refund at the end of the transaction. - RefundCounter = 21, + RefundCounter = 24, /// Length of the addresses access list. - AccessedAddressesLen = 22, + AccessedAddressesLen = 25, /// Length of the storage keys access list. - AccessedStorageKeysLen = 23, + AccessedStorageKeysLen = 26, /// Length of the self-destruct list. - SelfDestructListLen = 24, + SelfDestructListLen = 27, /// Length of the bloom entry buffer. - BloomEntryLen = 25, + BloomEntryLen = 28, /// Length of the journal. - JournalLen = 26, + JournalLen = 29, /// Length of the `JournalData` segment. - JournalDataLen = 27, + JournalDataLen = 30, /// Current checkpoint. - CurrentCheckpoint = 28, - TouchedAddressesLen = 29, + CurrentCheckpoint = 31, + TouchedAddressesLen = 32, // Gas cost for the access list in type-1 txns. See EIP-2930. - AccessListDataCost = 30, + AccessListDataCost = 33, // Start of the access list in the RLP for type-1 txns. - AccessListRlpStart = 31, + AccessListRlpStart = 34, // Length of the access list in the RLP for type-1 txns. - AccessListRlpLen = 32, + AccessListRlpLen = 35, // Boolean flag indicating if the txn is a contract creation txn. - ContractCreation = 33, - IsPrecompileFromEoa = 34, - CallStackDepth = 35, - /// Transaction logs list length - LogsLen = 36, - LogsDataLen = 37, - LogsPayloadLen = 38, + ContractCreation = 36, + IsPrecompileFromEoa = 37, + CallStackDepth = 38, + /// Transaction logs list length. + LogsLen = 39, + LogsDataLen = 40, + LogsPayloadLen = 41, + TxnNumberBefore = 42, + TxnNumberAfter = 43, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 39; + pub(crate) const COUNT: usize = 44; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -103,6 +109,9 @@ impl GlobalMetadata { Self::BlockGasLimit, Self::BlockChainId, Self::BlockBaseFee, + Self::BlockGasUsed, + Self::BlockGasUsedBefore, + Self::BlockGasUsedAfter, Self::RefundCounter, Self::AccessedAddressesLen, Self::AccessedStorageKeysLen, @@ -121,6 +130,8 @@ impl GlobalMetadata { Self::LogsLen, Self::LogsDataLen, Self::LogsPayloadLen, + Self::TxnNumberBefore, + Self::TxnNumberAfter, ] } @@ -148,6 +159,9 @@ impl GlobalMetadata { Self::BlockGasLimit => "GLOBAL_METADATA_BLOCK_GAS_LIMIT", Self::BlockChainId => "GLOBAL_METADATA_BLOCK_CHAIN_ID", Self::BlockBaseFee => "GLOBAL_METADATA_BLOCK_BASE_FEE", + Self::BlockGasUsed => "GLOBAL_METADATA_BLOCK_GAS_USED", + Self::BlockGasUsedBefore => "GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE", + Self::BlockGasUsedAfter => "GLOBAL_METADATA_BLOCK_GAS_USED_AFTER", Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER", Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN", Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN", @@ -166,6 +180,8 @@ impl GlobalMetadata { Self::LogsLen => "GLOBAL_METADATA_LOGS_LEN", Self::LogsDataLen => "GLOBAL_METADATA_LOGS_DATA_LEN", Self::LogsPayloadLen => "GLOBAL_METADATA_LOGS_PAYLOAD_LEN", + Self::TxnNumberBefore => "GLOBAL_METADATA_TXN_NUMBER_BEFORE", + Self::TxnNumberAfter => "GLOBAL_METADATA_TXN_NUMBER_AFTER", } } } diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index e0e7f4cfa4..d9b0c12119 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -2,6 +2,8 @@ use core::mem::{self, MaybeUninit}; use std::collections::BTreeMap; use std::ops::Range; +use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; +use ethereum_types::BigEndianHash; use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; use plonky2::field::extension::Extendable; @@ -39,16 +41,18 @@ use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::{get_grand_product_challenge_set_target, GrandProductChallengeSet}; use crate::proof::{ - BlockMetadataTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRootsTarget, + 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_public_value_targets, set_trie_roots_target, PlonkWrapperCircuit, PublicInputs, - StarkWrapperCircuit, + set_extra_public_values_target, set_public_value_targets, set_trie_roots_target, + PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::stark::Stark; +use crate::util::u256_limbs; /// The recursion threshold. We end a chain of recursive proofs once we reach this size. const THRESHOLD_DEGREE_BITS: usize = 13; @@ -598,6 +602,13 @@ where rhs_public_values.trie_roots_before, ); + Self::connect_extra_public_values( + &mut builder, + &public_values.extra_block_data, + &lhs_public_values.extra_block_data, + &rhs_public_values.extra_block_data, + ); + // Pad to match the root circuit's degree. while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { builder.add_gate(NoopGate, vec![]); @@ -613,6 +624,39 @@ where } } + fn connect_extra_public_values( + builder: &mut CircuitBuilder, + pvs: &ExtraBlockDataTarget, + lhs: &ExtraBlockDataTarget, + rhs: &ExtraBlockDataTarget, + ) { + // Connect the transaction number in public values to the lhs and rhs values correctly. + builder.connect(pvs.txn_number_before, lhs.txn_number_before); + builder.connect(pvs.txn_number_after, rhs.txn_number_after); + + // Connect lhs `txn_number_after`with rhs `txn_number_before`. + builder.connect(lhs.txn_number_after, rhs.txn_number_before); + + // Connect the gas used in public values to the lhs and rhs values correctly. + builder.connect(pvs.gas_used_before, lhs.gas_used_before); + builder.connect(pvs.gas_used_after, rhs.gas_used_after); + + // Connect lhs `gas_used_after`with rhs `gas_used_before`. + builder.connect(lhs.gas_used_after, rhs.gas_used_before); + + // Connect the `block_bloom` in public values to the lhs and rhs values correctly. + for (&limb0, &limb1) in pvs.block_bloom_after.iter().zip(&rhs.block_bloom_after) { + builder.connect(limb0, limb1); + } + for (&limb0, &limb1) in pvs.block_bloom_before.iter().zip(&lhs.block_bloom_before) { + builder.connect(limb0, limb1); + } + // Connect lhs `block_bloom_after`with rhs `block_bloom_before`. + for (&limb0, &limb1) in lhs.block_bloom_after.iter().zip(&rhs.block_bloom_before) { + builder.connect(limb0, limb1); + } + } + fn add_agg_child( builder: &mut CircuitBuilder, root: &RootCircuitData, @@ -651,6 +695,12 @@ 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); + let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); + let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs); + + // Make connections between block proofs, and check initial and final block values. + Self::connect_block_proof(&mut builder, &parent_pv, &agg_pv); + let cyclic_vk = builder.add_verifier_data_public_inputs(); builder .conditionally_verify_cyclic_proof_or_dummy::( @@ -674,6 +724,88 @@ where } } + fn connect_block_proof( + builder: &mut CircuitBuilder, + lhs: &PublicValuesTarget, + rhs: &PublicValuesTarget, + ) { + // Between blocks, we only connect state tries. + for (&limb0, limb1) in lhs + .trie_roots_after + .state_root + .iter() + .zip(rhs.trie_roots_before.state_root) + { + builder.connect(limb0, limb1); + } + + // Connect block numbers. + let one = builder.one(); + let prev_block_nb = builder.sub(rhs.block_metadata.block_number, one); + builder.connect(lhs.block_metadata.block_number, prev_block_nb); + + // Check initial block values. + Self::connect_initial_values_block(builder, rhs); + + // Connect intermediary values for gas_used and bloom filters to the block's final values. We only plug on the right, so there is no need to check the left-handsidee block. + Self::connect_final_block_values_to_intermediary(builder, rhs); + } + + fn connect_final_block_values_to_intermediary( + builder: &mut CircuitBuilder, + x: &PublicValuesTarget, + ) where + F: RichField + Extendable, + { + builder.connect( + x.block_metadata.block_gas_used, + x.extra_block_data.gas_used_after, + ); + + for (&limb0, &limb1) in x + .block_metadata + .block_bloom + .iter() + .zip(&x.extra_block_data.block_bloom_after) + { + builder.connect(limb0, limb1); + } + } + + fn connect_initial_values_block(builder: &mut CircuitBuilder, x: &PublicValuesTarget) + where + F: RichField + Extendable, + { + let zero = builder.constant(F::ZERO); + // The initial number of transactions is 0. + builder.connect(x.extra_block_data.txn_number_before, zero); + // The initial gas used is 0 + builder.connect(x.extra_block_data.gas_used_before, zero); + + // The initial bloom filter is all zeroes. + let initial_bloom = builder.constants(&[F::ZERO; 64]); + for i in 0..x.extra_block_data.block_bloom_before.len() { + builder.connect(x.extra_block_data.block_bloom_before[i], initial_bloom[i]); + } + + // The transactions and receipts tries are empty at the beginning of the block. + let initial_trie = HashedPartialTrie::from(Node::Empty).hash(); + + for (i, limb) in initial_trie.into_uint().0.into_iter().enumerate() { + let temp = builder.constant(F::from_canonical_u32(limb as u32)); + builder.connect(x.trie_roots_before.transactions_root[2 * i], temp); + let temp2 = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); + builder.connect(x.trie_roots_before.transactions_root[2 * i + 1], temp2); + } + + for (i, limb) in initial_trie.into_uint().0.into_iter().enumerate() { + let temp = builder.constant(F::from_canonical_u32(limb as u32)); + builder.connect(x.trie_roots_before.receipts_root[2 * i], temp); + let temp2 = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); + builder.connect(x.trie_roots_before.receipts_root[2 * i + 1], temp2); + } + } + /// Create a proof for each STARK, then combine them, eventually culminating in a root proof. pub fn prove_root( &self, @@ -771,7 +903,11 @@ where &self.aggregation.public_values.trie_roots_after, &public_values.trie_roots_after, ); - + set_extra_public_values_target( + &mut agg_inputs, + &self.aggregation.public_values.extra_block_data, + &public_values.extra_block_data, + ); let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?; Ok((aggregation_proof, public_values)) } @@ -804,12 +940,26 @@ where block_inputs .set_proof_with_pis_target(&self.block.parent_block_proof, parent_block_proof); } else { + // Initialize state_root_after and the block number for correct connection between blocks. + let state_trie_root_keys = 24..32; + let block_number_key = TrieRootsTarget::SIZE * 2 + 6; + let mut nonzero_pis = HashMap::new(); + for (key, &value) in state_trie_root_keys.zip_eq(&u256_limbs::( + public_values.trie_roots_before.state_root.into_uint(), + )) { + nonzero_pis.insert(key, value); + } + nonzero_pis.insert( + block_number_key, + F::from_canonical_usize(public_values.block_metadata.block_number.as_usize()) + - F::ONE, + ); block_inputs.set_proof_with_pis_target( &self.block.parent_block_proof, &cyclic_base_proof( &self.block.circuit.common, &self.block.circuit.verifier_only, - HashMap::new(), + nonzero_pis, ), ); } @@ -819,6 +969,11 @@ where block_inputs .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); + set_extra_public_values_target( + &mut block_inputs, + &self.block.public_values.extra_block_data, + &public_values.extra_block_data, + ); set_block_metadata_target( &mut block_inputs, &self.block.public_values.block_metadata, diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 5beded4e9b..edae12e236 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -21,7 +21,7 @@ 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, PublicValues, TrieRoots}; +use crate::proof::{BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::util::h2u; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -38,6 +38,12 @@ use crate::witness::util::mem_write_log; /// Inputs needed for trace generation. #[derive(Clone, Debug, Deserialize, Serialize, Default)] pub struct GenerationInputs { + pub txn_number_before: U256, + pub gas_used_before: U256, + pub block_bloom_before: [U256; 8], + pub gas_used_after: U256, + pub block_bloom_after: [U256; 8], + pub signed_txns: Vec>, pub tries: TrieInputs, /// Expected trie roots after the transactions are executed. @@ -94,6 +100,14 @@ 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::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 + inputs.signed_txns.len(), + ), ( GlobalMetadata::StateTrieRootDigestBefore, h2u(tries.state_trie.hash()), @@ -121,14 +135,52 @@ fn apply_metadata_and_tries_memops, const D: usize> ]; let channel = MemoryChannel::GeneralPurpose(0); - let ops = fields.map(|(field, val)| { + let mut ops = fields + .map(|(field, val)| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + state, + val, + ) + }) + .to_vec(); + + // Write the block's final block bloom filter. + ops.extend((0..8).map(|i| { mem_write_log( channel, - MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + MemoryAddress::new(0, Segment::GlobalBlockBloom, i), state, - val, + metadata.block_bloom[i], ) - }); + })); + // Write the block's bloom filter before the current transaction. + ops.extend( + (0..8) + .map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalBlockBloom, i + 8), + state, + inputs.block_bloom_before[i], + ) + }) + .collect::>(), + ); + // Write the block's bloom filter after the current transaction. + ops.extend( + (0..8) + .map(|i| { + mem_write_log( + channel, + MemoryAddress::new(0, Segment::GlobalBlockBloom, i + 16), + state, + inputs.block_bloom_after[i], + ) + }) + .collect::>(), + ); state.memory.apply_ops(&ops); state.traces.memory_ops.extend(ops); @@ -176,10 +228,23 @@ pub fn generate_traces, const D: usize>( receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestAfter)), }; + let gas_used_after = read_metadata(GlobalMetadata::BlockGasUsedAfter); + let txn_number_after = read_metadata(GlobalMetadata::TxnNumberAfter); + + let extra_block_data = ExtraBlockData { + txn_number_before: inputs.txn_number_before, + txn_number_after, + gas_used_before: inputs.gas_used_before, + gas_used_after, + block_bloom_before: inputs.block_bloom_before, + block_bloom_after: inputs.block_bloom_after, + }; + let public_values = PublicValues { trie_roots_before, trie_roots_after, block_metadata: inputs.block_metadata, + extra_block_data, }; let tables = timed!( diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index 23e3569452..47129ed0f6 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -123,15 +123,6 @@ pub(crate) fn all_mpt_prover_inputs(trie_inputs: &TrieInputs) -> Vec { &parse_receipts, ); - // Temporary! The actual number of transactions in the trie cannot be known if the trie - // contains hash nodes. - let num_transactions = trie_inputs - .transactions_trie - .values() - .collect::>() - .len(); - prover_inputs.push(num_transactions.into()); - prover_inputs } diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index 9b03b80dd1..40b511df57 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -79,6 +79,12 @@ fn observe_block_metadata< challenger.observe_element(F::from_canonical_u32( (block_metadata.block_base_fee.as_u64() >> 32) as u32, )); + challenger.observe_element(F::from_canonical_u32( + block_metadata.block_gas_used.as_u32(), + )); + for i in 0..8 { + challenger.observe_elements(&u256_limbs(block_metadata.block_bloom[i])); + } } fn observe_block_metadata_target< @@ -98,6 +104,46 @@ fn observe_block_metadata_target< challenger.observe_element(block_metadata.block_gaslimit); challenger.observe_element(block_metadata.block_chain_id); challenger.observe_elements(&block_metadata.block_base_fee); + challenger.observe_element(block_metadata.block_gas_used); + challenger.observe_elements(&block_metadata.block_bloom); +} + +fn observe_extra_block_data< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + challenger: &mut Challenger, + extra_data: &ExtraBlockData, +) { + challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_before.as_u32())); + challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_after.as_u32())); + challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_before.as_u32())); + challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_after.as_u32())); + for i in 0..8 { + challenger.observe_elements(&u256_limbs(extra_data.block_bloom_before[i])); + } + for i in 0..8 { + challenger.observe_elements(&u256_limbs(extra_data.block_bloom_after[i])); + } +} + +fn observe_extra_block_data_target< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + challenger: &mut RecursiveChallenger, + extra_data: &ExtraBlockDataTarget, +) where + C::Hasher: AlgebraicHasher, +{ + challenger.observe_element(extra_data.txn_number_before); + challenger.observe_element(extra_data.txn_number_after); + challenger.observe_element(extra_data.gas_used_before); + challenger.observe_element(extra_data.gas_used_after); + challenger.observe_elements(&extra_data.block_bloom_before); + challenger.observe_elements(&extra_data.block_bloom_after); } pub(crate) fn observe_public_values< @@ -111,6 +157,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_extra_block_data::(challenger, &public_values.extra_block_data); } pub(crate) fn observe_public_values_target< @@ -126,6 +173,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_extra_block_data_target::(challenger, &public_values.extra_block_data); } impl, C: GenericConfig, const D: usize> AllProof { diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index e56d635d63..6a8fa2d685 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -51,23 +51,27 @@ pub enum Segment { SelfDestructList = 25, /// Contains the bloom filter of a transaction. TxnBloom = 26, - /// Contains the bloom filter of a block. + /// Contains the computed bloom filter of a block. BlockBloom = 27, + /// Contains the final block bloom, and the block bloom filters before and after the current transaction. + /// The first eight elements are `block_metadata.block_bloom`. The next eight are `block_bloom_before`, + /// and the last eight are `block_bloom_after. + GlobalBlockBloom = 28, /// List of log pointers pointing to the LogsData segment. - Logs = 28, - LogsData = 29, + Logs = 29, + LogsData = 30, /// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`. - Journal = 30, - JournalData = 31, - JournalCheckpoints = 32, + Journal = 31, + JournalData = 32, + JournalCheckpoints = 33, /// List of addresses that have been touched in the current transaction. - TouchedAddresses = 33, + TouchedAddresses = 34, /// List of checkpoints for the current context. Length in `ContextMetadata`. - ContextCheckpoints = 34, + ContextCheckpoints = 35, } impl Segment { - pub(crate) const COUNT: usize = 35; + pub(crate) const COUNT: usize = 36; pub(crate) fn all() -> [Self; Self::COUNT] { [ @@ -99,6 +103,7 @@ impl Segment { Self::SelfDestructList, Self::TxnBloom, Self::BlockBloom, + Self::GlobalBlockBloom, Self::Logs, Self::LogsData, Self::Journal, @@ -140,6 +145,7 @@ impl Segment { Segment::SelfDestructList => "SEGMENT_SELFDESTRUCT_LIST", Segment::TxnBloom => "SEGMENT_TXN_BLOOM", Segment::BlockBloom => "SEGMENT_BLOCK_BLOOM", + Segment::GlobalBlockBloom => "SEGMENT_GLOBAL_BLOCK_BLOOM", Segment::Logs => "SEGMENT_LOGS", Segment::LogsData => "SEGMENT_LOGS_DATA", Segment::Journal => "SEGMENT_JOURNAL", @@ -180,6 +186,7 @@ impl Segment { Segment::AccessedStorageKeys => 256, Segment::SelfDestructList => 256, Segment::TxnBloom => 8, + Segment::GlobalBlockBloom => 256, Segment::BlockBloom => 8, Segment::Logs => 256, Segment::LogsData => 256, diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 2cda5489d7..7c2b362aac 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 extra_block_data: ExtraBlockData, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -71,9 +72,20 @@ pub struct BlockMetadata { pub block_gaslimit: U256, pub block_chain_id: U256, pub block_base_fee: U256, + pub block_gas_used: U256, pub block_bloom: [U256; 8], } +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct ExtraBlockData { + pub txn_number_before: U256, + pub txn_number_after: U256, + pub gas_used_before: U256, + pub gas_used_after: U256, + pub block_bloom_before: [U256; 8], + pub block_bloom_after: [U256; 8], +} + /// Memory values which are public. /// Note: All the larger integers are encoded with 32-bit limbs in little-endian order. #[derive(Eq, PartialEq, Debug)] @@ -81,6 +93,7 @@ pub struct PublicValuesTarget { pub trie_roots_before: TrieRootsTarget, pub trie_roots_after: TrieRootsTarget, pub block_metadata: BlockMetadataTarget, + pub extra_block_data: ExtraBlockDataTarget, } impl PublicValuesTarget { @@ -113,6 +126,7 @@ impl PublicValuesTarget { block_gaslimit, block_chain_id, block_base_fee, + block_gas_used, block_bloom, } = self.block_metadata; @@ -123,8 +137,24 @@ impl PublicValuesTarget { buffer.write_target(block_gaslimit)?; buffer.write_target(block_chain_id)?; buffer.write_target_array(&block_base_fee)?; + buffer.write_target(block_gas_used)?; buffer.write_target_array(&block_bloom)?; + let ExtraBlockDataTarget { + txn_number_before, + txn_number_after, + gas_used_before, + gas_used_after, + block_bloom_before, + block_bloom_after, + } = self.extra_block_data; + buffer.write_target(txn_number_before)?; + buffer.write_target(txn_number_after)?; + buffer.write_target(gas_used_before)?; + buffer.write_target(gas_used_after)?; + buffer.write_target_array(&block_bloom_before)?; + buffer.write_target_array(&block_bloom_after)?; + Ok(()) } @@ -149,18 +179,36 @@ impl PublicValuesTarget { block_gaslimit: buffer.read_target()?, block_chain_id: buffer.read_target()?, block_base_fee: buffer.read_target_array()?, + block_gas_used: buffer.read_target()?, block_bloom: buffer.read_target_array()?, }; + let extra_block_data = ExtraBlockDataTarget { + txn_number_before: buffer.read_target()?, + txn_number_after: buffer.read_target()?, + gas_used_before: buffer.read_target()?, + gas_used_after: buffer.read_target()?, + block_bloom_before: buffer.read_target_array()?, + block_bloom_after: buffer.read_target_array()?, + }; + Ok(Self { trie_roots_before, trie_roots_after, block_metadata, + extra_block_data, }) } pub fn from_public_inputs(pis: &[Target]) -> Self { - assert!(pis.len() > TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE - 1); + assert!( + pis.len() + > TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + ExtraBlockDataTarget::SIZE + - 1 + ); + Self { trie_roots_before: TrieRootsTarget::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]), trie_roots_after: TrieRootsTarget::from_public_inputs( @@ -170,6 +218,12 @@ impl PublicValuesTarget { &pis[TrieRootsTarget::SIZE * 2 ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE], ), + extra_block_data: ExtraBlockDataTarget::from_public_inputs( + &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + ..TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + ExtraBlockDataTarget::SIZE], + ), } } @@ -198,6 +252,12 @@ impl PublicValuesTarget { pv0.block_metadata, pv1.block_metadata, ), + extra_block_data: ExtraBlockDataTarget::select( + builder, + condition, + pv0.extra_block_data, + pv1.extra_block_data, + ), } } } @@ -210,7 +270,7 @@ pub struct TrieRootsTarget { } impl TrieRootsTarget { - const SIZE: usize = 24; + pub const SIZE: usize = 24; pub fn from_public_inputs(pis: &[Target]) -> Self { let state_root = pis[0..8].try_into().unwrap(); @@ -269,11 +329,12 @@ pub struct BlockMetadataTarget { pub block_gaslimit: Target, pub block_chain_id: Target, pub block_base_fee: [Target; 2], + pub block_gas_used: Target, pub block_bloom: [Target; 64], } impl BlockMetadataTarget { - const SIZE: usize = 76; + const SIZE: usize = 77; pub fn from_public_inputs(pis: &[Target]) -> Self { let block_beneficiary = pis[0..5].try_into().unwrap(); @@ -283,7 +344,8 @@ impl BlockMetadataTarget { let block_gaslimit = pis[8]; let block_chain_id = pis[9]; let block_base_fee = pis[10..12].try_into().unwrap(); - let block_bloom = pis[12..76].try_into().unwrap(); + let block_gas_used = pis[12]; + let block_bloom = pis[13..77].try_into().unwrap(); Self { block_beneficiary, @@ -293,6 +355,7 @@ impl BlockMetadataTarget { block_gaslimit, block_chain_id, block_base_fee, + block_gas_used, block_bloom, } } @@ -319,6 +382,7 @@ impl BlockMetadataTarget { block_base_fee: core::array::from_fn(|i| { builder.select(condition, bm0.block_base_fee[i], bm1.block_base_fee[i]) }), + block_gas_used: builder.select(condition, bm0.block_gas_used, bm1.block_gas_used), block_bloom: core::array::from_fn(|i| { builder.select(condition, bm0.block_bloom[i], bm1.block_bloom[i]) }), @@ -347,6 +411,87 @@ impl BlockMetadataTarget { } } +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub struct ExtraBlockDataTarget { + pub txn_number_before: Target, + pub txn_number_after: Target, + pub gas_used_before: Target, + pub gas_used_after: Target, + pub block_bloom_before: [Target; 64], + pub block_bloom_after: [Target; 64], +} + +impl ExtraBlockDataTarget { + const SIZE: usize = 132; + + pub fn from_public_inputs(pis: &[Target]) -> Self { + let txn_number_before = pis[0]; + let txn_number_after = pis[1]; + let gas_used_before = pis[2]; + let gas_used_after = pis[3]; + let block_bloom_before = pis[4..68].try_into().unwrap(); + let block_bloom_after = pis[68..132].try_into().unwrap(); + + Self { + txn_number_before, + txn_number_after, + gas_used_before, + gas_used_after, + block_bloom_before, + block_bloom_after, + } + } + + pub fn select, const D: usize>( + builder: &mut CircuitBuilder, + condition: BoolTarget, + ed0: Self, + ed1: Self, + ) -> Self { + Self { + txn_number_before: builder.select( + condition, + ed0.txn_number_before, + ed1.txn_number_before, + ), + txn_number_after: builder.select(condition, ed0.txn_number_after, ed1.txn_number_after), + gas_used_before: builder.select(condition, ed0.gas_used_before, ed1.gas_used_before), + gas_used_after: builder.select(condition, ed0.gas_used_after, ed1.gas_used_after), + block_bloom_before: core::array::from_fn(|i| { + builder.select( + condition, + ed0.block_bloom_before[i], + ed1.block_bloom_before[i], + ) + }), + block_bloom_after: core::array::from_fn(|i| { + builder.select( + condition, + ed0.block_bloom_after[i], + ed1.block_bloom_after[i], + ) + }), + } + } + + pub fn connect, const D: usize>( + builder: &mut CircuitBuilder, + ed0: Self, + ed1: Self, + ) { + builder.connect(ed0.txn_number_before, ed1.txn_number_before); + builder.connect(ed0.txn_number_after, ed1.txn_number_after); + builder.connect(ed0.gas_used_before, ed1.gas_used_before); + builder.connect(ed1.gas_used_after, ed1.gas_used_after); + for i in 0..64 { + builder.connect(ed0.block_bloom_before[i], ed1.block_bloom_before[i]); + } + for i in 0..64 { + builder.connect(ed0.block_bloom_after[i], ed1.block_bloom_after[i]); + } + } +} + #[derive(Debug, Clone)] pub struct StarkProof, C: GenericConfig, const D: usize> { /// Merkle cap of LDEs of trace values. diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 296cbfb2fb..0403763760 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -37,9 +37,9 @@ use crate::permutation::{ PermutationCheckDataTarget, }; use crate::proof::{ - BlockMetadata, BlockMetadataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget, - StarkProof, StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots, - TrieRootsTarget, + BlockMetadata, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, + PublicValuesTarget, StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget, + StarkProofTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, }; use crate::stark::Stark; use crate::util::u256_limbs; @@ -509,7 +509,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 = [ + let block_fields_without_beneficiary_and_basefee_and_bloom = [ ( GlobalMetadata::BlockTimestamp as usize, public_values.block_metadata.block_timestamp, @@ -530,28 +530,97 @@ pub(crate) fn get_memory_extra_looking_products_circuit< GlobalMetadata::BlockChainId as usize, public_values.block_metadata.block_chain_id, ), + ( + GlobalMetadata::BlockGasUsed as usize, + public_values.block_metadata.block_gas_used, + ), + ( + GlobalMetadata::BlockGasUsedBefore as usize, + public_values.extra_block_data.gas_used_before, + ), + ( + GlobalMetadata::BlockGasUsedAfter as usize, + public_values.extra_block_data.gas_used_after, + ), + ( + GlobalMetadata::TxnNumberBefore as usize, + public_values.extra_block_data.txn_number_before, + ), + ( + GlobalMetadata::TxnNumberAfter as usize, + public_values.extra_block_data.txn_number_after, + ), ]; - product = add_metadata_write( - builder, - challenge, - product, - GlobalMetadata::BlockBeneficiary as usize, - &public_values.block_metadata.block_beneficiary, - ); + let beneficiary_base_fee_fields: [(usize, &[Target]); 2] = [ + ( + GlobalMetadata::BlockBeneficiary as usize, + &public_values.block_metadata.block_beneficiary, + ), + ( + GlobalMetadata::BlockBaseFee as usize, + &public_values.block_metadata.block_base_fee, + ), + ]; - block_fields_without_beneficiary_and_basefee.map(|(field, target)| { + let metadata_segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32)); + block_fields_without_beneficiary_and_basefee_and_bloom.map(|(field, target)| { // Each of those fields fit in 32 bits, hence in a single Target. - product = add_metadata_write(builder, challenge, product, field, &[target]); + product = add_data_write( + builder, + challenge, + product, + metadata_segment, + field, + &[target], + ); }); - product = add_metadata_write( - builder, - challenge, - product, - GlobalMetadata::BlockBaseFee as usize, - &public_values.block_metadata.block_base_fee, - ); + beneficiary_base_fee_fields.map(|(field, targets)| { + product = add_data_write( + builder, + challenge, + product, + metadata_segment, + field, + targets, + ); + }); + + // Add block bloom filters writes. + let bloom_segment = builder.constant(F::from_canonical_u32(Segment::GlobalBlockBloom as u32)); + for i in 0..8 { + product = add_data_write( + builder, + challenge, + product, + bloom_segment, + 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 + 16, + &public_values.extra_block_data.block_bloom_after[i * 8..(i + 1) * 8], + ); + } // Add trie roots writes. let trie_fields = [ @@ -582,25 +651,32 @@ pub(crate) fn get_memory_extra_looking_products_circuit< ]; trie_fields.map(|(field, targets)| { - product = add_metadata_write(builder, challenge, product, field, &targets); + product = add_data_write( + builder, + challenge, + product, + metadata_segment, + field, + &targets, + ); }); product } -fn add_metadata_write, const D: usize>( +fn add_data_write, const D: usize>( builder: &mut CircuitBuilder, challenge: GrandProductChallenge, running_product: Target, - metadata_idx: usize, - metadata: &[Target], + segment: Target, + idx: usize, + val: &[Target], ) -> Target { - debug_assert!(metadata.len() <= VALUE_LIMBS); - let len = core::cmp::min(metadata.len(), VALUE_LIMBS); + debug_assert!(val.len() <= VALUE_LIMBS); + let len = core::cmp::min(val.len(), VALUE_LIMBS); let zero = builder.zero(); let one = builder.one(); - let segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32)); let row = builder.add_virtual_targets(13); // is_read @@ -610,12 +686,12 @@ fn add_metadata_write, const D: usize>( // segment builder.connect(row[2], segment); // virtual - let field_target = builder.constant(F::from_canonical_usize(metadata_idx)); + let field_target = builder.constant(F::from_canonical_usize(idx)); builder.connect(row[3], field_target); // values for j in 0..len { - builder.connect(row[4 + j], metadata[j]); + builder.connect(row[4 + j], val[j]); } for j in len..VALUE_LIMBS { builder.connect(row[4 + j], zero); @@ -653,10 +729,12 @@ 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 extra_block_data = add_virtual_extra_block_data(builder); PublicValuesTarget { trie_roots_before, trie_roots_after, block_metadata, + extra_block_data, } } @@ -683,6 +761,7 @@ pub(crate) fn add_virtual_block_metadata, const D: let block_gaslimit = builder.add_virtual_public_input(); let block_chain_id = builder.add_virtual_public_input(); let block_base_fee = builder.add_virtual_public_input_arr(); + let block_gas_used = builder.add_virtual_public_input(); let block_bloom = builder.add_virtual_public_input_arr(); BlockMetadataTarget { block_beneficiary, @@ -692,9 +771,28 @@ pub(crate) fn add_virtual_block_metadata, const D: block_gaslimit, block_chain_id, block_base_fee, + block_gas_used, block_bloom, } } +pub(crate) fn add_virtual_extra_block_data, const D: usize>( + builder: &mut CircuitBuilder, +) -> ExtraBlockDataTarget { + let txn_number_before = builder.add_virtual_public_input(); + let txn_number_after = builder.add_virtual_public_input(); + let gas_used_before = builder.add_virtual_public_input(); + let gas_used_after = builder.add_virtual_public_input(); + let block_bloom_before: [Target; 64] = builder.add_virtual_public_input_arr(); + let block_bloom_after: [Target; 64] = builder.add_virtual_public_input_arr(); + ExtraBlockDataTarget { + txn_number_before, + txn_number_after, + gas_used_before, + gas_used_after, + block_bloom_before, + block_bloom_after, + } +} pub(crate) fn add_virtual_stark_proof< F: RichField + Extendable, @@ -796,6 +894,11 @@ pub(crate) fn set_public_value_targets( &public_values_target.block_metadata, &public_values.block_metadata, ); + set_extra_public_values_target( + witness, + &public_values_target.extra_block_data, + &public_values.extra_block_data, + ); } pub(crate) fn set_trie_roots_target( @@ -894,9 +997,55 @@ pub(crate) fn set_block_metadata_target( block_metadata_target.block_base_fee[1], F::from_canonical_u32((block_metadata.block_base_fee.as_u64() >> 32) as u32), ); + witness.set_target( + block_metadata_target.block_gas_used, + F::from_canonical_u64(block_metadata.block_gas_used.as_u64()), + ); let mut block_bloom_limbs = [F::ZERO; 64]; for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() { limbs.copy_from_slice(&u256_limbs(block_metadata.block_bloom[i])); } witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs); } + +pub(crate) fn set_extra_public_values_target( + witness: &mut W, + ed_target: &ExtraBlockDataTarget, + ed: &ExtraBlockData, +) where + F: RichField + Extendable, + W: Witness, +{ + witness.set_target( + ed_target.txn_number_before, + F::from_canonical_usize(ed.txn_number_before.as_usize()), + ); + witness.set_target( + ed_target.txn_number_after, + F::from_canonical_usize(ed.txn_number_after.as_usize()), + ); + witness.set_target( + ed_target.gas_used_before, + F::from_canonical_usize(ed.gas_used_before.as_usize()), + ); + witness.set_target( + ed_target.gas_used_after, + F::from_canonical_usize(ed.gas_used_after.as_usize()), + ); + + let block_bloom_before = ed.block_bloom_before; + let mut block_bloom_limbs = [F::ZERO; 64]; + for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() { + limbs.copy_from_slice(&u256_limbs(block_bloom_before[i])); + } + + witness.set_target_arr(&ed_target.block_bloom_before, &block_bloom_limbs); + + let block_bloom_after = ed.block_bloom_after; + let mut block_bloom_limbs = [F::ZERO; 64]; + for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() { + limbs.copy_from_slice(&u256_limbs(block_bloom_after[i])); + } + + witness.set_target_arr(&ed_target.block_bloom_after, &block_bloom_limbs); +} diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 715b1738ae..218f41ad07 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -176,6 +176,26 @@ where GlobalMetadata::BlockBaseFee, public_values.block_metadata.block_base_fee, ), + ( + GlobalMetadata::BlockGasUsed, + public_values.block_metadata.block_gas_used, + ), + ( + GlobalMetadata::TxnNumberBefore, + public_values.extra_block_data.txn_number_before, + ), + ( + GlobalMetadata::TxnNumberAfter, + public_values.extra_block_data.txn_number_after, + ), + ( + GlobalMetadata::BlockGasUsedBefore, + public_values.extra_block_data.gas_used_before, + ), + ( + GlobalMetadata::BlockGasUsedAfter, + public_values.extra_block_data.gas_used_after, + ), ( GlobalMetadata::StateTrieRootDigestBefore, h2u(public_values.trie_roots_before.state_root), @@ -201,28 +221,52 @@ where h2u(public_values.trie_roots_after.receipts_root), ), ]; - let is_read = F::ZERO; - let context = F::ZERO; + let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); - let timestamp = F::ONE; - let mut row = vec![F::ZERO; 13]; - fields.map(|(field, val)| { - row[0] = is_read; - row[1] = context; - row[2] = segment; - row[3] = F::from_canonical_usize(field as usize); - - for j in 0..VALUE_LIMBS { - row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); - } - row[12] = timestamp; - prod *= challenge.combine(row.iter()); - }); + fields.map(|(field, val)| prod = add_data_write(challenge, segment, prod, field as usize, val)); + + // Add block bloom writes. + let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32); + for index in 0..8 { + let val = public_values.block_metadata.block_bloom[index]; + prod = add_data_write(challenge, bloom_segment, prod, index, val); + } + + for index in 0..8 { + let val = public_values.extra_block_data.block_bloom_before[index]; + prod = add_data_write(challenge, bloom_segment, prod, index + 8, val); + } + for index in 0..8 { + let val = public_values.extra_block_data.block_bloom_after[index]; + prod = add_data_write(challenge, bloom_segment, prod, index + 16, val); + } prod } +fn add_data_write( + challenge: GrandProductChallenge, + segment: F, + running_product: F, + index: usize, + val: U256, +) -> F +where + F: RichField + Extendable, +{ + let mut row = vec![F::ZERO; 13]; + row[0] = F::ZERO; // is_read + row[1] = F::ZERO; // context + row[2] = segment; + row[3] = F::from_canonical_usize(index); + + for j in 0..VALUE_LIMBS { + row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); + } + 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 e0b6f10446..91ddb14e1b 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -86,6 +86,7 @@ fn add11_yml() -> anyhow::Result<()> { block_gaslimit: 0xff112233u32.into(), block_chain_id: 1.into(), block_base_fee: 0xa.into(), + block_gas_used: 0xa868u64.into(), block_bloom: [0.into(); 8], }; @@ -148,6 +149,11 @@ fn add11_yml() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 0xa868u64.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], addresses: vec![], }; diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index 89aacd2c0a..f6d27ac39c 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -166,6 +166,11 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], addresses: vec![], }; diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 896e5112cb..7ad04bdc83 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -57,6 +57,11 @@ fn test_empty_txn_list() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 0.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], addresses: vec![], }; diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index 8219e4edbf..b6f7ee8494 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; +use ethereum_types::{Address, U256}; use hex_literal::hex; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -16,9 +16,10 @@ use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; use plonky2_evm::all_stark::AllStark; 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, TrieRoots}; +use plonky2_evm::proof::{BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -137,6 +138,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { block_gaslimit: 0xffffffffu32.into(), block_chain_id: 1.into(), block_base_fee: 0xa.into(), + block_gas_used: 0.into(), block_bloom: [0.into(); 8], }; @@ -204,12 +206,32 @@ fn test_log_opcodes() -> anyhow::Result<()> { transactions_root: HashedPartialTrie::from(Node::Empty).hash(), receipts_root: receipts_trie.hash(), }; + let block_bloom_after = [ + U256::from_dec_str("392318858461667547739736838950479151006397215279002157056").unwrap(), + 0.into(), + U256::from_dec_str( + "55213970774324510299478046898216203619608871777363092441300193790394368", + ) + .unwrap(), + U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(), + U256::from_dec_str("33554432").unwrap(), + U256::from_dec_str("98079714615416886934934209737619787760822675856605315072").unwrap(), + U256::from_dec_str("262144").unwrap(), + U256::from_dec_str("6739986666787659948666753771754908317446393422488596686587943714816") + .unwrap(), + ]; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], tries: tries_before, trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after, + addresses: vec![], }; @@ -231,6 +253,370 @@ fn test_log_opcodes() -> anyhow::Result<()> { verify_proof(&all_stark, proof, &config) } +// Tests proving two transactions, one of which with logs, and aggregating them. +#[test] +#[ignore] // Too slow to run on CI. +fn test_log_with_aggreg() -> anyhow::Result<()> { + init_logger(); + + let code = [ + 0x64, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0x60, 0x0, 0x52, // MSTORE(0x0, 0xA1B2C3D4E5) + 0x60, 0x0, 0x60, 0x0, 0xA0, // LOG0(0x0, 0x0) + 0x60, 99, 0x60, 98, 0x60, 5, 0x60, 27, 0xA2, // LOG2(27, 5, 98, 99) + 0x00, + ]; + + let code_gas = 3 + 3 + 3 // PUSHs and MSTORE + + 3 + 3 + 375 // PUSHs and LOG0 + + 3 + 3 + 3 + 3 + 375 + 375*2 + 8*5 // PUSHs and LOG2 + + 3 // Memory expansion + ; + + let gas_used = 21_000 + code_gas; + + let code_hash = keccak(code); + + // First transaction. + let all_stark_first = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); + let sender_first = hex!("af1276cbb260bb13deddb4209ae99ae6e497f446"); + let to_first = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); + let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552e89"); + + let beneficiary_state_key = keccak(beneficiary); + let sender_state_key = keccak(sender_first); + let to_hashed = keccak(to_first); + let to_hashed_2 = keccak(to); + + let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); + let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); + let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); + let to_second_nibbles = Nibbles::from_bytes_be(to_hashed_2.as_bytes()).unwrap(); + + let beneficiary_account_before = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + let sender_balance_before = 1000000000000000000u64.into(); + let sender_account_before = AccountRlp { + balance: sender_balance_before, + ..AccountRlp::default() + }; + let to_account_before = AccountRlp { + ..AccountRlp::default() + }; + let to_account_second_before = AccountRlp { + code_hash, + ..AccountRlp::default() + }; + + // In the first transaction, the sender account sends `txn_value` to `to_account`. + let gas_price = 10; + let txn_value = 0xau64; + let mut state_trie_before = HashedPartialTrie::from(Node::Empty); + state_trie_before.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_before).to_vec(), + ); + state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); + state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); + state_trie_before.insert( + to_second_nibbles, + rlp::encode(&to_account_second_before).to_vec(), + ); + + let tries_before = TrieInputs { + state_trie: state_trie_before, + transactions_trie: Node::Empty.into(), + receipts_trie: Node::Empty.into(), + storage_tries: vec![], + }; + + let txn = hex!("f85f800a82520894095e7baea6a6c7c4c2dfeb977efac326af552d870a8026a0122f370ed4023a6c253350c6bfb87d7d7eb2cd86447befee99e0a26b70baec20a07100ab1b3977f2b4571202b9f4b68850858caf5469222794600b5ce1cfb348ad"); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_gaslimit: 0x445566u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: (22570 + 21000).into(), + block_bloom: [ + 0.into(), + 0.into(), + U256::from_dec_str( + "55213970774324510299479508399853534522527075462195808724319849722937344", + ) + .unwrap(), + U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(), + 33554432.into(), + U256::from_dec_str("9223372036854775808").unwrap(), + U256::from_dec_str( + "3618502788666131106986593281521497120414687020801267626233049500247285563392", + ) + .unwrap(), + U256::from_dec_str("2722259584404615024560450425766186844160").unwrap(), + ], + }; + + let beneficiary_account_after = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + + let sender_balance_after = sender_balance_before - gas_price * 21000 - txn_value; + let sender_account_after = AccountRlp { + balance: sender_balance_after, + nonce: 1.into(), + ..AccountRlp::default() + }; + let to_account_after = AccountRlp { + balance: txn_value.into(), + ..AccountRlp::default() + }; + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(code_hash, code.to_vec()); + + let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); + expected_state_trie_after.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_after).to_vec(), + ); + expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); + expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); + expected_state_trie_after.insert( + to_second_nibbles, + rlp::encode(&to_account_second_before).to_vec(), + ); + + // Compute new receipt trie. + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: 21000u64.into(), + bloom: [0x00; 256].to_vec().into(), + logs: vec![], + }; + receipts_trie.insert( + Nibbles::from_str("0x80").unwrap(), + rlp::encode(&receipt_0).to_vec(), + ); + + let tries_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: HashedPartialTrie::from(Node::Empty).hash(), + receipts_root: receipts_trie.clone().hash(), + }; + + let inputs_first = GenerationInputs { + signed_txns: vec![txn.to_vec()], + tries: tries_before, + trie_roots_after: tries_after, + contract_code, + block_metadata: block_metadata.clone(), + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 21000u64.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], + addresses: vec![], + }; + + let mut timing = TimingTree::new("prove first", log::Level::Debug); + let proof = prove::(&all_stark_first, &config, inputs_first.clone(), &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + // The output bloom filter, gas used and transaction number are fed to the next transaction, so the two proofs can be correctly aggregated. + let block_bloom_second = proof.public_values.extra_block_data.block_bloom_after; + let gas_used_second = proof.public_values.extra_block_data.gas_used_after; + + verify_proof(&all_stark_first, proof, &config)?; + + // Create the aggregation circuits. + let all_circuits_first = AllRecursiveCircuits::::new( + &all_stark_first, + &[9..17, 9..19, 9..15, 9..11, 9..14, 9..21], // Minimal ranges to prove an empty list + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (root_proof_first, first_public_values) = + all_circuits_first.prove_root(&all_stark_first, &config, inputs_first, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + // Prove second transaction. In this second transaction, the code with logs is executed. + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let state_trie_before = expected_state_trie_after; + + let tries_before = TrieInputs { + state_trie: state_trie_before, + transactions_trie: Node::Empty.into(), + receipts_trie: receipts_trie.clone(), + storage_tries: vec![], + }; + + // Prove a transaction which carries out two LOG opcodes. + let txn_gas_price = 10; + let txn_2 = hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b"); + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(code_hash, code.to_vec()); + + // Update the state and receipt tries after the transaction, so that we have the correct expected tries: + // Update accounts. + let beneficiary_account_after = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + + let sender_balance_after = sender_balance_after - gas_used * txn_gas_price; + let sender_account_after = AccountRlp { + balance: sender_balance_after, + nonce: 2.into(), + ..AccountRlp::default() + }; + let balance_after = to_account_after.balance; + let to_account_after = AccountRlp { + balance: balance_after, + ..AccountRlp::default() + }; + let to_account_second_after = AccountRlp { + balance: to_account_second_before.balance, + code_hash, + ..AccountRlp::default() + }; + + // Update the receipt trie. + let first_log = LogRlp { + address: to.into(), + topics: vec![], + data: Bytes::new(), + }; + + let second_log = LogRlp { + address: to.into(), + topics: vec![ + hex!("0000000000000000000000000000000000000000000000000000000000000062").into(), // dec: 98 + hex!("0000000000000000000000000000000000000000000000000000000000000063").into(), // dec: 99 + ], + data: hex!("a1b2c3d4e5").to_vec().into(), + }; + + let receipt = LegacyReceiptRlp { + status: true, + cum_gas_used: (22570 + 21000).into(), + bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000001000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000800000000000000008000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000800002000000000000000000000000000").to_vec().into(), + logs: vec![first_log, second_log], + }; + + let receipt_nibbles = Nibbles::from_str("0x01").unwrap(); // RLP(1) = 0x1 + + receipts_trie.insert(receipt_nibbles, rlp::encode(&receipt).to_vec()); + + // Update the state trie. + let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); + expected_state_trie_after.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_after).to_vec(), + ); + expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); + expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); + expected_state_trie_after.insert( + to_second_nibbles, + rlp::encode(&to_account_second_after).to_vec(), + ); + + let trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: HashedPartialTrie::from(Node::Empty).hash(), + receipts_root: receipts_trie.hash(), + }; + + let block_bloom_final = [ + 0.into(), + 0.into(), + U256::from_dec_str( + "55213970774324510299479508399853534522527075462195808724319849722937344", + ) + .unwrap(), + U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(), + U256::from_dec_str("33554432").unwrap(), + U256::from_dec_str("9223372036854775808").unwrap(), + U256::from_dec_str( + "3618502788666131106986593281521497120414687020801267626233049500247285563392", + ) + .unwrap(), + U256::from_dec_str("2722259584404615024560450425766186844160").unwrap(), + ]; + let inputs = GenerationInputs { + signed_txns: vec![txn_2.to_vec()], + tries: tries_before, + trie_roots_after, + contract_code, + block_metadata, + txn_number_before: 1.into(), + gas_used_before: gas_used_second, + gas_used_after: receipt.cum_gas_used, + block_bloom_before: block_bloom_second, + block_bloom_after: block_bloom_final, + addresses: vec![], + }; + let mut timing = TimingTree::new("prove second", log::Level::Debug); + let proof = prove::(&all_stark, &config, inputs.clone(), &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + verify_proof(&all_stark, proof, &config)?; + let config = StarkConfig::standard_fast_config(); + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[9..17, 9..19, 9..15, 9..11, 9..14, 9..21], + &config, + ); + let mut timing = TimingTree::new("prove root second", log::Level::Info); + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_root(root_proof.clone())?; + + // Update public values for the aggregation. + let agg_public_values = PublicValues { + trie_roots_before: first_public_values.trie_roots_before, + trie_roots_after: public_values.trie_roots_after, + extra_block_data: ExtraBlockData { + txn_number_before: first_public_values.extra_block_data.txn_number_before, + txn_number_after: public_values.extra_block_data.txn_number_after, + gas_used_before: first_public_values.extra_block_data.gas_used_before, + gas_used_after: public_values.extra_block_data.gas_used_after, + block_bloom_before: first_public_values.extra_block_data.block_bloom_before, + block_bloom_after: public_values.extra_block_data.block_bloom_after, + }, + block_metadata: public_values.block_metadata, + }; + + // We can duplicate the proofs here because the state hasn't mutated. + let (agg_proof, updated_agg_public_values) = all_circuits_first.prove_aggregation( + false, + &root_proof_first, + false, + &root_proof, + agg_public_values, + )?; + all_circuits_first.verify_aggregation(&agg_proof)?; + let (block_proof, _block_public_values) = + all_circuits.prove_block(None, &agg_proof, updated_agg_public_values)?; + all_circuits.verify_block(&block_proof) +} + /// Values taken from the block 1000000 of Goerli: https://goerli.etherscan.io/txs?block=1000000 #[test] fn test_txn_and_receipt_trie_hash() -> anyhow::Result<()> { @@ -411,6 +797,7 @@ fn test_two_txn() -> anyhow::Result<()> { block_gaslimit: 0xffffffffu32.into(), block_chain_id: 1.into(), block_base_fee: 0xa.into(), + block_gas_used: 0.into(), block_bloom: [0.into(); 8], }; @@ -481,6 +868,11 @@ fn test_two_txn() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 42000u64.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], addresses: vec![], }; diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index 19a7fcf287..daeccfc6d9 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -155,6 +155,11 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], addresses: vec![], }; diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index 5bf95f58dc..f3e2e839d9 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -74,6 +74,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { block_gaslimit: 0xff112233u32.into(), block_chain_id: 1.into(), block_base_fee: 0xa.into(), + block_gas_used: 21032.into(), block_bloom: [0.into(); 8], }; @@ -135,6 +136,11 @@ fn test_simple_transfer() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 21032.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], addresses: vec![], }; From 6bd17e29c1cf6c747a0ac5e33505a608a1dd7a64 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Tue, 5 Sep 2023 14:20:45 +0100 Subject: [PATCH 2/3] Apply comments --- evm/src/fixed_recursive_verifier.rs | 57 +++++++++++++++-------------- evm/src/util.rs | 4 +- evm/tests/log_opcode.rs | 2 +- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index d9b0c12119..41c1d21708 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -3,7 +3,6 @@ use std::collections::BTreeMap; use std::ops::Range; use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; -use ethereum_types::BigEndianHash; use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; use plonky2::field::extension::Extendable; @@ -52,7 +51,7 @@ use crate::recursive_verifier::{ PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::stark::Stark; -use crate::util::u256_limbs; +use crate::util::h256_limbs; /// The recursion threshold. We end a chain of recursive proofs once we reach this size. const THRESHOLD_DEGREE_BITS: usize = 13; @@ -699,7 +698,7 @@ where let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs); // Make connections between block proofs, and check initial and final block values. - Self::connect_block_proof(&mut builder, &parent_pv, &agg_pv); + Self::connect_block_proof(&mut builder, has_parent_block, &parent_pv, &agg_pv); let cyclic_vk = builder.add_verifier_data_public_inputs(); builder @@ -726,6 +725,7 @@ where fn connect_block_proof( builder: &mut CircuitBuilder, + has_parent_block: BoolTarget, lhs: &PublicValuesTarget, rhs: &PublicValuesTarget, ) { @@ -747,8 +747,24 @@ where // Check initial block values. Self::connect_initial_values_block(builder, rhs); - // Connect intermediary values for gas_used and bloom filters to the block's final values. We only plug on the right, so there is no need to check the left-handsidee block. + // Connect intermediary values for gas_used and bloom filters to the block's final values. We only plug on the right, so there is no need to check the left-handside block. Self::connect_final_block_values_to_intermediary(builder, rhs); + + // Chack that the genesis block number is 0. + let zero = builder.zero(); + let has_not_parent_block = builder.sub(one, has_parent_block.target); + let gen_block_constr = builder.mul(has_not_parent_block, rhs.block_metadata.block_number); + builder.connect(gen_block_constr, zero); + + // Check that the genesis block has empty state trie. + let initial_trie = HashedPartialTrie::from(Node::Empty).hash(); + + for (i, limb) in h256_limbs::(initial_trie).into_iter().enumerate() { + let limb_target = builder.constant(limb); + let mut temp = builder.sub(rhs.trie_roots_before.state_root[i], limb_target); + temp = builder.mul(has_not_parent_block, temp); + builder.connect(temp, zero); + } } fn connect_final_block_values_to_intermediary( @@ -783,26 +799,17 @@ where builder.connect(x.extra_block_data.gas_used_before, zero); // The initial bloom filter is all zeroes. - let initial_bloom = builder.constants(&[F::ZERO; 64]); - for i in 0..x.extra_block_data.block_bloom_before.len() { - builder.connect(x.extra_block_data.block_bloom_before[i], initial_bloom[i]); + for t in x.extra_block_data.block_bloom_before { + builder.connect(t, zero); } // The transactions and receipts tries are empty at the beginning of the block. let initial_trie = HashedPartialTrie::from(Node::Empty).hash(); - for (i, limb) in initial_trie.into_uint().0.into_iter().enumerate() { - let temp = builder.constant(F::from_canonical_u32(limb as u32)); - builder.connect(x.trie_roots_before.transactions_root[2 * i], temp); - let temp2 = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); - builder.connect(x.trie_roots_before.transactions_root[2 * i + 1], temp2); - } - - for (i, limb) in initial_trie.into_uint().0.into_iter().enumerate() { - let temp = builder.constant(F::from_canonical_u32(limb as u32)); - builder.connect(x.trie_roots_before.receipts_root[2 * i], temp); - let temp2 = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); - builder.connect(x.trie_roots_before.receipts_root[2 * i + 1], temp2); + for (i, limb) in h256_limbs::(initial_trie).into_iter().enumerate() { + let limb_target = builder.constant(limb); + builder.connect(x.trie_roots_before.transactions_root[i], limb_target); + builder.connect(x.trie_roots_before.receipts_root[i], limb_target); } } @@ -944,16 +951,12 @@ where let state_trie_root_keys = 24..32; let block_number_key = TrieRootsTarget::SIZE * 2 + 6; let mut nonzero_pis = HashMap::new(); - for (key, &value) in state_trie_root_keys.zip_eq(&u256_limbs::( - public_values.trie_roots_before.state_root.into_uint(), - )) { + for (key, &value) in state_trie_root_keys + .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) + { nonzero_pis.insert(key, value); } - nonzero_pis.insert( - block_number_key, - F::from_canonical_usize(public_values.block_metadata.block_number.as_usize()) - - F::ONE, - ); + nonzero_pis.insert(block_number_key, F::NEG_ONE); block_inputs.set_proof_with_pis_target( &self.block.parent_block_proof, &cyclic_base_proof( diff --git a/evm/src/util.rs b/evm/src/util.rs index a1bb7a1fc6..5fa085dcf2 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -64,7 +64,9 @@ pub(crate) fn u256_limbs(u256: U256) -> [F; 8] { #[allow(unused)] /// Returns the 32-bit little-endian limbs of a `H256`. pub(crate) fn h256_limbs(h256: H256) -> [F; 8] { - h256.0 + let mut temp_h256 = h256.0; + temp_h256.reverse(); + temp_h256 .chunks(4) .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) .map(F::from_canonical_u32) diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index b6f7ee8494..1a5f179db2 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -339,7 +339,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { let block_metadata = BlockMetadata { block_beneficiary: Address::from(beneficiary), block_timestamp: 0x03e8.into(), - block_number: 1.into(), + block_number: 0.into(), block_difficulty: 0x020000.into(), block_gaslimit: 0x445566u32.into(), block_chain_id: 1.into(), From d4b71c56864809b5039687359ecf31c29ec1c1f2 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Wed, 6 Sep 2023 15:12:00 +0100 Subject: [PATCH 3/3] Replace genesis state trie check with TODO --- evm/src/fixed_recursive_verifier.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 41c1d21708..59b6026379 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -756,15 +756,7 @@ where let gen_block_constr = builder.mul(has_not_parent_block, rhs.block_metadata.block_number); builder.connect(gen_block_constr, zero); - // Check that the genesis block has empty state trie. - let initial_trie = HashedPartialTrie::from(Node::Empty).hash(); - - for (i, limb) in h256_limbs::(initial_trie).into_iter().enumerate() { - let limb_target = builder.constant(limb); - let mut temp = builder.sub(rhs.trie_roots_before.state_root[i], limb_target); - temp = builder.mul(has_not_parent_block, temp); - builder.connect(temp, zero); - } + // TODO: Check that the genesis block has a predetermined state trie root. } fn connect_final_block_values_to_intermediary(