diff --git a/Cargo.toml b/Cargo.toml index 61db35cd5..0277a2534 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ rlp = "0.5.2" rlp-derive = "0.1.0" serde = "1.0.166" serde_json = "1.0.96" +serde-big-array = "0.5.1" thiserror = "1.0.49" # plonky2-related dependencies diff --git a/evm_arithmetization/Cargo.toml b/evm_arithmetization/Cargo.toml index 43e75b902..7835399a4 100644 --- a/evm_arithmetization/Cargo.toml +++ b/evm_arithmetization/Cargo.toml @@ -39,6 +39,7 @@ static_assertions = "1.1.0" hashbrown = { version = "0.14.0" } tiny-keccak = "2.0.2" serde_json = { workspace = true } +serde-big-array = { workspace = true } # Local dependencies mpt_trie = { version = "0.2.1", path = "../mpt_trie" } diff --git a/evm_arithmetization/src/cpu/kernel/interpreter.rs b/evm_arithmetization/src/cpu/kernel/interpreter.rs index fb9aa22e6..93d5e1203 100644 --- a/evm_arithmetization/src/cpu/kernel/interpreter.rs +++ b/evm_arithmetization/src/cpu/kernel/interpreter.rs @@ -9,6 +9,7 @@ use ethereum_types::{BigEndianHash, U256}; use log::Level; use mpt_trie::partial_trie::PartialTrie; use plonky2::field::types::Field; +use serde::{Deserialize, Serialize}; use crate::byte_packing::byte_packing_stark::BytePackingOp; use crate::cpu::columns::CpuColumnsView; @@ -147,7 +148,7 @@ pub(crate) fn simulate_cpu_and_get_user_jumps( } /// State data required to initialize the state passed to the prover. -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, Serialize, Deserialize)] pub(crate) struct ExtraSegmentData { pub(crate) trimmed_inputs: TrimmedGenerationInputs, pub(crate) bignum_modmul_result_limbs: Vec, diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 2ba8ffc8c..562d039c9 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -23,7 +23,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{ CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget, }; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; use plonky2::recursion::dummy_circuit::cyclic_base_proof; @@ -91,11 +91,12 @@ where pub root: RootCircuitData, /// The segment aggregation circuit, which verifies that two segment proofs /// that can either be root or aggregation proofs. - pub segment_aggregation: AggregationCircuitData, - /// The transaction aggregation circuit, which verifies a transaction proof - /// and an optional previous transaction proof. - pub txn_aggregation: BlockCircuitData, - /// The block circuit, which verifies an aggregation root proof and an + pub segment_aggregation: SegmentAggregationCircuitData, + /// The transaction aggregation circuit, which verifies the aggregation of + /// two proofs that can either be a segment aggregation representing a + /// transaction or an aggregation of transactions. + pub txn_aggregation: TxnAggregationCircuitData, + /// The block circuit, which verifies a transaction aggregation proof and an /// optional previous block proof. pub block: BlockCircuitData, /// Holds chains of circuits for each table and for each initial @@ -175,11 +176,11 @@ where } } -/// Data for the aggregation circuit, which is used to compress two proofs into -/// one. Each inner proof can be either an EVM root proof or another aggregation -/// proof. +/// Data for the segment aggregation circuit, which is used to compress two +/// segment proofs into one. Each inner proof can be either an EVM root proof or +/// another segment aggregation proof. #[derive(Eq, PartialEq, Debug)] -pub struct AggregationCircuitData +pub struct SegmentAggregationCircuitData where F: RichField + Extendable, C: GenericConfig, @@ -191,7 +192,7 @@ where cyclic_vk: VerifierCircuitTarget, } -impl AggregationCircuitData +impl SegmentAggregationCircuitData where F: RichField + Extendable, C: GenericConfig, @@ -234,29 +235,29 @@ where struct AggregationChildTarget { is_agg: BoolTarget, agg_proof: ProofWithPublicInputsTarget, - evm_proof: ProofWithPublicInputsTarget, + proof: ProofWithPublicInputsTarget, } impl AggregationChildTarget { fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { buffer.write_target_bool(self.is_agg)?; buffer.write_target_proof_with_public_inputs(&self.agg_proof)?; - buffer.write_target_proof_with_public_inputs(&self.evm_proof)?; + buffer.write_target_proof_with_public_inputs(&self.proof)?; Ok(()) } fn from_buffer(buffer: &mut Buffer) -> IoResult { let is_agg = buffer.read_target_bool()?; let agg_proof = buffer.read_target_proof_with_public_inputs()?; - let evm_proof = buffer.read_target_proof_with_public_inputs()?; + let proof = buffer.read_target_proof_with_public_inputs()?; Ok(Self { is_agg, agg_proof, - evm_proof, + proof, }) } - // `len_mem_cap` us the length of the Merkle + // `len_mem_cap` is the length of the Merkle // caps for `MemBefore` and `MemAfter`. fn public_values>( &self, @@ -265,9 +266,64 @@ impl AggregationChildTarget { ) -> PublicValuesTarget { let agg_pv = PublicValuesTarget::from_public_inputs(&self.agg_proof.public_inputs, len_mem_cap); - let evm_pv = - PublicValuesTarget::from_public_inputs(&self.evm_proof.public_inputs, len_mem_cap); - PublicValuesTarget::select(builder, self.is_agg, agg_pv, evm_pv) + let segment_pv = + PublicValuesTarget::from_public_inputs(&self.proof.public_inputs, len_mem_cap); + PublicValuesTarget::select(builder, self.is_agg, agg_pv, segment_pv) + } +} + +/// Data for the transaction aggregation circuit, which is used to compress two +/// proofs into one. Each inner proof can be either a segment aggregation proof +/// or another transaction aggregation proof. +#[derive(Eq, PartialEq, Debug)] +pub struct TxnAggregationCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub circuit: CircuitData, + lhs: AggregationChildTarget, + rhs: AggregationChildTarget, + public_values: PublicValuesTarget, + cyclic_vk: VerifierCircuitTarget, +} + +impl TxnAggregationCircuitData +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_verifier_circuit(&self.cyclic_vk)?; + self.public_values.to_buffer(buffer)?; + self.lhs.to_buffer(buffer)?; + self.rhs.to_buffer(buffer)?; + Ok(()) + } + + 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 cyclic_vk = buffer.read_target_verifier_circuit()?; + let public_values = PublicValuesTarget::from_buffer(buffer)?; + let lhs = AggregationChildTarget::from_buffer(buffer)?; + let rhs = AggregationChildTarget::from_buffer(buffer)?; + Ok(Self { + circuit, + lhs, + rhs, + public_values, + cyclic_vk, + }) } } @@ -399,13 +455,16 @@ where let mut buffer = Buffer::new(bytes); let root = RootCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let segment_aggregation = AggregationCircuitData::from_buffer( + let segment_aggregation = SegmentAggregationCircuitData::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; + let txn_aggregation = TxnAggregationCircuitData::from_buffer( &mut buffer, gate_serializer, generator_serializer, )?; - let txn_aggregation = - BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; @@ -548,8 +607,9 @@ where mem_after, ]; let root = Self::create_segment_circuit(&by_table, stark_config); - let segment_aggregation = Self::create_segmented_aggregation_circuit(&root); - let txn_aggregation = Self::create_transaction_circuit(&segment_aggregation, stark_config); + let segment_aggregation = Self::create_segment_aggregation_circuit(&root); + let txn_aggregation = + Self::create_txn_aggregation_circuit(&segment_aggregation, stark_config); let block = Self::create_block_circuit(&txn_aggregation); Self { root, @@ -587,19 +647,15 @@ where let inner_common_data: [_; NUM_TABLES] = core::array::from_fn(|i| &by_table[i].final_circuits()[0].common); - let cap_length_before = 1 + let cap_length = 1 << inner_common_data[*Table::MemBefore] .fri_params .config .cap_height; - let cap_length_after = 1 - << inner_common_data[*Table::MemAfter] - .fri_params - .config - .cap_height; + let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); - let public_values = add_virtual_public_values(&mut builder, cap_length_before); + let public_values = add_virtual_public_values(&mut builder, cap_length); let recursive_proofs = core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i])); @@ -707,11 +763,11 @@ where let merkle_before = MemCapTarget::from_public_inputs( &recursive_proofs[*Table::MemBefore].public_inputs, - cap_length_before, + cap_length, ); let merkle_after = MemCapTarget::from_public_inputs( &recursive_proofs[*Table::MemAfter].public_inputs, - cap_length_after, + cap_length, ); // Connect Memory before and after the execution with // the public values. @@ -740,9 +796,9 @@ where } } - fn create_segmented_aggregation_circuit( + fn create_segment_aggregation_circuit( root: &RootCircuitData, - ) -> AggregationCircuitData { + ) -> SegmentAggregationCircuitData { let cap_before_len = root.proof_with_pis[*Table::MemBefore] .proof .wires_cap @@ -753,92 +809,92 @@ where let public_values = add_virtual_public_values(&mut builder, cap_before_len); let cyclic_vk = builder.add_verifier_data_public_inputs(); - let parent_segment = Self::add_agg_child(&mut builder, root); - let segment = Self::add_agg_child(&mut builder, root); + let lhs_segment = Self::add_segment_agg_child(&mut builder, root); + let rhs_segment = Self::add_segment_agg_child(&mut builder, root); - let parent_pv = parent_segment.public_values(&mut builder, cap_before_len); - let segment_pv = segment.public_values(&mut builder, cap_before_len); + let lhs_pv = lhs_segment.public_values(&mut builder, cap_before_len); + let rhs_pv = rhs_segment.public_values(&mut builder, cap_before_len); // All the block metadata is the same for both segments. It is also the case for // extra_block_data. TrieRootsTarget::connect( &mut builder, public_values.trie_roots_before, - parent_pv.trie_roots_before, + lhs_pv.trie_roots_before, ); TrieRootsTarget::connect( &mut builder, public_values.trie_roots_after, - segment_pv.trie_roots_after, + rhs_pv.trie_roots_after, ); TrieRootsTarget::connect( &mut builder, public_values.trie_roots_before, - segment_pv.trie_roots_before, + rhs_pv.trie_roots_before, ); TrieRootsTarget::connect( &mut builder, public_values.trie_roots_after, - parent_pv.trie_roots_after, + lhs_pv.trie_roots_after, ); BlockMetadataTarget::connect( &mut builder, public_values.block_metadata, - segment_pv.block_metadata, + rhs_pv.block_metadata, ); BlockMetadataTarget::connect( &mut builder, public_values.block_metadata, - parent_pv.block_metadata, + lhs_pv.block_metadata, ); BlockHashesTarget::connect( &mut builder, public_values.block_hashes, - segment_pv.block_hashes, + rhs_pv.block_hashes, ); BlockHashesTarget::connect( &mut builder, public_values.block_hashes, - parent_pv.block_hashes, + lhs_pv.block_hashes, ); ExtraBlockDataTarget::connect( &mut builder, public_values.extra_block_data, - segment_pv.extra_block_data, + rhs_pv.extra_block_data, ); ExtraBlockDataTarget::connect( &mut builder, public_values.extra_block_data, - parent_pv.extra_block_data, + lhs_pv.extra_block_data, ); // Connect registers and merkle caps between segments. RegistersDataTarget::connect( &mut builder, public_values.registers_after.clone(), - segment_pv.registers_after.clone(), + rhs_pv.registers_after.clone(), ); RegistersDataTarget::connect( &mut builder, public_values.registers_before.clone(), - parent_pv.registers_before.clone(), + lhs_pv.registers_before.clone(), ); RegistersDataTarget::connect( &mut builder, - parent_pv.registers_after, - segment_pv.registers_before.clone(), + lhs_pv.registers_after, + rhs_pv.registers_before.clone(), ); MemCapTarget::connect( &mut builder, public_values.mem_before.clone(), - parent_pv.mem_before.clone(), + lhs_pv.mem_before.clone(), ); MemCapTarget::connect( &mut builder, public_values.mem_after.clone(), - segment_pv.mem_after, + rhs_pv.mem_after, ); - MemCapTarget::connect(&mut builder, parent_pv.mem_after, segment_pv.mem_before); + MemCapTarget::connect(&mut builder, lhs_pv.mem_after, rhs_pv.mem_before); // Pad to match the root circuit's degree. while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { @@ -846,126 +902,110 @@ where } let circuit = builder.build::(); - AggregationCircuitData { + SegmentAggregationCircuitData { circuit, - lhs: parent_segment, - rhs: segment, + lhs: lhs_segment, + rhs: rhs_segment, public_values, cyclic_vk, } } - // We assume that transactions are always aggregated on the right. - fn create_transaction_circuit( - agg: &AggregationCircuitData, + fn create_txn_aggregation_circuit( + agg: &SegmentAggregationCircuitData, stark_config: &StarkConfig, - ) -> BlockCircuitData { + ) -> TxnAggregationCircuitData { // Create a circuit for the aggregation of two transactions. - let expected_common_data = CommonCircuitData { - fri_params: FriParams { - degree_bits: 14, - ..agg.circuit.common.fri_params.clone() - }, - ..agg.circuit.common.clone() - }; - let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); + let cap_len = agg.public_values.mem_before.mem_cap.0.len(); - let len_before = agg.public_values.mem_before.mem_cap.0.len(); - let public_values = add_virtual_public_values(&mut builder, len_before); - let has_parent_block = builder.add_virtual_bool_target_safe(); - let parent_txn_proof = builder.add_virtual_proof_with_pis(&expected_common_data); - let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common); + let mut builder = CircuitBuilder::::new(agg.circuit.common.config.clone()); + let public_values = add_virtual_public_values(&mut builder, cap_len); + let cyclic_vk = builder.add_verifier_data_public_inputs(); - let parent_pv = - PublicValuesTarget::from_public_inputs(&parent_txn_proof.public_inputs, len_before); - let agg_pv = - PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs, len_before); + let lhs_txn_proof = Self::add_txn_agg_child(&mut builder, agg); + let rhs_txn_proof = Self::add_txn_agg_child(&mut builder, agg); + + let lhs_pv = lhs_txn_proof.public_values(&mut builder, cap_len); + let rhs_pv = rhs_txn_proof.public_values(&mut builder, cap_len); // Connect all block hash values BlockHashesTarget::connect( &mut builder, public_values.block_hashes, - agg_pv.block_hashes, + rhs_pv.block_hashes, ); BlockHashesTarget::connect( &mut builder, public_values.block_hashes, - parent_pv.block_hashes, + lhs_pv.block_hashes, ); // Connect all block metadata values. BlockMetadataTarget::connect( &mut builder, public_values.block_metadata, - agg_pv.block_metadata, + rhs_pv.block_metadata, ); BlockMetadataTarget::connect( &mut builder, public_values.block_metadata, - parent_pv.block_metadata, + lhs_pv.block_metadata, ); // Connect aggregation `trie_roots_after` with rhs `trie_roots_after`. TrieRootsTarget::connect( &mut builder, public_values.trie_roots_after, - agg_pv.trie_roots_after, + rhs_pv.trie_roots_after, ); // Connect lhs `trie_roots_after` with rhs `trie_roots_before`. TrieRootsTarget::connect( &mut builder, - parent_pv.trie_roots_after, - agg_pv.trie_roots_before, + lhs_pv.trie_roots_after, + rhs_pv.trie_roots_before, ); // Connect lhs `trie_roots_before` with public values `trie_roots_before`. TrieRootsTarget::connect( &mut builder, public_values.trie_roots_before, - parent_pv.trie_roots_before, + lhs_pv.trie_roots_before, ); Self::connect_extra_public_values( &mut builder, &public_values.extra_block_data, - &parent_pv.extra_block_data, - &agg_pv.extra_block_data, + &lhs_pv.extra_block_data, + &rhs_pv.extra_block_data, ); // We check the registers before and after for the current aggregation. RegistersDataTarget::connect( &mut builder, public_values.registers_after.clone(), - agg_pv.registers_after.clone(), + rhs_pv.registers_after.clone(), ); RegistersDataTarget::connect( &mut builder, public_values.registers_before.clone(), - agg_pv.registers_before.clone(), + lhs_pv.registers_before.clone(), ); // Check the initial and final register values. - Self::connect_initial_final_segment(&mut builder, &public_values); + Self::connect_initial_final_segment(&mut builder, &rhs_pv); + Self::connect_initial_final_segment(&mut builder, &lhs_pv); // Check the initial `MemBefore` `MerkleCap` value. - Self::check_init_merkle_cap(&mut builder, &agg_pv, stark_config); + Self::check_init_merkle_cap(&mut builder, &rhs_pv, stark_config); + Self::check_init_merkle_cap(&mut builder, &lhs_pv, stark_config); - let cyclic_vk = builder.add_verifier_data_public_inputs(); - builder - .conditionally_verify_cyclic_proof_or_dummy::( - has_parent_block, - &parent_txn_proof, - &expected_common_data, - ) - .expect("Failed to build cyclic recursion circuit"); - - let agg_verifier_data = builder.constant_verifier_data(&agg.circuit.verifier_only); - builder.verify_proof::(&agg_root_proof, &agg_verifier_data, &agg.circuit.common); + while log2_ceil(builder.num_gates()) < agg.circuit.common.degree_bits() { + builder.add_gate(NoopGate, vec![]); + } let circuit = builder.build::(); - BlockCircuitData { + TxnAggregationCircuitData { circuit, - has_parent_block, - parent_block_proof: parent_txn_proof, - agg_root_proof, + lhs: lhs_txn_proof, + rhs: rhs_txn_proof, public_values, cyclic_vk, } @@ -1014,7 +1054,7 @@ where builder.connect(x.registers_before.program_counter, main_label); } - fn create_block_circuit(agg: &BlockCircuitData) -> BlockCircuitData { + fn create_block_circuit(agg: &TxnAggregationCircuitData) -> BlockCircuitData { // Here, we have two block proofs and we aggregate them together. // The block circuit is similar to the agg circuit; both verify two inner // proofs. @@ -1027,8 +1067,8 @@ where }; let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); - let len_before = agg.public_values.mem_before.mem_cap.0.len(); - let public_values = add_virtual_public_values(&mut builder, len_before); + let mem_cap_len = agg.public_values.mem_before.mem_cap.0.len(); + let public_values = add_virtual_public_values(&mut builder, mem_cap_len); 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); @@ -1037,9 +1077,9 @@ where Self::connect_block_hashes(&mut builder, &parent_block_proof, &agg_root_proof); let parent_pv = - PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs, len_before); + PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs, mem_cap_len); let agg_pv = - PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs, len_before); + PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs, mem_cap_len); // Connect block `trie_roots_before` with parent_pv `trie_roots_before`. TrieRootsTarget::connect( @@ -1134,7 +1174,7 @@ where builder.connect(lhs.gas_used_after, rhs.gas_used_before); } - fn add_agg_child( + fn add_segment_agg_child( builder: &mut CircuitBuilder, root: &RootCircuitData, ) -> AggregationChildTarget { @@ -1142,16 +1182,40 @@ where let root_vk = builder.constant_verifier_data(&root.circuit.verifier_only); let is_agg = builder.add_virtual_bool_target_safe(); let agg_proof = builder.add_virtual_proof_with_pis(common); - let evm_proof = builder.add_virtual_proof_with_pis(common); + let proof = builder.add_virtual_proof_with_pis(common); + builder + .conditionally_verify_cyclic_proof::(is_agg, &agg_proof, &proof, &root_vk, common) + .expect("Failed to build cyclic recursion circuit"); + AggregationChildTarget { + is_agg, + agg_proof, + proof, + } + } + + fn add_txn_agg_child( + builder: &mut CircuitBuilder, + segment_agg: &SegmentAggregationCircuitData, + ) -> AggregationChildTarget { + let common = &segment_agg.circuit.common; + let inner_segment_agg_vk = + builder.constant_verifier_data(&segment_agg.circuit.verifier_only); + let is_agg = builder.add_virtual_bool_target_safe(); + let agg_proof = builder.add_virtual_proof_with_pis(common); + let proof = builder.add_virtual_proof_with_pis(common); builder .conditionally_verify_cyclic_proof::( - is_agg, &agg_proof, &evm_proof, &root_vk, common, + is_agg, + &agg_proof, + &proof, + &inner_segment_agg_vk, + common, ) .expect("Failed to build cyclic recursion circuit"); AggregationChildTarget { is_agg, agg_proof, - evm_proof, + proof, } } @@ -1311,7 +1375,6 @@ where all_stark: &AllStark, config: &StarkConfig, generation_inputs: GenerationInputs, - max_cpu_len_log: usize, segment_data: &mut GenerationSegmentData, timing: &mut TimingTree, abort_signal: Option>, @@ -1396,7 +1459,6 @@ where all_stark, config, generation_inputs.clone(), - max_cpu_len_log, &mut data, timing, abort_signal.clone(), @@ -1546,13 +1608,21 @@ where ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut agg_inputs = PartialWitness::new(); - agg_inputs.set_bool_target(self.segment_aggregation.lhs.is_agg, lhs_is_agg); - agg_inputs.set_proof_with_pis_target(&self.segment_aggregation.lhs.agg_proof, lhs_proof); - agg_inputs.set_proof_with_pis_target(&self.segment_aggregation.lhs.evm_proof, lhs_proof); + Self::set_dummy_if_necessary( + &self.segment_aggregation.lhs, + lhs_is_agg, + &self.segment_aggregation.circuit, + &mut agg_inputs, + lhs_proof, + ); - agg_inputs.set_bool_target(self.segment_aggregation.rhs.is_agg, rhs_is_agg); - agg_inputs.set_proof_with_pis_target(&self.segment_aggregation.rhs.agg_proof, rhs_proof); - agg_inputs.set_proof_with_pis_target(&self.segment_aggregation.rhs.evm_proof, rhs_proof); + Self::set_dummy_if_necessary( + &self.segment_aggregation.rhs, + rhs_is_agg, + &self.segment_aggregation.circuit, + &mut agg_inputs, + rhs_proof, + ); agg_inputs.set_verifier_data_target( &self.segment_aggregation.cyclic_vk, @@ -1618,8 +1688,8 @@ where /// one will generate a proof of /// validity for both the transaction range covered by the previous proof /// and the current transaction. - /// - `agg_segment_proof`: the final aggregation proof containing all - /// segments within the current transaction. + /// - `agg_proof`: the final aggregation proof containing all segments + /// within the current transaction. /// - `public_values`: the public values associated to the aggregation /// proof. /// @@ -1630,226 +1700,44 @@ where /// for a verifier to assert correctness of the computation. pub fn prove_transaction_aggregation( &self, - opt_parent_txn_proof: Option<&ProofWithPublicInputs>, - agg_segment_proof: &ProofWithPublicInputs, - public_values: PublicValues, + lhs_is_agg: bool, + lhs_proof: &ProofWithPublicInputs, + lhs_public_values: PublicValues, + rhs_is_agg: bool, + rhs_proof: &ProofWithPublicInputs, + rhs_public_values: PublicValues, ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut txn_inputs = PartialWitness::new(); - txn_inputs.set_bool_target( - self.txn_aggregation.has_parent_block, - opt_parent_txn_proof.is_some(), + Self::set_dummy_if_necessary( + &self.txn_aggregation.lhs, + lhs_is_agg, + &self.txn_aggregation.circuit, + &mut txn_inputs, + lhs_proof, ); - if let Some(parent_txn_proof) = opt_parent_txn_proof { - txn_inputs.set_proof_with_pis_target( - &self.txn_aggregation.parent_block_proof, - parent_txn_proof, - ); - } else { - // Initialize some public inputs for a correct connection between the first - // transaction and the current one. - let mut nonzero_pis = HashMap::new(); - - // Initialize checkpoint state trie. - let checkpoint_state_trie_keys = - TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - ..TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + 8; - for (key, &value) in checkpoint_state_trie_keys.zip_eq(&h256_limbs::( - public_values.extra_block_data.checkpoint_state_trie_root, - )) { - nonzero_pis.insert(key, value); - } - // Initialize the block metadata for a correct connection between the first - // transaction and the current one. - let block_metadata_keys = TrieRootsTarget::SIZE * 2..TrieRootsTarget::SIZE * 2 + 5; - for (key, value) in block_metadata_keys.zip_eq( - u256_limbs::(U256::from_big_endian( - &public_values.block_metadata.block_beneficiary.0, - ))[..5] - .to_vec(), - ) { - nonzero_pis.insert(key, value); - } - let block_timestamp_key = TrieRootsTarget::SIZE * 2 + 5; - nonzero_pis.insert( - block_timestamp_key, - F::from_canonical_u64(public_values.block_metadata.block_timestamp.as_u64()), - ); - let block_number_key = block_timestamp_key + 1; - nonzero_pis.insert( - block_number_key, - F::from_canonical_u64(public_values.block_metadata.block_number.as_u64()), - ); - let block_difficulty_key = block_number_key + 1; - nonzero_pis.insert( - block_difficulty_key, - F::from_canonical_u64(public_values.block_metadata.block_difficulty.as_u64()), - ); - let block_random_keys = block_difficulty_key + 1..block_difficulty_key + 9; - for (key, &value) in - block_random_keys.zip_eq(&h256_limbs(public_values.block_metadata.block_random)) - { - nonzero_pis.insert(key, value); - } - let block_gaslimit_key = block_difficulty_key + 9; - nonzero_pis.insert( - block_gaslimit_key, - F::from_canonical_u64(public_values.block_metadata.block_gaslimit.as_u64()), - ); - let block_chain_id_key = block_gaslimit_key + 1; - nonzero_pis.insert( - block_chain_id_key, - F::from_canonical_u64(public_values.block_metadata.block_chain_id.as_u64()), - ); - let block_basefee_key_low = block_chain_id_key + 1; - nonzero_pis.insert( - block_basefee_key_low, - F::from_canonical_u64(public_values.block_metadata.block_base_fee.low_u32() as u64), - ); - let block_basefee_key_hi = block_basefee_key_low + 1; - nonzero_pis.insert( - block_basefee_key_hi, - F::from_canonical_u64( - (public_values.block_metadata.block_base_fee >> 32).low_u32() as u64, - ), - ); - let block_gas_used_key = block_basefee_key_hi + 1; - nonzero_pis.insert( - block_gas_used_key, - F::from_canonical_u64(public_values.block_metadata.block_gas_used.as_u64()), - ); - let init_block_bloom = block_gas_used_key + 1; - for i in 0..8 { - let block_bloom_keys = init_block_bloom + i * 8..init_block_bloom + (i + 1) * 8; - for (key, &value) in block_bloom_keys - .zip_eq(&u256_limbs(public_values.block_metadata.block_bloom[i])) - { - nonzero_pis.insert(key, value); - } - } - - // Initialize the block hashes. They are all the same within the same block. - let block_hashes_keys = TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE - ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - - 8; - - for i in 0..public_values.block_hashes.prev_hashes.len() { - let targets = h256_limbs::(public_values.block_hashes.prev_hashes[i]); - for j in 0..8 { - nonzero_pis.insert(block_hashes_keys.start + 8 * i + j, targets[j]); - } - } - let block_hashes_current_start = - TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - 8; - let cur_targets = h256_limbs::(public_values.block_hashes.cur_hash); - for i in 0..8 { - nonzero_pis.insert(block_hashes_current_start + i, cur_targets[i]); - } - - // Initialize the first transaction roots before, and state root after. - // At the start of the block, the transaction and receipt roots are empty. - let state_trie_root_before_keys = 0..TrieRootsTarget::HASH_SIZE; - for (key, &value) in state_trie_root_before_keys - .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) - { - nonzero_pis.insert(key, value); - } - - let initial_trie = HashedPartialTrie::from(Node::Empty).hash(); - - let txn_trie_root_before_keys = - TrieRootsTarget::HASH_SIZE..TrieRootsTarget::HASH_SIZE * 2; - for (key, &value) in txn_trie_root_before_keys - .clone() - .zip_eq(&h256_limbs::(initial_trie)) - { - nonzero_pis.insert(key, value); - } - let receipts_trie_root_before_keys = - TrieRootsTarget::HASH_SIZE * 2..TrieRootsTarget::HASH_SIZE * 3; - for (key, &value) in receipts_trie_root_before_keys - .clone() - .zip_eq(&h256_limbs::(initial_trie)) - { - nonzero_pis.insert(key, value); - } - let state_trie_root_after_keys = - TrieRootsTarget::SIZE..TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE; - 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); - } - - let txn_trie_root_after_keys = TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE - ..TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE * 2; - for (key, &value) in txn_trie_root_after_keys - .clone() - .zip_eq(&h256_limbs::(initial_trie)) - { - nonzero_pis.insert(key, value); - } - let receipts_trie_root_after_keys = TrieRootsTarget::SIZE - + TrieRootsTarget::HASH_SIZE * 2 - ..TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE * 3; - for (key, &value) in receipts_trie_root_after_keys - .clone() - .zip_eq(&h256_limbs::(initial_trie)) - { - nonzero_pis.insert(key, value); - } - - txn_inputs.set_proof_with_pis_target( - &self.txn_aggregation.parent_block_proof, - &cyclic_base_proof( - &self.txn_aggregation.circuit.common, - &self.txn_aggregation.circuit.verifier_only, - nonzero_pis, - ), - ); - } - - txn_inputs - .set_proof_with_pis_target(&self.txn_aggregation.agg_root_proof, agg_segment_proof); + Self::set_dummy_if_necessary( + &self.txn_aggregation.rhs, + rhs_is_agg, + &self.txn_aggregation.circuit, + &mut txn_inputs, + rhs_proof, + ); txn_inputs.set_verifier_data_target( &self.txn_aggregation.cyclic_vk, &self.txn_aggregation.circuit.verifier_only, ); - // This is basically identical to this block public values, apart from the - // `trie_roots_before`, the `txn_number_before` and the - // `gas_used_before`, that may come from the previous proof, if any. - let txn_number_before_key = - TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE + 8; - let gas_used_before_key = - TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE + 10; let txn_public_values = PublicValues { - trie_roots_before: opt_parent_txn_proof - .map(|p| TrieRoots::from_public_inputs(&p.public_inputs[0..TrieRootsTarget::SIZE])) - .unwrap_or(public_values.trie_roots_before), + trie_roots_before: lhs_public_values.trie_roots_before, extra_block_data: ExtraBlockData { - txn_number_before: opt_parent_txn_proof - .map(|p| { - p.public_inputs[txn_number_before_key] - .to_canonical_u64() - .into() - }) - .unwrap_or(public_values.extra_block_data.txn_number_before), - gas_used_before: opt_parent_txn_proof - .map(|p| { - p.public_inputs[gas_used_before_key] - .to_canonical_u64() - .into() - }) - .unwrap_or(public_values.extra_block_data.gas_used_before), - ..public_values.extra_block_data + txn_number_before: lhs_public_values.extra_block_data.txn_number_before, + gas_used_before: lhs_public_values.extra_block_data.gas_used_before, + ..rhs_public_values.extra_block_data }, - ..public_values + ..rhs_public_values }; set_public_value_targets( @@ -1877,6 +1765,69 @@ where ) } + /// Used in the case of a non aggregation transaction child. + /// Creates dummy public inputs to set the cyclic vk to the aggregation + /// circuit values, so that both aggregation and non-aggregation parts + /// of the child share the same vk. This is possible because only the + /// aggregation inner circuit is checked against its vk. + fn set_dummy_proof_with_cyclic_vk_pis( + circuit_agg: &CircuitData, + witness: &mut PartialWitness, + agg_proof: &ProofWithPublicInputsTarget, + proof: &ProofWithPublicInputs, + ) { + let ProofWithPublicInputs { + proof, + public_inputs, + } = proof; + let ProofWithPublicInputsTarget { + proof: proof_targets, + public_inputs: pi_targets, + } = agg_proof; + + // The proof remains the same. + witness.set_proof_target(proof_targets, proof); + + let num_pis = circuit_agg.common.num_public_inputs; + let mut dummy_pis = vec![F::ZERO; num_pis]; + let cyclic_verifying_data = &circuit_agg.verifier_only; + let mut cyclic_vk = cyclic_verifying_data.circuit_digest.to_vec(); + cyclic_vk.append(&mut cyclic_verifying_data.constants_sigmas_cap.flatten()); + + let cyclic_vk_len = cyclic_vk.len(); + for i in 0..cyclic_vk_len { + dummy_pis[num_pis - cyclic_vk_len + i] = cyclic_vk[i]; + } + + // Set dummy public inputs. + for (&pi_t, pi) in pi_targets.iter().zip_eq(dummy_pis) { + witness.set_target(pi_t, pi); + } + } + + /// If the lhs is not an aggregation, we set the cyclic vk to a dummy value, + /// so that it corresponds to the aggregation cyclic vk. + fn set_dummy_if_necessary( + agg_child: &AggregationChildTarget, + is_agg: bool, + circuit: &CircuitData, + agg_inputs: &mut PartialWitness, + proof: &ProofWithPublicInputs, + ) { + agg_inputs.set_bool_target(agg_child.is_agg, is_agg); + if is_agg { + agg_inputs.set_proof_with_pis_target(&agg_child.agg_proof, proof); + } else { + Self::set_dummy_proof_with_cyclic_vk_pis( + circuit, + agg_inputs, + &agg_child.agg_proof, + proof, + ) + } + agg_inputs.set_proof_with_pis_target(&agg_child.proof, proof); + } + /// Create a final block proof, once all transactions of a given block have /// been combined into a single aggregation proof. /// diff --git a/evm_arithmetization/src/generation/mpt.rs b/evm_arithmetization/src/generation/mpt.rs index a4c902af3..c8d266e14 100644 --- a/evm_arithmetization/src/generation/mpt.rs +++ b/evm_arithmetization/src/generation/mpt.rs @@ -8,6 +8,7 @@ use mpt_trie::nibbles::{Nibbles, NibblesIntern}; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use rlp::{Decodable, DecoderError, Encodable, PayloadInfo, Rlp, RlpStream}; use rlp_derive::{RlpDecodable, RlpEncodable}; +use serde::{Deserialize, Serialize}; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::generation::TrieInputs; @@ -23,7 +24,7 @@ pub struct AccountRlp { pub code_hash: H256, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct TrieRootPtrs { pub state_root_ptr: usize, pub txn_root_ptr: usize, diff --git a/evm_arithmetization/src/memory/segments.rs b/evm_arithmetization/src/memory/segments.rs index c4f2dd597..ae6b96a24 100644 --- a/evm_arithmetization/src/memory/segments.rs +++ b/evm_arithmetization/src/memory/segments.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32; /// This contains all the existing memory segments. The values in the enum are @@ -5,7 +7,7 @@ pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32; /// segment / virtual) bundling in the kernel. #[allow(dead_code)] #[allow(clippy::enum_clike_unportable_variant)] -#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Serialize, Deserialize)] pub(crate) enum Segment { /// Contains EVM bytecode. // The Kernel has optimizations relying on the Code segment being 0. diff --git a/evm_arithmetization/src/prover.rs b/evm_arithmetization/src/prover.rs index e52ca4a1b..12be79cc9 100644 --- a/evm_arithmetization/src/prover.rs +++ b/evm_arithmetization/src/prover.rs @@ -16,6 +16,7 @@ use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, GenericHashOut}; use plonky2::timed; use plonky2::util::timing::TimingTree; +use serde::{Deserialize, Serialize}; use starky::config::StarkConfig; use starky::cross_table_lookup::{get_ctl_data, CtlData}; use starky::lookup::GrandProductChallengeSet; @@ -37,7 +38,7 @@ use crate::witness::memory::{MemoryAddress, MemoryState}; use crate::witness::state::RegistersState; /// Structure holding the data needed to initialize a segment. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct GenerationSegmentData { /// Registers at the start of the segment execution. pub(crate) registers_before: RegistersState, @@ -543,6 +544,15 @@ pub fn generate_all_data_segments( }; } + // We need at least two segments to prove a segment aggregation. + if all_seg_data.len() == 1 { + let dummy_seg = GenerationSegmentData { + registers_before: segment_data.registers_after, + ..segment_data + }; + all_seg_data.push(dummy_seg); + } + Ok(all_seg_data) } diff --git a/evm_arithmetization/src/witness/memory.rs b/evm_arithmetization/src/witness/memory.rs index 99127d1ce..d8560e27b 100644 --- a/evm_arithmetization/src/witness/memory.rs +++ b/evm_arithmetization/src/witness/memory.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use ethereum_types::U256; +use serde::{Deserialize, Serialize}; +use serde_big_array::BigArray; use crate::cpu::membus::{NUM_CHANNELS, NUM_GP_CHANNELS}; @@ -162,7 +164,7 @@ impl MemoryOp { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub(crate) struct MemoryState { pub(crate) contexts: Vec, preinitialized_segments: HashMap, @@ -302,8 +304,9 @@ impl Default for MemoryState { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub(crate) struct MemoryContextState { + #[serde(with = "BigArray")] /// The content of each memory segment. pub(crate) segments: [MemorySegmentState; Segment::COUNT], } @@ -316,7 +319,7 @@ impl Default for MemoryContextState { } } -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default, Debug, Serialize, Deserialize)] pub(crate) struct MemorySegmentState { pub(crate) content: Vec>, } diff --git a/evm_arithmetization/tests/add11_yml.rs b/evm_arithmetization/tests/add11_yml.rs index fbd1d28ec..4ed2ec3dc 100644 --- a/evm_arithmetization/tests/add11_yml.rs +++ b/evm_arithmetization/tests/add11_yml.rs @@ -4,19 +4,18 @@ use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256}; -use evm_arithmetization::fixed_recursive_verifier::ProverOutputData; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::TrieInputs; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::{generate_all_data_segments, prove}; use evm_arithmetization::verifier::verify_proof; -use evm_arithmetization::{AllRecursiveCircuits, AllStark, GenerationInputs, Node}; +use evm_arithmetization::{AllStark, GenerationInputs, Node}; use hex_literal::hex; use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::{KeccakGoldilocksConfig, PoseidonGoldilocksConfig}; +use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; use starky::config::StarkConfig; @@ -194,83 +193,6 @@ fn add11_yml() -> anyhow::Result<()> { verify_proof(&all_stark, proof, &config) } -#[test] -#[ignore] // Too slow to run on CI. -fn add11_segments_aggreg() -> anyhow::Result<()> { - init_logger(); - - type F = GoldilocksField; - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let inputs = get_generation_inputs(); - - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[ - 16..17, - 8..15, - 8..16, - 4..15, - 7..11, - 4..13, - 16..19, - 7..18, - 11..18, - ], // Minimal ranges to prove an empty list - &config, - ); - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let max_cpu_len_log = 14; - - let all_segment_proofs = &all_circuits.prove_all_segments( - &all_stark, - &config, - inputs, - max_cpu_len_log, - &mut timing, - None, - )?; - - for segment_proof in all_segment_proofs { - let ProverOutputData { - proof_with_pis: proof, - .. - } = segment_proof; - all_circuits.verify_root(proof.clone())?; - } - - assert_eq!(all_segment_proofs.len(), 3); - - let (first_aggreg_proof, first_aggreg_pv) = all_circuits.prove_segment_aggregation( - false, - &all_segment_proofs[0].proof_with_pis, - all_segment_proofs[0].public_values.clone(), - false, - &all_segment_proofs[1].proof_with_pis, - all_segment_proofs[1].public_values.clone(), - )?; - all_circuits.verify_segment_aggregation(&first_aggreg_proof)?; - - let (second_aggreg_proof, second_aggreg_pv) = all_circuits.prove_segment_aggregation( - true, - &first_aggreg_proof, - first_aggreg_pv, - false, - &all_segment_proofs[2].proof_with_pis, - all_segment_proofs[2].public_values.clone(), - )?; - all_circuits.verify_segment_aggregation(&second_aggreg_proof)?; - - let (txn_aggreg_proof, _) = - all_circuits.prove_transaction_aggregation(None, &second_aggreg_proof, second_aggreg_pv)?; - all_circuits.verify_txn_aggregation(&txn_aggreg_proof) -} - fn init_logger() { let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); } diff --git a/evm_arithmetization/tests/empty_txn_list.rs b/evm_arithmetization/tests/empty_txn_list.rs index 72f9bc3c7..303183e35 100644 --- a/evm_arithmetization/tests/empty_txn_list.rs +++ b/evm_arithmetization/tests/empty_txn_list.rs @@ -216,7 +216,10 @@ fn test_empty_txn_list() -> anyhow::Result<()> { assert_eq!(retrieved_public_values, segmented_agg_public_values); let (txn_proof, txn_public_values) = all_circuits.prove_transaction_aggregation( - None, + false, + &segmented_agg_proof, + segmented_agg_public_values.clone(), + false, &segmented_agg_proof, segmented_agg_public_values, )?; diff --git a/evm_arithmetization/tests/log_opcode.rs b/evm_arithmetization/tests/log_opcode.rs index 30c9db1b5..ce39e5fd4 100644 --- a/evm_arithmetization/tests/log_opcode.rs +++ b/evm_arithmetization/tests/log_opcode.rs @@ -11,9 +11,7 @@ use evm_arithmetization::generation::mpt::transaction_testing::{ }; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; -use evm_arithmetization::proof::{ - BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots, -}; +use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::{generate_all_data_segments, prove}; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; @@ -642,27 +640,17 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { )?; all_circuits.verify_segment_aggregation(&segment_agg_proof_second)?; - let (txn_proof_first, txn_pv_first) = all_circuits.prove_transaction_aggregation( - None, + let (txn_proof, txn_pv) = all_circuits.prove_transaction_aggregation( + false, &segment_agg_proof_first, updated_agg_public_values_first, - )?; - let txn_pvs = PublicValues { - trie_roots_before: txn_pv_first.trie_roots_before, - extra_block_data: ExtraBlockData { - txn_number_before: txn_pv_first.extra_block_data.txn_number_before, - gas_used_before: txn_pv_first.extra_block_data.txn_number_before, - ..updated_agg_public_values_second.extra_block_data - }, - ..updated_agg_public_values_second - }; - let (txn_proof_second, txn_pv_second) = all_circuits.prove_transaction_aggregation( - Some(&txn_proof_first), + false, &segment_agg_proof_second, - txn_pvs, + updated_agg_public_values_second, )?; + let (first_block_proof, _block_public_values) = - all_circuits.prove_block(None, &txn_proof_second, txn_pv_second)?; + all_circuits.prove_block(None, &txn_proof, txn_pv)?; all_circuits.verify_block(&first_block_proof)?; // Prove the next, empty block. @@ -741,7 +729,10 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { all_circuits.verify_segment_aggregation(&segment_agg_proof)?; let (second_txn_proof, second_txn_pvs) = all_circuits.prove_transaction_aggregation( - None, + false, + &segment_agg_proof, + updated_agg_public_values.clone(), + false, &segment_agg_proof, updated_agg_public_values, )?; diff --git a/proof_gen/src/proof_gen.rs b/proof_gen/src/proof_gen.rs index f1f1276dd..39075ab02 100644 --- a/proof_gen/src/proof_gen.rs +++ b/proof_gen/src/proof_gen.rs @@ -12,7 +12,10 @@ use plonky2::{ }; use crate::{ - proof_types::{AggregatableProof, GeneratedAggProof, GeneratedBlockProof, GeneratedTxnProof}, + proof_types::{ + GeneratedBlockProof, GeneratedSegmentAggProof, GeneratedSegmentProof, GeneratedTxnAggProof, + SegmentAggregatableProof, TxnAggregatableProof, + }, prover_state::ProverState, types::{Config, Field, PlonkyProofIntern, EXTENSION_DEGREE}, }; @@ -41,21 +44,18 @@ impl From for ProofGenError { } /// Generates a transaction proof from some IR data. -pub fn generate_txn_proof( +pub fn generate_segment_proof( p_state: &ProverState, gen_inputs: GenerationInputs, segment_data: &mut GenerationSegmentData, abort_signal: Option>, -) -> ProofGenResult { - // TODO: change the `max_cpu_len_log` and `segment_index` arguments once we can - // automatically determine them. +) -> ProofGenResult { let output_data = p_state .state .prove_segment( &AllStark::default(), &StarkConfig::standard_fast_config(), gen_inputs, - 32, segment_data, &mut TimingTree::default(), abort_signal, @@ -64,17 +64,17 @@ pub fn generate_txn_proof( let p_vals = output_data.public_values; let intern = output_data.proof_with_pis; - Ok(GeneratedTxnProof { p_vals, intern }) + Ok(GeneratedSegmentProof { p_vals, intern }) } /// Generates an aggregation proof from two child proofs. /// /// Note that the child proofs may be either transaction or aggregation proofs. -pub fn generate_agg_proof( +pub fn generate_segment_agg_proof( p_state: &ProverState, - lhs_child: &AggregatableProof, - rhs_child: &AggregatableProof, -) -> ProofGenResult { + lhs_child: &SegmentAggregatableProof, + rhs_child: &SegmentAggregatableProof, +) -> ProofGenResult { let (intern, p_vals) = p_state .state .prove_segment_aggregation( @@ -87,7 +87,7 @@ pub fn generate_agg_proof( ) .map_err(|err| err.to_string())?; - Ok(GeneratedAggProof { p_vals, intern }) + Ok(GeneratedSegmentAggProof { p_vals, intern }) } /// Generates a transaction aggregation proof from two child proofs. @@ -95,27 +95,23 @@ pub fn generate_agg_proof( /// Note that the child proofs may be either transaction or aggregation proofs. pub fn generate_transaction_agg_proof( p_state: &ProverState, - prev_opt_parent_b_proof: Option<&GeneratedBlockProof>, - curr_block_agg_proof: &GeneratedAggProof, -) -> ProofGenResult { - let b_height = curr_block_agg_proof - .p_vals - .block_metadata - .block_number - .low_u64(); - let parent_intern = prev_opt_parent_b_proof.map(|p| &p.intern); - - let (b_proof_intern, _) = p_state + lhs_child: &TxnAggregatableProof, + rhs_child: &TxnAggregatableProof, +) -> ProofGenResult { + let (b_proof_intern, p_vals) = p_state .state - .prove_block( - parent_intern, - &curr_block_agg_proof.intern, - curr_block_agg_proof.p_vals.clone(), + .prove_transaction_aggregation( + lhs_child.is_agg(), + lhs_child.intern(), + lhs_child.public_values(), + rhs_child.is_agg(), + rhs_child.intern(), + rhs_child.public_values(), ) .map_err(|err| err.to_string())?; - Ok(GeneratedBlockProof { - b_height, + Ok(GeneratedTxnAggProof { + p_vals, intern: b_proof_intern, }) } @@ -127,7 +123,7 @@ pub fn generate_transaction_agg_proof( pub fn generate_block_proof( p_state: &ProverState, prev_opt_parent_b_proof: Option<&GeneratedBlockProof>, - curr_block_agg_proof: &GeneratedAggProof, + curr_block_agg_proof: &GeneratedTxnAggProof, ) -> ProofGenResult { let b_height = curr_block_agg_proof .p_vals diff --git a/proof_gen/src/proof_types.rs b/proof_gen/src/proof_types.rs index 49d3b25f3..94f5ff3be 100644 --- a/proof_gen/src/proof_types.rs +++ b/proof_gen/src/proof_types.rs @@ -9,26 +9,39 @@ use crate::types::PlonkyProofIntern; /// A transaction proof along with its public values, for proper connection with /// contiguous proofs. #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GeneratedTxnProof { +pub struct GeneratedSegmentProof { /// Public values of this transaction proof. pub p_vals: PublicValues, /// Underlying plonky2 proof. pub intern: PlonkyProofIntern, } -/// An aggregation proof along with its public values, for proper connection -/// with contiguous proofs. +/// A segment aggregation proof along with its public values, for proper +/// connection with contiguous proofs. /// /// Aggregation proofs can represent any contiguous range of two or more -/// transactions, up to an entire block. +/// segments, up to an entire transaction. #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GeneratedAggProof { +pub struct GeneratedSegmentAggProof { /// Public values of this aggregation proof. pub p_vals: PublicValues, /// Underlying plonky2 proof. pub intern: PlonkyProofIntern, } +/// A transaction aggregation proof along with its public values, for proper +/// connection with contiguous proofs. +/// +/// Transaction agregation proofs can represent any contiguous range of two or +/// more transactions, up to an entire block. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GeneratedTxnAggProof { + /// Public values of this transaction aggregation proof. + pub p_vals: PublicValues, + /// Underlying plonky2 proof. + pub intern: PlonkyProofIntern, +} + /// A block proof along with the block height against which this proof ensures /// the validity since the last proof checkpoint. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -43,44 +56,101 @@ pub struct GeneratedBlockProof { /// we can combine it into an agg proof. For these cases, we want to abstract /// away whether or not the proof was a txn or agg proof. #[derive(Clone, Debug, Deserialize, Serialize)] -pub enum AggregatableProof { +pub enum SegmentAggregatableProof { + /// The underlying proof is a segment proof. + Txn(GeneratedSegmentProof), + /// The underlying proof is an aggregation proof. + Agg(GeneratedSegmentAggProof), +} + +/// Sometimes we don't care about the underlying proof type and instead only if +/// we can combine it into an agg proof. For these cases, we want to abstract +/// away whether or not the proof was a txn or agg proof. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum TxnAggregatableProof { /// The underlying proof is a transaction proof. - Txn(GeneratedTxnProof), + Txn(GeneratedSegmentAggProof), /// The underlying proof is an aggregation proof. - Agg(GeneratedAggProof), + Agg(GeneratedTxnAggProof), } -impl AggregatableProof { +impl SegmentAggregatableProof { pub(crate) fn public_values(&self) -> PublicValues { match self { - AggregatableProof::Txn(info) => info.p_vals.clone(), - AggregatableProof::Agg(info) => info.p_vals.clone(), + SegmentAggregatableProof::Txn(info) => info.p_vals.clone(), + SegmentAggregatableProof::Agg(info) => info.p_vals.clone(), } } pub(crate) const fn is_agg(&self) -> bool { match self { - AggregatableProof::Txn(_) => false, - AggregatableProof::Agg(_) => true, + SegmentAggregatableProof::Txn(_) => false, + SegmentAggregatableProof::Agg(_) => true, } } pub(crate) const fn intern(&self) -> &PlonkyProofIntern { match self { - AggregatableProof::Txn(info) => &info.intern, - AggregatableProof::Agg(info) => &info.intern, + SegmentAggregatableProof::Txn(info) => &info.intern, + SegmentAggregatableProof::Agg(info) => &info.intern, + } + } +} + +impl TxnAggregatableProof { + pub(crate) fn public_values(&self) -> PublicValues { + match self { + TxnAggregatableProof::Txn(info) => info.p_vals.clone(), + TxnAggregatableProof::Agg(info) => info.p_vals.clone(), } } + + pub(crate) fn is_agg(&self) -> bool { + match self { + TxnAggregatableProof::Txn(_) => false, + TxnAggregatableProof::Agg(_) => true, + } + } + + pub(crate) fn intern(&self) -> &PlonkyProofIntern { + match self { + TxnAggregatableProof::Txn(info) => &info.intern, + TxnAggregatableProof::Agg(info) => &info.intern, + } + } +} + +impl From for SegmentAggregatableProof { + fn from(v: GeneratedSegmentProof) -> Self { + Self::Txn(v) + } +} + +impl From for SegmentAggregatableProof { + fn from(v: GeneratedSegmentAggProof) -> Self { + Self::Agg(v) + } } -impl From for AggregatableProof { - fn from(v: GeneratedTxnProof) -> Self { +impl From for TxnAggregatableProof { + fn from(v: GeneratedSegmentAggProof) -> Self { Self::Txn(v) } } -impl From for AggregatableProof { - fn from(v: GeneratedAggProof) -> Self { +impl From for TxnAggregatableProof { + fn from(v: GeneratedTxnAggProof) -> Self { Self::Agg(v) } } + +impl From for TxnAggregatableProof { + fn from(v: SegmentAggregatableProof) -> Self { + match v { + SegmentAggregatableProof::Agg(agg) => TxnAggregatableProof::Txn(agg), + SegmentAggregatableProof::Txn(_) => { + panic!("Should be an aggregation by now. Missing segment?") + } + } + } +} diff --git a/trace_decoder/src/types.rs b/trace_decoder/src/types.rs index b1e2da5ca..9ad329495 100644 --- a/trace_decoder/src/types.rs +++ b/trace_decoder/src/types.rs @@ -2,6 +2,7 @@ use ethereum_types::{Address, H256, U256}; use evm_arithmetization::{ generation::GenerationInputs, proof::{BlockHashes, BlockMetadata}, + prover::GenerationSegmentData, }; use mpt_trie::{nibbles::Nibbles, partial_trie::HashedPartialTrie}; use serde::{Deserialize, Serialize}; @@ -37,6 +38,9 @@ pub(crate) type TriePathIter = mpt_trie::special_query::TriePathIter Vec; +/// All data needed to prove all transaction segments. +pub type AllData = (GenerationInputs, GenerationSegmentData); + // 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 pub(crate) const EMPTY_CODE_HASH: H256 = H256([ 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202,