From 7cb794210e4f50d8f4303ab61ee84b79108977f3 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 20 Aug 2024 18:07:10 -0400 Subject: [PATCH 1/9] Truncate final PVs --- evm_arithmetization/src/proof.rs | 128 +++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 39 deletions(-) diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 34ab31d4b..54371ae3e 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -115,19 +115,13 @@ impl PublicValues { } } -/// Memory values which are public. +/// Memory values which are public once a final block proof is generated. #[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct FinalPublicValues { - /// Trie hashes before the execution of the local state transition - pub trie_roots_before: TrieRoots, - /// Trie hashes after the execution of the local state transition. - pub trie_roots_after: TrieRoots, - /// Block metadata: it remains unchanged within a block. - pub block_metadata: BlockMetadata, - /// 256 previous block hashes and current block's hash. - pub block_hashes: BlockHashes, - /// Extra block data that is specific to the current proof. - pub extra_block_data: ExtraBlockData, + /// State trie root before the execution of this global state transition. + pub state_trie_root_before: H256, + /// State trie root after the execution of this global state transition. + pub state_trie_root_after: H256, } impl FinalPublicValues { @@ -135,33 +129,16 @@ impl FinalPublicValues { /// Public values are always the first public inputs added to the circuit, /// so we can start extracting at index 0. pub fn from_public_inputs(pis: &[F]) -> Self { - assert!( - PublicValuesTarget::SIZE - 2 * RegistersDataTarget::SIZE - 2 * MemCapTarget::SIZE - <= pis.len() - ); + assert!(PublicValuesTarget::SIZE <= pis.len()); let mut offset = 0; - let trie_roots_before = - TrieRoots::from_public_inputs(&pis[offset..offset + TrieRootsTarget::SIZE]); - offset += TrieRootsTarget::SIZE; - let trie_roots_after = - TrieRoots::from_public_inputs(&pis[offset..offset + TrieRootsTarget::SIZE]); - offset += TrieRootsTarget::SIZE; - let block_metadata = - BlockMetadata::from_public_inputs(&pis[offset..offset + BlockMetadataTarget::SIZE]); - offset += BlockMetadataTarget::SIZE; - let block_hashes = - BlockHashes::from_public_inputs(&pis[offset..offset + BlockHashesTarget::SIZE]); - offset += BlockHashesTarget::SIZE; - let extra_block_data = - ExtraBlockData::from_public_inputs(&pis[offset..offset + ExtraBlockDataTarget::SIZE]); + let state_trie_root_before = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); + offset += TrieRootsTarget::SIZE; // skip over txn and receipt tries + let state_trie_root_after = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); Self { - trie_roots_before, - trie_roots_after, - block_metadata, - block_hashes, - extra_block_data, + state_trie_root_before, + state_trie_root_after, } } } @@ -169,11 +146,83 @@ impl FinalPublicValues { impl From for FinalPublicValues { fn from(value: PublicValues) -> Self { Self { - trie_roots_before: value.trie_roots_before, - trie_roots_after: value.trie_roots_after, - block_metadata: value.block_metadata, - block_hashes: value.block_hashes, - extra_block_data: value.extra_block_data, + state_trie_root_before: value.trie_roots_before.state_root, + state_trie_root_after: value.trie_roots_after.state_root, + } + } +} + +/// Memory values which are public once a final block proof is generated. +/// Note: All the larger integers are encoded with 32-bit limbs in little-endian +/// order. +#[derive(Eq, PartialEq, Debug)] +pub struct FinalPublicValuesTarget { + /// State trie root before the execution of this global state transition. + pub state_trie_root_before: [Target; TARGET_HASH_SIZE], + /// State trie root after the execution of this global state transition. + pub state_trie_root_after: [Target; TARGET_HASH_SIZE], +} + +impl FinalPublicValuesTarget { + pub(crate) const SIZE: usize = TARGET_HASH_SIZE * 2; + + /// Serializes public value targets. + pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target_array(&self.state_trie_root_before)?; + buffer.write_target_array(&self.state_trie_root_after)?; + + Ok(()) + } + + /// Deserializes public value targets. + pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { + let state_trie_root_before = buffer.read_target_array()?; + let state_trie_root_after = buffer.read_target_array()?; + + Ok(Self { + state_trie_root_before, + state_trie_root_after, + }) + } + + /// Extracts public value `Target`s from the given public input `Target`s. + /// Public values are always the first public inputs added to the circuit, + /// so we can start extracting at index 0. + pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { + assert!(pis.len() >= Self::SIZE); + + let mut offset = 0; + let state_trie_root_before = pis[offset..offset + TARGET_HASH_SIZE].try_into().unwrap(); + offset += TARGET_HASH_SIZE; + let state_trie_root_after = pis[offset..offset + TARGET_HASH_SIZE].try_into().unwrap(); + Self { + state_trie_root_before, + state_trie_root_after, + } + } + + /// Returns the public values in `pv0` or `pv1` depending on `condition`. + pub(crate) fn select, const D: usize>( + builder: &mut CircuitBuilder, + condition: BoolTarget, + pv0: Self, + pv1: Self, + ) -> Self { + Self { + state_trie_root_before: core::array::from_fn(|i| { + builder.select( + condition, + pv0.state_trie_root_before[i], + pv1.state_trie_root_before[i], + ) + }), + state_trie_root_after: core::array::from_fn(|i| { + builder.select( + condition, + pv0.state_trie_root_after[i], + pv1.state_trie_root_after[i], + ) + }), } } } @@ -419,6 +468,7 @@ pub struct MemCap { /// STARK cap. pub mem_cap: Vec<[U256; NUM_HASH_OUT_ELTS]>, } + impl MemCap { pub fn from_public_inputs(pis: &[F]) -> Self { let mem_cap = (0..DEFAULT_CAP_LEN) From 3d5551c9da0d5666b901e99c4add1d585476d860 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 21 Aug 2024 18:11:43 -0400 Subject: [PATCH 2/9] Add extra layer --- .../src/fixed_recursive_verifier.rs | 205 ++++++++++++++-- evm_arithmetization/src/proof.rs | 56 ++--- evm_arithmetization/src/recursive_verifier.rs | 226 +++++++++++++++++- evm_arithmetization/tests/two_to_one_block.rs | 62 ++--- 4 files changed, 453 insertions(+), 96 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index a892e924a..a052b74f3 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -42,14 +42,15 @@ use crate::generation::{GenerationInputs, TrimmedGenerationInputs}; use crate::get_challenges::observe_public_values_target; use crate::proof::{ AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, - FinalPublicValues, MemCapTarget, PublicValues, PublicValuesTarget, RegistersDataTarget, - TrieRoots, TrieRootsTarget, DEFAULT_CAP_LEN, TARGET_HASH_SIZE, + FinalPublicValues, FinalPublicValuesTarget, MemCapTarget, PublicValues, PublicValuesTarget, + RegistersDataTarget, TrieRoots, TrieRootsTarget, DEFAULT_CAP_LEN, TARGET_HASH_SIZE, }; use crate::prover::{check_abort_signal, prove}; use crate::recursive_verifier::{ - add_common_recursion_gates, add_virtual_public_values, get_memory_extra_looking_sum_circuit, - recursive_stark_circuit, set_public_value_targets, PlonkWrapperCircuit, PublicInputs, - StarkWrapperCircuit, + add_common_recursion_gates, add_virtual_final_public_values_public_input, + add_virtual_public_values_public_input, get_memory_extra_looking_sum_circuit, + recursive_stark_circuit, set_final_public_value_targets, set_public_value_targets, + PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::util::h256_limbs; use crate::verifier::initial_memory_merkle_cap; @@ -95,6 +96,9 @@ where /// The block circuit, which verifies a transaction aggregation proof and an /// optional previous block proof. pub block: BlockCircuitData, + /// A single wrapping layer on top of a block proof for easy aggregation + /// with additional block proofs from other chains. + pub block_wrapper: BlockWrapperCircuitData, /// The two-to-one block aggregation circuit, which verifies two unrelated /// block proofs. pub two_to_one_block: TwoToOneBlockCircuitData, @@ -434,6 +438,56 @@ where } } +/// Data for the block wrapper circuit, which is used to generate a wrap final +/// block proof and obfuscate the remaining private elements of a chain. +#[derive(Eq, PartialEq, Debug)] +pub struct BlockWrapperCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub circuit: CircuitData, + parent_block_proof: ProofWithPublicInputsTarget, + public_values: FinalPublicValuesTarget, + local_vk: VerifierCircuitTarget, +} + +impl BlockWrapperCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + fn to_buffer( + &self, + buffer: &mut Vec, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult<()> { + buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; + buffer.write_target_proof_with_public_inputs(&self.parent_block_proof)?; + buffer.write_target_verifier_circuit(&self.local_vk)?; + self.public_values.to_buffer(buffer) + } + + fn from_buffer( + buffer: &mut Buffer, + gate_serializer: &dyn GateSerializer, + generator_serializer: &dyn WitnessGeneratorSerializer, + ) -> IoResult { + let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; + let parent_block_proof = buffer.read_target_proof_with_public_inputs()?; + let public_values = FinalPublicValuesTarget::from_buffer(buffer)?; + let local_vk = buffer.read_target_verifier_circuit()?; + + Ok(Self { + circuit, + parent_block_proof, + public_values, + local_vk, + }) + } +} + /// Data for the two-to-one block circuit, which is used to generate a /// proof of two unrelated proofs. #[derive(Eq, PartialEq, Debug)] @@ -518,6 +572,8 @@ where .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; self.block .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + self.block_wrapper + .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; self.two_to_one_block .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; if !skip_tables { @@ -562,6 +618,11 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; + let block_wrapper = BlockWrapperCircuitData::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; let two_to_one_block = TwoToOneBlockCircuitData::from_buffer( &mut buffer, gate_serializer, @@ -603,6 +664,7 @@ where segment_aggregation, txn_aggregation, block, + block_wrapper, two_to_one_block, by_table, }) @@ -714,12 +776,14 @@ where let txn_aggregation = Self::create_txn_aggregation_circuit(&segment_aggregation, stark_config); let block = Self::create_block_circuit(&txn_aggregation); - let two_to_one_block = Self::create_two_to_one_block_circuit(&block); + let block_wrapper = Self::create_block_wrapper_circuit(&block); + let two_to_one_block = Self::create_two_to_one_block_circuit(&block_wrapper); Self { root, segment_aggregation, txn_aggregation, block, + block_wrapper, two_to_one_block, by_table, } @@ -754,7 +818,7 @@ where let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); - let public_values = add_virtual_public_values(&mut builder); + let public_values = add_virtual_public_values_public_input(&mut builder); let recursive_proofs = core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i])); @@ -895,7 +959,7 @@ where root: &RootCircuitData, ) -> SegmentAggregationCircuitData { let mut builder = CircuitBuilder::::new(root.circuit.common.config.clone()); - let public_values = add_virtual_public_values(&mut builder); + let public_values = add_virtual_public_values_public_input(&mut builder); let cyclic_vk = builder.add_verifier_data_public_inputs(); // The right hand side child might be dummy. @@ -1051,7 +1115,7 @@ where // Create a circuit for the aggregation of two transactions. let mut builder = CircuitBuilder::::new(agg.circuit.common.config.clone()); - let public_values = add_virtual_public_values(&mut builder); + let public_values = add_virtual_public_values_public_input(&mut builder); let cyclic_vk = builder.add_verifier_data_public_inputs(); let lhs_txn_proof = Self::add_txn_agg_child(&mut builder, agg); @@ -1233,7 +1297,7 @@ where }; let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); - let public_values = add_virtual_public_values(&mut builder); + let public_values = add_virtual_public_values_public_input(&mut builder); let has_parent_block = builder.add_virtual_bool_target_safe(); 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); @@ -1418,6 +1482,45 @@ where } } + fn create_block_wrapper_circuit( + block: &BlockCircuitData, + ) -> BlockWrapperCircuitData { + let mut builder = CircuitBuilder::::new(block.circuit.common.config.clone()); + + let parent_block_proof = builder.add_virtual_proof_with_pis(&block.circuit.common); + let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); + + let final_pv = add_virtual_final_public_values_public_input(&mut builder); + final_pv.connect_parent(&mut builder, &parent_pv); + + let block_verifier_data = builder.constant_verifier_data(&block.circuit.verifier_only); + + // We want these wrapped block proofs to have the exact same structure as 2-to-1 + // aggregation proofs, so we add public inputs for cyclic verification, + // even though they'll be ignored. + let local_vk = builder.add_verifier_data_public_inputs(); + + builder.verify_proof::( + &parent_block_proof, + &block_verifier_data, + &block.circuit.common, + ); + + // Pad to match the block circuit's degree. + while log2_ceil(builder.num_gates()) < block.circuit.common.degree_bits() { + builder.add_gate(NoopGate, vec![]); + } + + let circuit = builder.build::(); + + BlockWrapperCircuitData { + circuit, + parent_block_proof, + public_values: final_pv, + local_vk, + } + } + /// Create two-to-one block aggregation circuit. /// /// # Arguments @@ -1429,22 +1532,23 @@ where /// /// Returns a [`TwoToOneBlockCircuitData`]. fn create_two_to_one_block_circuit( - block_circuit: &BlockCircuitData, + block_wrapper_circuit: &BlockWrapperCircuitData, ) -> TwoToOneBlockCircuitData where F: RichField + Extendable, C: GenericConfig, C::Hasher: AlgebraicHasher, { - let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); + let mut builder = + CircuitBuilder::::new(block_wrapper_circuit.circuit.common.config.clone()); let mix_hash = builder.add_virtual_hash_public_input(); // We need to pad by PIS to match the count of PIS of the `base_proof`. - let mut padding = block_circuit.circuit.common.num_public_inputs; + let mut padding = block_wrapper_circuit.circuit.common.num_public_inputs; // The number of PIS that will be added *after* padding by // [`add_verifier_data_public_inputs()`]. - padding -= verification_key_len(&block_circuit.circuit); + padding -= verification_key_len(&block_wrapper_circuit.circuit); // Account for `mix_pv_hash`. padding -= builder.num_public_inputs(); @@ -1455,14 +1559,14 @@ where let cyclic_vk = builder.add_verifier_data_public_inputs(); - let lhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); - let rhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); + let lhs = Self::add_agg_child(&mut builder, &block_wrapper_circuit.circuit); + let rhs = Self::add_agg_child(&mut builder, &block_wrapper_circuit.circuit); let lhs_public_inputs = lhs.public_inputs(&mut builder); let rhs_public_inputs = rhs.public_inputs(&mut builder); - let lhs_public_values = extract_block_public_values(&lhs_public_inputs); - let rhs_public_values = extract_block_public_values(&rhs_public_inputs); + let lhs_public_values = extract_block_final_public_values(&lhs_public_inputs); + let rhs_public_values = extract_block_final_public_values(&rhs_public_inputs); let lhs_agg_pv_hash = extract_two_to_one_block_hash(&lhs_public_inputs); let rhs_agg_pv_hash = extract_two_to_one_block_hash(&rhs_public_inputs); @@ -2116,7 +2220,7 @@ where opt_parent_block_proof: Option<&ProofWithPublicInputs>, agg_root_proof: &ProofWithPublicInputs, public_values: PublicValues, - ) -> anyhow::Result<(ProofWithPublicInputs, FinalPublicValues)> { + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut block_inputs = PartialWitness::new(); block_inputs.set_bool_target( @@ -2246,7 +2350,7 @@ where })?; let block_proof = self.block.circuit.prove(block_inputs)?; - Ok((block_proof, block_public_values.into())) + Ok((block_proof, block_public_values)) } pub fn verify_block(&self, block_proof: &ProofWithPublicInputs) -> anyhow::Result<()> { @@ -2258,6 +2362,59 @@ where ) } + /// Wrap a block proof, representing one or an aggregation of contiguous + /// blocks, for easier aggregation with other chains' proofs. + /// + /// # Arguments + /// + /// - `block_proof`: the final block proof of a chain to be wrapped. + /// - `public_values`: the public values associated to the aggregation + /// proof. + /// + /// # Outputs + /// + /// This method outputs a tuple of [`ProofWithPublicInputs`] and + /// associated [`FinalPublicValues`]. Only the proof with public inputs is + /// necessary for a verifier to assert correctness of the computation. + pub fn prove_block_wrapper( + &self, + block_proof: &ProofWithPublicInputs, + public_values: PublicValues, + ) -> anyhow::Result<(ProofWithPublicInputs, FinalPublicValues)> { + let mut block_wrapper_inputs = PartialWitness::new(); + + block_wrapper_inputs + .set_proof_with_pis_target(&self.block_wrapper.parent_block_proof, block_proof); + + block_wrapper_inputs.set_verifier_data_target( + &self.block_wrapper.local_vk, // dummy + &self.block_wrapper.circuit.verifier_only, + ); + + let final_pvs = public_values.into(); + set_final_public_value_targets( + &mut block_wrapper_inputs, + &self.block_wrapper.public_values, + &final_pvs, + ) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; + + let block_proof = self.block_wrapper.circuit.prove(block_wrapper_inputs)?; + + Ok((block_proof, final_pvs)) + } + + pub fn verify_block_wrapper( + &self, + wrapped_block_proof: &ProofWithPublicInputs, + ) -> anyhow::Result<()> { + self.block_wrapper + .circuit + .verify(wrapped_block_proof.clone()) + } + /// Aggregates two proofs in manner similar to [`prove_aggregation`]. /// /// # Arguments @@ -2677,10 +2834,12 @@ pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OU /// /// # Outputs /// -/// - A slice containing exactly the public values. -pub fn extract_block_public_values(public_inputs: &[T]) -> &[T; PublicValuesTarget::SIZE] { +/// - A slice containing exactly the final public values. +pub fn extract_block_final_public_values( + public_inputs: &[T], +) -> &[T; FinalPublicValuesTarget::SIZE] { const PV_INDEX_START: usize = 0; - const PV_INDEX_END: usize = PV_INDEX_START + PublicValuesTarget::SIZE; + const PV_INDEX_END: usize = PV_INDEX_START + FinalPublicValuesTarget::SIZE; public_inputs[PV_INDEX_START..PV_INDEX_END] .try_into() .expect("Public inputs vector was malformed.") diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 54371ae3e..67b44ae04 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -129,11 +129,11 @@ impl FinalPublicValues { /// Public values are always the first public inputs added to the circuit, /// so we can start extracting at index 0. pub fn from_public_inputs(pis: &[F]) -> Self { - assert!(PublicValuesTarget::SIZE <= pis.len()); + assert!(FinalPublicValuesTarget::SIZE <= pis.len()); let mut offset = 0; let state_trie_root_before = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); - offset += TrieRootsTarget::SIZE; // skip over txn and receipt tries + offset += TARGET_HASH_SIZE; let state_trie_root_after = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); Self { @@ -185,44 +185,22 @@ impl FinalPublicValuesTarget { }) } - /// Extracts public value `Target`s from the given public input `Target`s. - /// Public values are always the first public inputs added to the circuit, - /// so we can start extracting at index 0. - pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - assert!(pis.len() >= Self::SIZE); - - let mut offset = 0; - let state_trie_root_before = pis[offset..offset + TARGET_HASH_SIZE].try_into().unwrap(); - offset += TARGET_HASH_SIZE; - let state_trie_root_after = pis[offset..offset + TARGET_HASH_SIZE].try_into().unwrap(); - Self { - state_trie_root_before, - state_trie_root_after, - } - } - - /// Returns the public values in `pv0` or `pv1` depending on `condition`. - pub(crate) fn select, const D: usize>( + /// Connects these `FinalPublicValuesTarget` with their corresponding + /// counterpart in a full parent `PublicValuesTarget`. + pub(crate) fn connect_parent, const D: usize>( + &self, builder: &mut CircuitBuilder, - condition: BoolTarget, - pv0: Self, - pv1: Self, - ) -> Self { - Self { - state_trie_root_before: core::array::from_fn(|i| { - builder.select( - condition, - pv0.state_trie_root_before[i], - pv1.state_trie_root_before[i], - ) - }), - state_trie_root_after: core::array::from_fn(|i| { - builder.select( - condition, - pv0.state_trie_root_after[i], - pv1.state_trie_root_after[i], - ) - }), + pv1: &PublicValuesTarget, + ) { + for i in 0..8 { + builder.connect( + self.state_trie_root_before[i], + pv1.trie_roots_before.state_root[i], + ); + builder.connect( + self.state_trie_root_after[i], + pv1.trie_roots_after.state_root[i], + ); } } } diff --git a/evm_arithmetization/src/recursive_verifier.rs b/evm_arithmetization/src/recursive_verifier.rs index 391032307..96328bb0e 100644 --- a/evm_arithmetization/src/recursive_verifier.rs +++ b/evm_arithmetization/src/recursive_verifier.rs @@ -36,8 +36,9 @@ use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::proof::{ BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData, - ExtraBlockDataTarget, MemCap, MemCapTarget, PublicValues, PublicValuesTarget, RegistersData, - RegistersDataTarget, TrieRoots, TrieRootsTarget, DEFAULT_CAP_LEN, + ExtraBlockDataTarget, FinalPublicValues, FinalPublicValuesTarget, MemCap, MemCapTarget, + PublicValues, PublicValuesTarget, RegistersData, RegistersDataTarget, TrieRoots, + TrieRootsTarget, DEFAULT_CAP_LEN, }; use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; use crate::witness::errors::ProgramError; @@ -611,6 +612,55 @@ fn add_data_write, const D: usize>( builder.add(running_sum, inverse) } +pub(crate) fn add_virtual_final_public_values_public_input< + F: RichField + Extendable, + const D: usize, +>( + builder: &mut CircuitBuilder, +) -> FinalPublicValuesTarget { + let state_trie_root_before = builder.add_virtual_public_input_arr(); + let state_trie_root_after = builder.add_virtual_public_input_arr(); + + FinalPublicValuesTarget { + state_trie_root_before, + state_trie_root_after, + } +} + +pub(crate) fn add_virtual_public_values_public_input< + F: RichField + Extendable, + const D: usize, +>( + builder: &mut CircuitBuilder, +) -> PublicValuesTarget { + let trie_roots_before = add_virtual_trie_roots_public_input(builder); + let trie_roots_after = add_virtual_trie_roots_public_input(builder); + let block_metadata = add_virtual_block_metadata_public_input(builder); + let block_hashes = add_virtual_block_hashes_public_input(builder); + let extra_block_data = add_virtual_extra_block_data_public_input(builder); + let registers_before = add_virtual_registers_data_public_input(builder); + let registers_after = add_virtual_registers_data_public_input(builder); + + let mem_before = MemCapTarget { + mem_cap: MerkleCapTarget(builder.add_virtual_hashes_public_input(DEFAULT_CAP_LEN)), + }; + let mem_after = MemCapTarget { + mem_cap: MerkleCapTarget(builder.add_virtual_hashes_public_input(DEFAULT_CAP_LEN)), + }; + + PublicValuesTarget { + trie_roots_before, + trie_roots_after, + block_metadata, + block_hashes, + extra_block_data, + registers_before, + registers_after, + mem_before, + mem_after, + } +} + pub(crate) fn add_virtual_public_values, const D: usize>( builder: &mut CircuitBuilder, ) -> PublicValuesTarget { @@ -642,12 +692,13 @@ pub(crate) fn add_virtual_public_values, const D: u } } -pub(crate) fn add_virtual_trie_roots, const D: usize>( +pub(crate) fn add_virtual_trie_roots_public_input, const D: usize>( builder: &mut CircuitBuilder, ) -> TrieRootsTarget { let state_root = builder.add_virtual_public_input_arr(); let transactions_root = builder.add_virtual_public_input_arr(); let receipts_root = builder.add_virtual_public_input_arr(); + TrieRootsTarget { state_root, transactions_root, @@ -655,7 +706,24 @@ pub(crate) fn add_virtual_trie_roots, const D: usiz } } -pub(crate) fn add_virtual_block_metadata, const D: usize>( +pub(crate) fn add_virtual_trie_roots, const D: usize>( + builder: &mut CircuitBuilder, +) -> TrieRootsTarget { + let state_root = builder.add_virtual_target_arr(); + let transactions_root = builder.add_virtual_target_arr(); + let receipts_root = builder.add_virtual_target_arr(); + + TrieRootsTarget { + state_root, + transactions_root, + receipts_root, + } +} + +pub(crate) fn add_virtual_block_metadata_public_input< + F: RichField + Extendable, + const D: usize, +>( builder: &mut CircuitBuilder, ) -> BlockMetadataTarget { let block_beneficiary = builder.add_virtual_public_input_arr(); @@ -671,6 +739,7 @@ pub(crate) fn add_virtual_block_metadata, const D: let block_excess_blob_gas = builder.add_virtual_public_input_arr(); let parent_beacon_block_root = builder.add_virtual_public_input_arr(); let block_bloom = builder.add_virtual_public_input_arr(); + BlockMetadataTarget { block_beneficiary, block_timestamp, @@ -688,17 +757,71 @@ pub(crate) fn add_virtual_block_metadata, const D: } } -pub(crate) fn add_virtual_block_hashes, const D: usize>( +pub(crate) fn add_virtual_block_metadata, const D: usize>( + builder: &mut CircuitBuilder, +) -> BlockMetadataTarget { + let block_beneficiary = builder.add_virtual_target_arr(); + let block_timestamp = builder.add_virtual_target(); + let block_number = builder.add_virtual_target(); + let block_difficulty = builder.add_virtual_target(); + let block_random = builder.add_virtual_target_arr(); + let block_gaslimit = builder.add_virtual_target(); + let block_chain_id = builder.add_virtual_target(); + let block_base_fee = builder.add_virtual_target_arr(); + let block_gas_used = builder.add_virtual_target(); + let block_blob_gas_used = builder.add_virtual_target_arr(); + let block_excess_blob_gas = builder.add_virtual_target_arr(); + let parent_beacon_block_root = builder.add_virtual_target_arr(); + let block_bloom = builder.add_virtual_target_arr(); + + BlockMetadataTarget { + block_beneficiary, + block_timestamp, + block_number, + block_difficulty, + block_random, + block_gaslimit, + block_chain_id, + block_base_fee, + block_gas_used, + block_blob_gas_used, + block_excess_blob_gas, + parent_beacon_block_root, + block_bloom, + } +} + +pub(crate) fn add_virtual_block_hashes_public_input< + F: RichField + Extendable, + const D: usize, +>( builder: &mut CircuitBuilder, ) -> BlockHashesTarget { let prev_hashes = builder.add_virtual_public_input_arr(); let cur_hash = builder.add_virtual_public_input_arr(); + BlockHashesTarget { prev_hashes, cur_hash, } } -pub(crate) fn add_virtual_extra_block_data, const D: usize>( + +pub(crate) fn add_virtual_block_hashes, const D: usize>( + builder: &mut CircuitBuilder, +) -> BlockHashesTarget { + let prev_hashes = builder.add_virtual_target_arr(); + let cur_hash = builder.add_virtual_target_arr(); + + BlockHashesTarget { + prev_hashes, + cur_hash, + } +} + +pub(crate) fn add_virtual_extra_block_data_public_input< + F: RichField + Extendable, + const D: usize, +>( builder: &mut CircuitBuilder, ) -> ExtraBlockDataTarget { let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); @@ -706,6 +829,7 @@ pub(crate) fn add_virtual_extra_block_data, const D 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(); + ExtraBlockDataTarget { checkpoint_state_trie_root, txn_number_before, @@ -715,7 +839,28 @@ pub(crate) fn add_virtual_extra_block_data, const D } } -pub(crate) fn add_virtual_registers_data, const D: usize>( +pub(crate) fn add_virtual_extra_block_data, const D: usize>( + builder: &mut CircuitBuilder, +) -> ExtraBlockDataTarget { + let checkpoint_state_trie_root = builder.add_virtual_target_arr(); + let txn_number_before = builder.add_virtual_target(); + let txn_number_after = builder.add_virtual_target(); + let gas_used_before = builder.add_virtual_target(); + let gas_used_after = builder.add_virtual_target(); + + ExtraBlockDataTarget { + checkpoint_state_trie_root, + txn_number_before, + txn_number_after, + gas_used_before, + gas_used_after, + } +} + +pub(crate) fn add_virtual_registers_data_public_input< + F: RichField + Extendable, + const D: usize, +>( builder: &mut CircuitBuilder, ) -> RegistersDataTarget { let program_counter = builder.add_virtual_public_input(); @@ -724,6 +869,27 @@ pub(crate) fn add_virtual_registers_data, const D: let stack_top = builder.add_virtual_public_input_arr(); let context = builder.add_virtual_public_input(); let gas_used = builder.add_virtual_public_input(); + + RegistersDataTarget { + program_counter, + is_kernel, + stack_len, + stack_top, + context, + gas_used, + } +} + +pub(crate) fn add_virtual_registers_data, const D: usize>( + builder: &mut CircuitBuilder, +) -> RegistersDataTarget { + let program_counter = builder.add_virtual_target(); + let is_kernel = builder.add_virtual_target(); + let stack_len = builder.add_virtual_target(); + let stack_top = builder.add_virtual_target_arr(); + let context = builder.add_virtual_target(); + let gas_used = builder.add_virtual_target(); + RegistersDataTarget { program_counter, is_kernel, @@ -807,6 +973,52 @@ where Ok(()) } +pub fn set_final_public_value_targets( + witness: &mut W, + public_values_target: &FinalPublicValuesTarget, + public_values: &FinalPublicValues, +) -> Result<(), ProgramError> +where + F: RichField + Extendable, + W: Witness, +{ + for (i, limb) in public_values + .state_trie_root_before + .into_uint() + .0 + .into_iter() + .enumerate() + { + witness.set_target( + public_values_target.state_trie_root_before[2 * i], + F::from_canonical_u32(limb as u32), + ); + witness.set_target( + public_values_target.state_trie_root_before[2 * i + 1], + F::from_canonical_u32((limb >> 32) as u32), + ); + } + + for (i, limb) in public_values + .state_trie_root_after + .into_uint() + .0 + .into_iter() + .enumerate() + { + witness.set_target( + public_values_target.state_trie_root_after[2 * i], + F::from_canonical_u32(limb as u32), + ); + witness.set_target( + public_values_target.state_trie_root_after[2 * i + 1], + F::from_canonical_u32((limb >> 32) as u32), + ); + } + + Ok(()) +} + pub(crate) fn set_trie_roots_target( witness: &mut W, trie_roots_target: &TrieRootsTarget, diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 59f0c377d..326bd08cc 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -1,10 +1,10 @@ use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256}; use evm_arithmetization::fixed_recursive_verifier::{ - extract_block_public_values, extract_two_to_one_block_hash, + extract_block_final_public_values, extract_two_to_one_block_hash, }; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; -use evm_arithmetization::proof::{BlockMetadata, PublicValues, TrieRoots}; +use evm_arithmetization::proof::{BlockMetadata, FinalPublicValues, PublicValues, TrieRoots}; use evm_arithmetization::testing_utils::{ beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, @@ -127,7 +127,7 @@ fn get_test_block_proof( let dummy0_proof = all_circuits.prove_segment_aggregation(false, &dummy1_proof[0], false, &dummy1_proof[1])?; - let (agg_proof0, pv0) = all_circuits.prove_transaction_aggregation( + let (agg_proof, pv) = all_circuits.prove_transaction_aggregation( false, &inputs0_proof.proof_with_pis, inputs0_proof.public_values, @@ -136,26 +136,39 @@ fn get_test_block_proof( dummy0_proof.public_values, )?; - all_circuits.verify_txn_aggregation(&agg_proof0)?; + all_circuits.verify_txn_aggregation(&agg_proof)?; // Test retrieved public values from the proof public inputs. - let retrieved_public_values0 = PublicValues::from_public_inputs(&agg_proof0.public_inputs); - assert_eq!(retrieved_public_values0, pv0); + let retrieved_public_values = PublicValues::from_public_inputs(&agg_proof.public_inputs); + assert_eq!(retrieved_public_values, pv); assert_eq!( - pv0.trie_roots_before.state_root, - pv0.extra_block_data.checkpoint_state_trie_root + pv.trie_roots_before.state_root, + pv.extra_block_data.checkpoint_state_trie_root ); - let (block_proof0, block_public_values) = all_circuits.prove_block( + let (block_proof, block_public_values) = all_circuits.prove_block( None, // We don't specify a previous proof, considering block 1 as the new checkpoint. - &agg_proof0, - pv0.clone(), + &agg_proof, + pv.clone(), )?; - let pv_block = PublicValues::from_public_inputs(&block_proof0.public_inputs); - assert_eq!(block_public_values, pv_block.into()); + all_circuits.verify_block(&block_proof)?; - Ok(block_proof0) + // Test retrieved public values from the proof public inputs. + let retrieved_public_values = PublicValues::from_public_inputs(&block_proof.public_inputs); + assert_eq!(retrieved_public_values, block_public_values); + + let (wrapped_block_proof, block_final_public_values) = + all_circuits.prove_block_wrapper(&block_proof, block_public_values)?; + + // Test retrieved final public values from the proof public inputs. + let retrieved_final_public_values = + FinalPublicValues::from_public_inputs(&wrapped_block_proof.public_inputs); + assert_eq!(retrieved_final_public_values, block_final_public_values); + + all_circuits.verify_block_wrapper(&wrapped_block_proof)?; + + Ok(wrapped_block_proof) } #[ignore] @@ -170,29 +183,23 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { &all_stark, &[ 16..17, - 9..15, - 12..18, + 8..9, 14..15, 9..10, - 12..13, - 17..20, - 16..17, + 8..9, + 7..8, + 17..18, + 17..18, 7..8, ], &config, ); - let unrelated_block_proofs = some_timestamps + let bp = some_timestamps .iter() .map(|&ts| get_test_block_proof(ts, &all_circuits, &all_stark, &config)) .collect::>>>()?; - unrelated_block_proofs - .iter() - .try_for_each(|bp| all_circuits.verify_block(bp))?; - - let bp = unrelated_block_proofs; - { // Aggregate the same proof twice let aggproof_42_42 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[0], false)?; @@ -224,7 +231,8 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { let mut hashes: Vec<_> = bp .iter() .map(|block_proof| { - let public_values = extract_block_public_values(&block_proof.public_inputs); + let public_values = + extract_block_final_public_values(&block_proof.public_inputs); PoseidonHash::hash_no_pad(public_values) }) .collect(); From 3d48c32491270726ffb7b7ee1eaeb718783b627a Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 21 Aug 2024 18:14:57 -0400 Subject: [PATCH 3/9] Cleanup --- .../src/fixed_recursive_verifier.rs | 6 +- evm_arithmetization/src/recursive_verifier.rs | 129 ------------------ 2 files changed, 3 insertions(+), 132 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index a052b74f3..58eccb5c0 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -438,8 +438,8 @@ where } } -/// Data for the block wrapper circuit, which is used to generate a wrap final -/// block proof and obfuscate the remaining private elements of a chain. +/// Data for the block wrapper circuit, which is used to generate a wrapped +/// final block proof and obfuscate the remaining private elements of a chain. #[derive(Eq, PartialEq, Debug)] pub struct BlockWrapperCircuitData where @@ -1506,7 +1506,7 @@ where &block.circuit.common, ); - // Pad to match the block circuit's degree. + // Pad to match the (non-existing yet!) 2-to-1 circuit's degree. while log2_ceil(builder.num_gates()) < block.circuit.common.degree_bits() { builder.add_gate(NoopGate, vec![]); } diff --git a/evm_arithmetization/src/recursive_verifier.rs b/evm_arithmetization/src/recursive_verifier.rs index 96328bb0e..8f82f4bf2 100644 --- a/evm_arithmetization/src/recursive_verifier.rs +++ b/evm_arithmetization/src/recursive_verifier.rs @@ -661,37 +661,6 @@ pub(crate) fn add_virtual_public_values_public_input< } } -pub(crate) fn add_virtual_public_values, const D: usize>( - builder: &mut CircuitBuilder, -) -> PublicValuesTarget { - let trie_roots_before = add_virtual_trie_roots(builder); - let trie_roots_after = add_virtual_trie_roots(builder); - let block_metadata = add_virtual_block_metadata(builder); - let block_hashes = add_virtual_block_hashes(builder); - let extra_block_data = add_virtual_extra_block_data(builder); - let registers_before = add_virtual_registers_data(builder); - let registers_after = add_virtual_registers_data(builder); - - let mem_before = MemCapTarget { - mem_cap: MerkleCapTarget(builder.add_virtual_hashes_public_input(DEFAULT_CAP_LEN)), - }; - let mem_after = MemCapTarget { - mem_cap: MerkleCapTarget(builder.add_virtual_hashes_public_input(DEFAULT_CAP_LEN)), - }; - - PublicValuesTarget { - trie_roots_before, - trie_roots_after, - block_metadata, - block_hashes, - extra_block_data, - registers_before, - registers_after, - mem_before, - mem_after, - } -} - pub(crate) fn add_virtual_trie_roots_public_input, const D: usize>( builder: &mut CircuitBuilder, ) -> TrieRootsTarget { @@ -706,20 +675,6 @@ pub(crate) fn add_virtual_trie_roots_public_input, } } -pub(crate) fn add_virtual_trie_roots, const D: usize>( - builder: &mut CircuitBuilder, -) -> TrieRootsTarget { - let state_root = builder.add_virtual_target_arr(); - let transactions_root = builder.add_virtual_target_arr(); - let receipts_root = builder.add_virtual_target_arr(); - - TrieRootsTarget { - state_root, - transactions_root, - receipts_root, - } -} - pub(crate) fn add_virtual_block_metadata_public_input< F: RichField + Extendable, const D: usize, @@ -757,40 +712,6 @@ pub(crate) fn add_virtual_block_metadata_public_input< } } -pub(crate) fn add_virtual_block_metadata, const D: usize>( - builder: &mut CircuitBuilder, -) -> BlockMetadataTarget { - let block_beneficiary = builder.add_virtual_target_arr(); - let block_timestamp = builder.add_virtual_target(); - let block_number = builder.add_virtual_target(); - let block_difficulty = builder.add_virtual_target(); - let block_random = builder.add_virtual_target_arr(); - let block_gaslimit = builder.add_virtual_target(); - let block_chain_id = builder.add_virtual_target(); - let block_base_fee = builder.add_virtual_target_arr(); - let block_gas_used = builder.add_virtual_target(); - let block_blob_gas_used = builder.add_virtual_target_arr(); - let block_excess_blob_gas = builder.add_virtual_target_arr(); - let parent_beacon_block_root = builder.add_virtual_target_arr(); - let block_bloom = builder.add_virtual_target_arr(); - - BlockMetadataTarget { - block_beneficiary, - block_timestamp, - block_number, - block_difficulty, - block_random, - block_gaslimit, - block_chain_id, - block_base_fee, - block_gas_used, - block_blob_gas_used, - block_excess_blob_gas, - parent_beacon_block_root, - block_bloom, - } -} - pub(crate) fn add_virtual_block_hashes_public_input< F: RichField + Extendable, const D: usize, @@ -806,18 +727,6 @@ pub(crate) fn add_virtual_block_hashes_public_input< } } -pub(crate) fn add_virtual_block_hashes, const D: usize>( - builder: &mut CircuitBuilder, -) -> BlockHashesTarget { - let prev_hashes = builder.add_virtual_target_arr(); - let cur_hash = builder.add_virtual_target_arr(); - - BlockHashesTarget { - prev_hashes, - cur_hash, - } -} - pub(crate) fn add_virtual_extra_block_data_public_input< F: RichField + Extendable, const D: usize, @@ -839,24 +748,6 @@ pub(crate) fn add_virtual_extra_block_data_public_input< } } -pub(crate) fn add_virtual_extra_block_data, const D: usize>( - builder: &mut CircuitBuilder, -) -> ExtraBlockDataTarget { - let checkpoint_state_trie_root = builder.add_virtual_target_arr(); - let txn_number_before = builder.add_virtual_target(); - let txn_number_after = builder.add_virtual_target(); - let gas_used_before = builder.add_virtual_target(); - let gas_used_after = builder.add_virtual_target(); - - ExtraBlockDataTarget { - checkpoint_state_trie_root, - txn_number_before, - txn_number_after, - gas_used_before, - gas_used_after, - } -} - pub(crate) fn add_virtual_registers_data_public_input< F: RichField + Extendable, const D: usize, @@ -880,26 +771,6 @@ pub(crate) fn add_virtual_registers_data_public_input< } } -pub(crate) fn add_virtual_registers_data, const D: usize>( - builder: &mut CircuitBuilder, -) -> RegistersDataTarget { - let program_counter = builder.add_virtual_target(); - let is_kernel = builder.add_virtual_target(); - let stack_len = builder.add_virtual_target(); - let stack_top = builder.add_virtual_target_arr(); - let context = builder.add_virtual_target(); - let gas_used = builder.add_virtual_target(); - - RegistersDataTarget { - program_counter, - is_kernel, - stack_len, - stack_top, - context, - gas_used, - } -} - pub(crate) fn debug_public_values(public_values: &PublicValues) { log::debug!("Public Values:"); log::debug!( From 77b7cc9d42b1391d032391a8975fa19624953123 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 21 Aug 2024 18:17:56 -0400 Subject: [PATCH 4/9] Add checkpoint root --- evm_arithmetization/src/proof.rs | 17 ++++++++++++++++- evm_arithmetization/src/recursive_verifier.rs | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 67b44ae04..4b38ec239 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -122,6 +122,8 @@ pub struct FinalPublicValues { pub state_trie_root_before: H256, /// State trie root after the execution of this global state transition. pub state_trie_root_after: H256, + /// The state trie digest of the checkpoint block. + pub checkpoint_state_trie_root: H256, } impl FinalPublicValues { @@ -135,10 +137,13 @@ impl FinalPublicValues { let state_trie_root_before = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); offset += TARGET_HASH_SIZE; let state_trie_root_after = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); + offset += TARGET_HASH_SIZE; + let checkpoint_state_trie_root = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); Self { state_trie_root_before, state_trie_root_after, + checkpoint_state_trie_root, } } } @@ -148,6 +153,7 @@ impl From for FinalPublicValues { Self { state_trie_root_before: value.trie_roots_before.state_root, state_trie_root_after: value.trie_roots_after.state_root, + checkpoint_state_trie_root: value.extra_block_data.checkpoint_state_trie_root, } } } @@ -161,15 +167,18 @@ pub struct FinalPublicValuesTarget { pub state_trie_root_before: [Target; TARGET_HASH_SIZE], /// State trie root after the execution of this global state transition. pub state_trie_root_after: [Target; TARGET_HASH_SIZE], + /// The state trie digest of the checkpoint block. + pub checkpoint_state_trie_root: [Target; TARGET_HASH_SIZE], } impl FinalPublicValuesTarget { - pub(crate) const SIZE: usize = TARGET_HASH_SIZE * 2; + pub(crate) const SIZE: usize = TARGET_HASH_SIZE * 3; /// Serializes public value targets. pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { buffer.write_target_array(&self.state_trie_root_before)?; buffer.write_target_array(&self.state_trie_root_after)?; + buffer.write_target_array(&self.checkpoint_state_trie_root)?; Ok(()) } @@ -178,10 +187,12 @@ impl FinalPublicValuesTarget { pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { let state_trie_root_before = buffer.read_target_array()?; let state_trie_root_after = buffer.read_target_array()?; + let checkpoint_state_trie_root = buffer.read_target_array()?; Ok(Self { state_trie_root_before, state_trie_root_after, + checkpoint_state_trie_root, }) } @@ -201,6 +212,10 @@ impl FinalPublicValuesTarget { self.state_trie_root_after[i], pv1.trie_roots_after.state_root[i], ); + builder.connect( + self.checkpoint_state_trie_root[i], + pv1.extra_block_data.checkpoint_state_trie_root[i], + ); } } } diff --git a/evm_arithmetization/src/recursive_verifier.rs b/evm_arithmetization/src/recursive_verifier.rs index 8f82f4bf2..90c8658fd 100644 --- a/evm_arithmetization/src/recursive_verifier.rs +++ b/evm_arithmetization/src/recursive_verifier.rs @@ -620,10 +620,12 @@ pub(crate) fn add_virtual_final_public_values_public_input< ) -> FinalPublicValuesTarget { let state_trie_root_before = builder.add_virtual_public_input_arr(); let state_trie_root_after = builder.add_virtual_public_input_arr(); + let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); FinalPublicValuesTarget { state_trie_root_before, state_trie_root_after, + checkpoint_state_trie_root, } } From 5b3c5a5550cec89b68957cc3876654d62e8d9512 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 21 Aug 2024 18:25:50 -0400 Subject: [PATCH 5/9] Clippy --- proof_gen/src/proof_types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proof_gen/src/proof_types.rs b/proof_gen/src/proof_types.rs index 1c1c51edd..ddca989da 100644 --- a/proof_gen/src/proof_types.rs +++ b/proof_gen/src/proof_types.rs @@ -2,7 +2,7 @@ //! generation process. use evm_arithmetization::{ - fixed_recursive_verifier::{extract_block_public_values, extract_two_to_one_block_hash}, + fixed_recursive_verifier::{extract_block_final_public_values, extract_two_to_one_block_hash}, proof::PublicValues, BlockHeight, }; @@ -187,7 +187,7 @@ impl AggregatableBlockProof { pub fn pv_hash(&self) -> Hash { match self { AggregatableBlockProof::Block(info) => { - let pv = extract_block_public_values(&info.intern.public_inputs); + let pv = extract_block_final_public_values(&info.intern.public_inputs); Hasher::hash_no_pad(pv) } AggregatableBlockProof::Agg(info) => { From abf65dd6c012aef96e85f4c3758771559b54db52 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 22 Aug 2024 15:31:57 -0400 Subject: [PATCH 6/9] Connect checkpoint --- .../src/fixed_recursive_verifier.rs | 4 ++++ evm_arithmetization/src/proof.rs | 17 ++++------------- evm_arithmetization/src/recursive_verifier.rs | 2 -- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 58eccb5c0..1d6cce3ab 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1491,6 +1491,10 @@ where let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); let final_pv = add_virtual_final_public_values_public_input(&mut builder); + + // This also enforces that the initial state trie root that will be stored in + // these `FinalPublicValues` actually matches the known checkpoint state trie + // root. final_pv.connect_parent(&mut builder, &parent_pv); let block_verifier_data = builder.constant_verifier_data(&block.circuit.verifier_only); diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 4b38ec239..eeb0a86db 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -122,8 +122,6 @@ pub struct FinalPublicValues { pub state_trie_root_before: H256, /// State trie root after the execution of this global state transition. pub state_trie_root_after: H256, - /// The state trie digest of the checkpoint block. - pub checkpoint_state_trie_root: H256, } impl FinalPublicValues { @@ -137,13 +135,10 @@ impl FinalPublicValues { let state_trie_root_before = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); offset += TARGET_HASH_SIZE; let state_trie_root_after = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); - offset += TARGET_HASH_SIZE; - let checkpoint_state_trie_root = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); Self { state_trie_root_before, state_trie_root_after, - checkpoint_state_trie_root, } } } @@ -153,7 +148,6 @@ impl From for FinalPublicValues { Self { state_trie_root_before: value.trie_roots_before.state_root, state_trie_root_after: value.trie_roots_after.state_root, - checkpoint_state_trie_root: value.extra_block_data.checkpoint_state_trie_root, } } } @@ -167,18 +161,15 @@ pub struct FinalPublicValuesTarget { pub state_trie_root_before: [Target; TARGET_HASH_SIZE], /// State trie root after the execution of this global state transition. pub state_trie_root_after: [Target; TARGET_HASH_SIZE], - /// The state trie digest of the checkpoint block. - pub checkpoint_state_trie_root: [Target; TARGET_HASH_SIZE], } impl FinalPublicValuesTarget { - pub(crate) const SIZE: usize = TARGET_HASH_SIZE * 3; + pub(crate) const SIZE: usize = TARGET_HASH_SIZE * 2; /// Serializes public value targets. pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { buffer.write_target_array(&self.state_trie_root_before)?; buffer.write_target_array(&self.state_trie_root_after)?; - buffer.write_target_array(&self.checkpoint_state_trie_root)?; Ok(()) } @@ -187,12 +178,10 @@ impl FinalPublicValuesTarget { pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { let state_trie_root_before = buffer.read_target_array()?; let state_trie_root_after = buffer.read_target_array()?; - let checkpoint_state_trie_root = buffer.read_target_array()?; Ok(Self { state_trie_root_before, state_trie_root_after, - checkpoint_state_trie_root, }) } @@ -212,8 +201,10 @@ impl FinalPublicValuesTarget { self.state_trie_root_after[i], pv1.trie_roots_after.state_root[i], ); + // We only use `FinalPublicValues` at the final block proof wrapping stage, + // where we should enforce consistency with the known checkpoint. builder.connect( - self.checkpoint_state_trie_root[i], + self.state_trie_root_before[i], pv1.extra_block_data.checkpoint_state_trie_root[i], ); } diff --git a/evm_arithmetization/src/recursive_verifier.rs b/evm_arithmetization/src/recursive_verifier.rs index 90c8658fd..8f82f4bf2 100644 --- a/evm_arithmetization/src/recursive_verifier.rs +++ b/evm_arithmetization/src/recursive_verifier.rs @@ -620,12 +620,10 @@ pub(crate) fn add_virtual_final_public_values_public_input< ) -> FinalPublicValuesTarget { let state_trie_root_before = builder.add_virtual_public_input_arr(); let state_trie_root_after = builder.add_virtual_public_input_arr(); - let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); FinalPublicValuesTarget { state_trie_root_before, state_trie_root_after, - checkpoint_state_trie_root, } } From c124b3e580d1eddfec621fb9b635b84b16d6a5eb Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 28 Aug 2024 17:49:13 -0400 Subject: [PATCH 7/9] Tweak deserialization ordering --- evm_arithmetization/src/fixed_recursive_verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 1d6cce3ab..6288857f9 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -476,8 +476,8 @@ where ) -> IoResult { let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; let parent_block_proof = buffer.read_target_proof_with_public_inputs()?; - let public_values = FinalPublicValuesTarget::from_buffer(buffer)?; let local_vk = buffer.read_target_verifier_circuit()?; + let public_values = FinalPublicValuesTarget::from_buffer(buffer)?; Ok(Self { circuit, From 0cf29e0c1ca63d11780a6d822ddc6b490a10f7af Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 29 Aug 2024 10:33:22 -0400 Subject: [PATCH 8/9] local_vk -> cyclic_vk --- .../src/fixed_recursive_verifier.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 6288857f9..fb03e408f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -449,7 +449,7 @@ where pub circuit: CircuitData, parent_block_proof: ProofWithPublicInputsTarget, public_values: FinalPublicValuesTarget, - local_vk: VerifierCircuitTarget, + cyclic_vk: VerifierCircuitTarget, } impl BlockWrapperCircuitData @@ -465,7 +465,7 @@ where ) -> IoResult<()> { buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; buffer.write_target_proof_with_public_inputs(&self.parent_block_proof)?; - buffer.write_target_verifier_circuit(&self.local_vk)?; + buffer.write_target_verifier_circuit(&self.cyclic_vk)?; self.public_values.to_buffer(buffer) } @@ -476,14 +476,14 @@ where ) -> IoResult { let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; let parent_block_proof = buffer.read_target_proof_with_public_inputs()?; - let local_vk = buffer.read_target_verifier_circuit()?; + let cyclic_vk = buffer.read_target_verifier_circuit()?; let public_values = FinalPublicValuesTarget::from_buffer(buffer)?; Ok(Self { circuit, parent_block_proof, public_values, - local_vk, + cyclic_vk, }) } } @@ -1502,7 +1502,7 @@ where // We want these wrapped block proofs to have the exact same structure as 2-to-1 // aggregation proofs, so we add public inputs for cyclic verification, // even though they'll be ignored. - let local_vk = builder.add_verifier_data_public_inputs(); + let cyclic_vk = builder.add_verifier_data_public_inputs(); builder.verify_proof::( &parent_block_proof, @@ -1521,7 +1521,7 @@ where circuit, parent_block_proof, public_values: final_pv, - local_vk, + cyclic_vk, } } @@ -2391,7 +2391,7 @@ where .set_proof_with_pis_target(&self.block_wrapper.parent_block_proof, block_proof); block_wrapper_inputs.set_verifier_data_target( - &self.block_wrapper.local_vk, // dummy + &self.block_wrapper.cyclic_vk, // dummy &self.block_wrapper.circuit.verifier_only, ); From ba717beec840d195ab6450827d5ac7298d5f6f2a Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 29 Aug 2024 10:59:26 -0400 Subject: [PATCH 9/9] Update comment --- evm_arithmetization/src/fixed_recursive_verifier.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index fb03e408f..8762896b4 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1511,6 +1511,8 @@ where ); // Pad to match the (non-existing yet!) 2-to-1 circuit's degree. + // We use the block circuit's degree as target reference here, as they end up + // having same degree. while log2_ceil(builder.num_gates()) < block.circuit.common.degree_bits() { builder.add_gate(NoopGate, vec![]); }