diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 7fefe95f..d9c49447 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -641,6 +641,14 @@ where lhs: &ExtraBlockDataTarget, rhs: &ExtraBlockDataTarget, ) { + // Connect genesis state root values. + for (&limb0, &limb1) in pvs.genesis_state_root.iter().zip(&rhs.genesis_state_root) { + builder.connect(limb0, limb1); + } + for (&limb0, &limb1) in pvs.genesis_state_root.iter().zip(&lhs.genesis_state_root) { + builder.connect(limb0, limb1); + } + // 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); @@ -777,6 +785,16 @@ where builder.connect(limb0, limb1); } + // Between blocks, the genesis state trie remains unchanged. + for (&limb0, limb1) in lhs + .extra_block_data + .genesis_state_root + .iter() + .zip(rhs.extra_block_data.genesis_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); @@ -788,13 +806,35 @@ where // 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); + + // Check that the genesis block number is 0. let gen_block_constr = builder.mul(has_not_parent_block, rhs.block_metadata.block_number); builder.connect(gen_block_constr, zero); - // TODO: Check that the genesis block has a predetermined state trie root. + // Check that the genesis block has the predetermined state trie root in `ExtraBlockData`. + Self::connect_genesis_block(builder, rhs, has_not_parent_block); + } + + fn connect_genesis_block( + builder: &mut CircuitBuilder, + x: &PublicValuesTarget, + has_not_parent_block: Target, + ) where + F: RichField + Extendable, + { + let zero = builder.zero(); + for (&limb0, limb1) in x + .trie_roots_before + .state_root + .iter() + .zip(x.extra_block_data.genesis_state_root) + { + let mut constr = builder.sub(limb0, limb1); + constr = builder.mul(has_not_parent_block, constr); + builder.connect(constr, zero); + } } fn connect_final_block_values_to_intermediary( @@ -968,16 +1008,34 @@ 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; + // Initialize genesis_state_trie, state_root_after and the block number for correct connection between blocks. + // Initialize `state_root_after`. + let state_trie_root_after_keys = 24..32; let mut nonzero_pis = HashMap::new(); - for (key, &value) in state_trie_root_keys + for (key, &value) in state_trie_root_after_keys .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) { nonzero_pis.insert(key, value); } + + // Initialize the genesis state trie digest. + let genesis_state_trie_keys = TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + BlockHashesTarget::BLOCK_HASHES_SIZE + ..TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + BlockHashesTarget::BLOCK_HASHES_SIZE + + 8; + for (key, &value) in genesis_state_trie_keys.zip_eq(&h256_limbs::( + public_values.extra_block_data.genesis_state_root, + )) { + nonzero_pis.insert(key, value); + } + + // Initialize the block number. + let block_number_key = TrieRootsTarget::SIZE * 2 + 6; 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/generation/mod.rs b/evm/src/generation/mod.rs index 6b9ce000..628a0600 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -50,6 +50,8 @@ pub struct GenerationInputs { pub tries: TrieInputs, /// Expected trie roots after the transactions are executed. pub trie_roots_after: TrieRoots, + /// State trie root of the genesis block. + pub genesis_state_trie_root: H256, /// Mapping between smart contract code hashes and the contract byte code. /// All account smart contracts that are invoked will have an entry present. @@ -259,6 +261,7 @@ pub fn generate_traces, const D: usize>( let txn_number_after = read_metadata(GlobalMetadata::TxnNumberAfter); let extra_block_data = ExtraBlockData { + genesis_state_root: inputs.genesis_state_trie_root, txn_number_before: inputs.txn_number_before, txn_number_after, gas_used_before: inputs.gas_used_before, diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index cf17231a..663a35e2 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -108,6 +108,7 @@ fn observe_extra_block_data< challenger: &mut Challenger, extra_data: &ExtraBlockData, ) -> Result<(), ProgramError> { + challenger.observe_elements(&h256_limbs(extra_data.genesis_state_root)); challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?); challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?); challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?); @@ -132,6 +133,7 @@ fn observe_extra_block_data_target< ) where C::Hasher: AlgebraicHasher, { + challenger.observe_elements(&extra_data.genesis_state_root); challenger.observe_element(extra_data.txn_number_before); challenger.observe_element(extra_data.txn_number_after); challenger.observe_element(extra_data.gas_used_before); diff --git a/evm/src/proof.rs b/evm/src/proof.rs index d14485c5..1a39ac0f 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -119,6 +119,8 @@ pub struct BlockMetadata { /// unlike `BlockMetadata`. #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct ExtraBlockData { + /// The state trie digest of the genesis block. + pub genesis_state_root: H256, /// The transaction count prior execution of the local state transition, starting /// at 0 for the initial transaction of a block. pub txn_number_before: U256, @@ -203,6 +205,7 @@ impl PublicValuesTarget { buffer.write_target_array(&cur_hash)?; let ExtraBlockDataTarget { + genesis_state_root, txn_number_before, txn_number_after, gas_used_before, @@ -210,6 +213,7 @@ impl PublicValuesTarget { block_bloom_before, block_bloom_after, } = self.extra_block_data; + buffer.write_target_array(&genesis_state_root)?; buffer.write_target(txn_number_before)?; buffer.write_target(txn_number_after)?; buffer.write_target(gas_used_before)?; @@ -252,6 +256,7 @@ impl PublicValuesTarget { }; let extra_block_data = ExtraBlockDataTarget { + genesis_state_root: buffer.read_target_array()?, txn_number_before: buffer.read_target()?, txn_number_after: buffer.read_target()?, gas_used_before: buffer.read_target()?, @@ -420,7 +425,7 @@ pub struct BlockMetadataTarget { } impl BlockMetadataTarget { - const SIZE: usize = 85; + pub const SIZE: usize = 85; pub fn from_public_inputs(pis: &[Target]) -> Self { let block_beneficiary = pis[0..5].try_into().unwrap(); @@ -513,7 +518,7 @@ pub struct BlockHashesTarget { } impl BlockHashesTarget { - const BLOCK_HASHES_SIZE: usize = 2056; + pub const BLOCK_HASHES_SIZE: usize = 2056; pub fn from_public_inputs(pis: &[Target]) -> Self { Self { prev_hashes: pis[0..2048].try_into().unwrap(), @@ -553,6 +558,7 @@ impl BlockHashesTarget { #[derive(Eq, PartialEq, Debug, Copy, Clone)] pub struct ExtraBlockDataTarget { + pub genesis_state_root: [Target; 8], pub txn_number_before: Target, pub txn_number_after: Target, pub gas_used_before: Target, @@ -562,17 +568,19 @@ pub struct ExtraBlockDataTarget { } impl ExtraBlockDataTarget { - const SIZE: usize = 132; + const SIZE: usize = 140; 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(); + let genesis_state_root = pis[0..8].try_into().unwrap(); + let txn_number_before = pis[8]; + let txn_number_after = pis[9]; + let gas_used_before = pis[10]; + let gas_used_after = pis[11]; + let block_bloom_before = pis[12..76].try_into().unwrap(); + let block_bloom_after = pis[76..140].try_into().unwrap(); Self { + genesis_state_root, txn_number_before, txn_number_after, gas_used_before, @@ -589,6 +597,13 @@ impl ExtraBlockDataTarget { ed1: Self, ) -> Self { Self { + genesis_state_root: core::array::from_fn(|i| { + builder.select( + condition, + ed0.genesis_state_root[i], + ed1.genesis_state_root[i], + ) + }), txn_number_before: builder.select( condition, ed0.txn_number_before, @@ -619,6 +634,9 @@ impl ExtraBlockDataTarget { ed0: Self, ed1: Self, ) { + for i in 0..8 { + builder.connect(ed0.genesis_state_root[i], ed1.genesis_state_root[i]); + } 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); diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 113dd287..72731c99 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -809,6 +809,7 @@ pub(crate) fn add_virtual_block_hashes, const D: us pub(crate) fn add_virtual_extra_block_data, const D: usize>( builder: &mut CircuitBuilder, ) -> ExtraBlockDataTarget { + let genesis_state_root = builder.add_virtual_public_input_arr(); 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(); @@ -816,6 +817,7 @@ pub(crate) fn add_virtual_extra_block_data, const D 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 { + genesis_state_root, txn_number_before, txn_number_after, gas_used_before, @@ -1077,6 +1079,10 @@ where F: RichField + Extendable, W: Witness, { + witness.set_target_arr( + &ed_target.genesis_state_root, + &h256_limbs::(ed.genesis_state_root), + ); witness.set_target( ed_target.txn_number_before, u256_to_u32(ed.txn_number_before)?, diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index a456f02d..d0596209 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -150,6 +150,7 @@ fn add11_yml() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), txn_number_before: 0.into(), gas_used_before: 0.into(), gas_used_after: 0xa868u64.into(), diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index 0130c6fe..162c5e20 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -182,6 +182,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 8bca4acc..dd4e624b 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -57,6 +57,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { }, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index 67407807..2742c425 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -226,6 +226,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), @@ -425,6 +426,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { tries: tries_before, trie_roots_after: tries_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata: block_metadata.clone(), txn_number_before: 0.into(), gas_used_before: 0.into(), @@ -566,6 +568,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 1.into(), gas_used_before: gas_used_second, @@ -591,6 +594,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { trie_roots_before: first_public_values.trie_roots_before, trie_roots_after: public_values.trie_roots_after, extra_block_data: ExtraBlockData { + genesis_state_root: first_public_values.extra_block_data.genesis_state_root, 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, @@ -867,6 +871,7 @@ fn test_two_txn() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index de16db94..ea434c55 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -168,6 +168,7 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index 268ad661..9ec28aa4 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -136,6 +136,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(),