diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index a892e924a..8762896b4 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 wrapped +/// 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, + cyclic_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.cyclic_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 cyclic_vk = buffer.read_target_verifier_circuit()?; + let public_values = FinalPublicValuesTarget::from_buffer(buffer)?; + + Ok(Self { + circuit, + parent_block_proof, + public_values, + cyclic_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,51 @@ 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); + + // 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); + + // 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 cyclic_vk = builder.add_verifier_data_public_inputs(); + + builder.verify_proof::( + &parent_block_proof, + &block_verifier_data, + &block.circuit.common, + ); + + // 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![]); + } + + let circuit = builder.build::(); + + BlockWrapperCircuitData { + circuit, + parent_block_proof, + public_values: final_pv, + cyclic_vk, + } + } + /// Create two-to-one block aggregation circuit. /// /// # Arguments @@ -1429,22 +1538,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 +1565,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 +2226,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 +2356,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 +2368,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.cyclic_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 +2840,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 34ab31d4b..eeb0a86db 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!(FinalPublicValuesTarget::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 += TARGET_HASH_SIZE; + 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,67 @@ 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, + }) + } + + /// Connects these `FinalPublicValuesTarget` with their corresponding + /// counterpart in a full parent `PublicValuesTarget`. + pub(crate) fn connect_parent, const D: usize>( + &self, + builder: &mut CircuitBuilder, + 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], + ); + // We only use `FinalPublicValues` at the final block proof wrapping stage, + // where we should enforce consistency with the known checkpoint. + builder.connect( + self.state_trie_root_before[i], + pv1.extra_block_data.checkpoint_state_trie_root[i], + ); } } } @@ -419,6 +452,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) diff --git a/evm_arithmetization/src/recursive_verifier.rs b/evm_arithmetization/src/recursive_verifier.rs index 391032307..8f82f4bf2 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,16 +612,34 @@ fn add_data_write, const D: usize>( builder.add(running_sum, inverse) } -pub(crate) fn add_virtual_public_values, const D: usize>( +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(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 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)), @@ -642,12 +661,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 +675,10 @@ 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_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 +694,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 +712,25 @@ 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_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_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 +738,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 +748,10 @@ 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_registers_data_public_input< + F: RichField + Extendable, + const D: usize, +>( builder: &mut CircuitBuilder, ) -> RegistersDataTarget { let program_counter = builder.add_virtual_public_input(); @@ -724,6 +760,7 @@ 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, @@ -807,6 +844,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(); 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) => {