From 4d7d9ffa3cc5a6ba39389a28c18657f641636ce6 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Mon, 11 Sep 2023 15:47:33 +0100 Subject: [PATCH 1/3] Constrain genesis block's state trie. --- evm/src/fixed_recursive_verifier.rs | 55 +++++++++++++++++++++++++---- evm/src/generation/mod.rs | 3 ++ evm/src/get_challenges.rs | 2 ++ evm/src/proof.rs | 35 +++++++++++++----- evm/src/recursive_verifier.rs | 6 ++++ evm/tests/add11_yml.rs | 1 + evm/tests/basic_smart_contract.rs | 1 + evm/tests/empty_txn_list.rs | 1 + evm/tests/log_opcode.rs | 5 +++ evm/tests/self_balance_gas_cost.rs | 1 + evm/tests/simple_transfer.rs | 1 + 11 files changed, 96 insertions(+), 15 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 72844db6..4d3f4d3d 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -640,6 +640,11 @@ 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); + } + // 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); @@ -776,6 +781,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); @@ -787,13 +802,23 @@ 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); + // Chack 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 a predetermined state trie root. + for (&limb0, limb1) in rhs + .trie_roots_before + .state_root + .iter() + .zip(rhs.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( @@ -981,16 +1006,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 13c6670b..01e3209d 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -48,6 +48,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. @@ -251,6 +253,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 59be8439..0afa1d80 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -116,6 +116,7 @@ fn observe_extra_block_data< challenger: &mut Challenger, extra_data: &ExtraBlockData, ) { + challenger.observe_elements(&h256_limbs(extra_data.genesis_state_root)); 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())); @@ -138,6 +139,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 14f22b67..a5bb2b3d 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -95,6 +95,7 @@ pub struct BlockMetadata { #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct ExtraBlockData { + pub genesis_state_root: H256, pub txn_number_before: U256, pub txn_number_after: U256, pub gas_used_before: U256, @@ -166,6 +167,7 @@ impl PublicValuesTarget { buffer.write_target_array(&cur_hash)?; let ExtraBlockDataTarget { + genesis_state_root, txn_number_before, txn_number_after, gas_used_before, @@ -173,6 +175,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)?; @@ -214,6 +217,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()?, @@ -381,7 +385,7 @@ pub struct BlockMetadataTarget { } impl BlockMetadataTarget { - const SIZE: usize = 77; + pub const SIZE: usize = 77; pub fn from_public_inputs(pis: &[Target]) -> Self { let block_beneficiary = pis[0..5].try_into().unwrap(); @@ -465,7 +469,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(), @@ -505,6 +509,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, @@ -514,17 +519,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, @@ -541,6 +548,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, @@ -571,6 +585,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 e669f4ab..3558dc9a 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -808,6 +808,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(); @@ -815,6 +816,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, @@ -1070,6 +1072,10 @@ pub(crate) fn set_extra_public_values_target( 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, F::from_canonical_usize(ed.txn_number_before.as_usize()), diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index f628e944..89ea13ee 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -149,6 +149,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 4d0a2090..3118a34a 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -165,6 +165,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 806726fc..977f3efd 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 271ab945..d86379ca 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -225,6 +225,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(), @@ -423,6 +424,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(), @@ -564,6 +566,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, @@ -589,6 +592,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, @@ -864,6 +868,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 d3461647..d0e95e11 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -154,6 +154,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 b8c47fe9..5dd3b1ac 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -135,6 +135,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(), From 3983969ce9724da1b2f077eb7f475317dfe6b1f2 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Mon, 25 Sep 2023 10:35:38 -0400 Subject: [PATCH 2/3] Use function for genesis block connection. --- evm/src/fixed_recursive_verifier.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 55577fb2..0e77e571 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -819,16 +819,28 @@ where let zero = builder.zero(); let has_not_parent_block = builder.sub(one, has_parent_block.target); + // Chack 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); - // Check that the genesis block has a predetermined state trie root. - for (&limb0, limb1) in rhs + // 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(rhs.extra_block_data.genesis_state_root) + .zip(x.extra_block_data.genesis_state_root) { let mut constr = builder.sub(limb0, limb1); constr = builder.mul(has_not_parent_block, constr); From 75c0e47a3007d33681620c845bf8fc15bcd04f6c Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Tue, 26 Sep 2023 12:21:29 -0400 Subject: [PATCH 3/3] Apply comments. --- evm/src/fixed_recursive_verifier.rs | 5 ++++- evm/src/proof.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 0e77e571..dffbd245 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -659,6 +659,9 @@ where 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); @@ -820,7 +823,7 @@ where let zero = builder.zero(); let has_not_parent_block = builder.sub(one, has_parent_block.target); - // Chack that the genesis block number is 0. + // 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); diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 71b2feb4..b3020fe6 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -118,7 +118,7 @@ pub struct BlockMetadata { /// unlike `BlockMetadata`. #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct ExtraBlockData { - /// The state trie digest of the gensis block. + /// 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.