From 233743bb28807354d51a81a1be94e6cf79132b99 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 25 Apr 2024 18:42:34 +0200 Subject: [PATCH 01/59] Two to one aggregation --- .../src/fixed_recursive_verifier.rs | 148 +++++++++++ evm_arithmetization/src/proof.rs | 2 +- evm_arithmetization/tests/log_opcode.rs | 2 +- evm_arithmetization/tests/two_to_one_agg.rs | 237 ++++++++++++++++++ 4 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 evm_arithmetization/tests/two_to_one_agg.rs diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index ed62014f5..31187c063 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -72,6 +72,7 @@ where /// The aggregation circuit, which verifies two proofs that can either be /// root or aggregation proofs. pub aggregation: AggregationCircuitData, + pub two_to_one_aggregation: TwoToOneAggCircuitData, /// The block circuit, which verifies an aggregation root proof and an /// optional previous block proof. pub block: BlockCircuitData, @@ -301,6 +302,60 @@ where } } +/// Data for the block circuit, which is used to generate a final block proof, +/// and compress it with an optional parent proof if present. +#[derive(Eq, PartialEq, Debug)] +pub struct TwoToOneAggCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub circuit: CircuitData, + agg_proof0: ProofWithPublicInputsTarget, + agg_proof1: ProofWithPublicInputsTarget, + pv0: PublicValuesTarget, + pv1: PublicValuesTarget, +} + +impl TwoToOneAggCircuitData +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.agg_proof0)?; + buffer.write_target_proof_with_public_inputs(&self.agg_proof1)?; + self.pv0.to_buffer(buffer)?; + self.pv1.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 agg_proof0 = buffer.read_target_proof_with_public_inputs()?; + let agg_proof1 = buffer.read_target_proof_with_public_inputs()?; + let pv0 = PublicValuesTarget::from_buffer(buffer)?; + let pv1 = PublicValuesTarget::from_buffer(buffer)?; + Ok(Self { + circuit, + agg_proof0, + agg_proof1, + pv0, + pv1, + }) + } +} + impl AllRecursiveCircuits where F: RichField + Extendable, @@ -368,6 +423,11 @@ where gate_serializer, generator_serializer, )?; + let two_to_one_aggregation = TwoToOneAggCircuitData::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; @@ -404,6 +464,7 @@ where Ok(Self { root, aggregation, + two_to_one_aggregation, block, by_table, }) @@ -493,10 +554,12 @@ where ]; let root = Self::create_root_circuit(&by_table, stark_config); let aggregation = Self::create_aggregation_circuit(&root); + let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); Self { root, aggregation, + two_to_one_aggregation, block, by_table, } @@ -1271,6 +1334,68 @@ where ) } + /// Create an aggregation proof, combining two contiguous proofs into a + /// single one. The combined proofs can either be transaction (aka root) + /// proofs, or other aggregation proofs, as long as their states are + /// contiguous, meaning that the final state of the left child proof is the + /// initial state of the right child proof. + /// + /// While regular transaction proofs can only assert validity of a single + /// transaction, aggregation proofs can cover an arbitrary range, up to + /// an entire block with all its transactions. + /// + /// # Arguments + /// + /// - `lhs_is_agg`: a boolean indicating whether the left child proof is an + /// aggregation proof or + /// a regular transaction proof. + /// - `lhs_proof`: the left child proof. + /// - `lhs_public_values`: the public values associated to the right child + /// proof. + /// - `rhs_is_agg`: a boolean indicating whether the right child proof is an + /// aggregation proof or + /// a regular transaction proof. + /// - `rhs_proof`: the right child proof. + /// - `rhs_public_values`: the public values associated to the right child + /// proof. + /// + /// # Outputs + /// + /// This method outputs a tuple of [`ProofWithPublicInputs`] and + /// its [`PublicValues`]. Only the proof with public inputs is necessary + /// for a verifier to assert correctness of the computation, + /// but the public values are output for the prover convenience, as these + /// are necessary during proof aggregation. + pub fn prove_two_to_one_aggregation( + &self, + proof0: &ProofWithPublicInputs, + proof1: &ProofWithPublicInputs, + pv0: PublicValues, + pv1: PublicValues, + ) -> anyhow::Result> { + let mut inputs = PartialWitness::new(); + + inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.agg_proof0, proof0); + inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.agg_proof1, proof1); + + set_public_value_targets(&mut inputs, &self.two_to_one_aggregation.pv0, &pv0).map_err( + |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), + )?; + set_public_value_targets(&mut inputs, &self.two_to_one_aggregation.pv1, &pv1).map_err( + |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), + )?; + + let proof = self.two_to_one_aggregation.circuit.prove(inputs)?; + Ok(proof) + } + + pub fn verify_two_to_one_aggregation( + &self, + proof: &ProofWithPublicInputs, + ) -> anyhow::Result<()> { + self.two_to_one_aggregation.circuit.verify(proof.clone()) + } + /// Create a final block proof, once all transactions of a given block have /// been combined into a single aggregation proof. /// @@ -1440,6 +1565,29 @@ where &self.block.circuit.common, ) } + + fn create_two_to_one_agg_circuit( + agg: &AggregationCircuitData, + ) -> TwoToOneAggCircuitData { + let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); + let pv0 = add_virtual_public_values(&mut builder); + let pv1 = add_virtual_public_values(&mut builder); + let agg_proof0 = builder.add_virtual_proof_with_pis(&agg.circuit.common); + let agg_proof1 = builder.add_virtual_proof_with_pis(&agg.circuit.common); + let agg_verifier_data = builder.constant_verifier_data(&agg.circuit.verifier_only); + builder.verify_proof::(&agg_proof0, &agg_verifier_data, &agg.circuit.common); + builder.verify_proof::(&agg_proof1, &agg_verifier_data, &agg.circuit.common); + + let circuit = builder.build::(); + + TwoToOneAggCircuitData { + circuit, + agg_proof0, + agg_proof1, + pv0, + pv1, + } + } } /// A map between initial degree sizes and their associated shrinking recursion diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 338eda2fa..27ee09141 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -97,7 +97,7 @@ impl PublicValues { } /// Trie hashes. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct TrieRoots { /// State trie hash. pub state_root: H256, diff --git a/evm_arithmetization/tests/log_opcode.rs b/evm_arithmetization/tests/log_opcode.rs index 75cdd44f5..31ec4c834 100644 --- a/evm_arithmetization/tests/log_opcode.rs +++ b/evm_arithmetization/tests/log_opcode.rs @@ -562,7 +562,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { signed_txn: Some(txn_2.to_vec()), withdrawals: vec![], tries: tries_before, - trie_roots_after: trie_roots_after.clone(), + trie_roots_after, contract_code, checkpoint_state_trie_root, block_metadata: block_1_metadata, diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs new file mode 100644 index 000000000..e9229890d --- /dev/null +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -0,0 +1,237 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use ethereum_types::{Address, BigEndianHash, H256, U256}; +use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; +use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; +use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; +use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; +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::PoseidonGoldilocksConfig; +use plonky2::util::timing::TimingTree; + +type F = GoldilocksField; +const D: usize = 2; +type C = PoseidonGoldilocksConfig; + +/// Test a simple token transfer to a new address. +fn simple_transfer(timestamp: u64) -> anyhow::Result { + init_logger(); + + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); + let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); + + let sender_state_key = keccak(sender); + let to_state_key = keccak(to); + + let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); + let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); + + let sender_account_before = AccountRlp { + nonce: 5.into(), + balance: eth_to_wei(100_000.into()), + storage_root: HashedPartialTrie::from(Node::Empty).hash(), + code_hash: keccak([]), + }; + let to_account_before = AccountRlp::default(); + + let state_trie_before = Node::Leaf { + nibbles: sender_nibbles, + value: rlp::encode(&sender_account_before).to_vec(), + } + .into(); + + let tries_before = TrieInputs { + state_trie: state_trie_before, + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + storage_tries: vec![], + }; + + // Generated using a little py-evm script. + let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); + let value = U256::from(100u32); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: timestamp.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: 21032.into(), + block_bloom: [0.into(); 8], + }; + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + + let expected_state_trie_after: HashedPartialTrie = { + let txdata_gas = 2 * 16; + let gas_used = 21_000 + txdata_gas; + + let sender_account_after = AccountRlp { + balance: sender_account_before.balance - value - gas_used * 10, + nonce: sender_account_before.nonce + 1, + ..sender_account_before + }; + let to_account_after = AccountRlp { + balance: value, + ..to_account_before + }; + + let mut children = core::array::from_fn(|_| Node::Empty.into()); + children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { + nibbles: sender_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&sender_account_after).to_vec(), + } + .into(); + children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { + nibbles: to_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&to_account_after).to_vec(), + } + .into(); + Node::Branch { + children, + value: vec![], + } + .into() + }; + + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: 21032.into(), + bloom: vec![0; 256].into(), + logs: vec![], + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert( + Nibbles::from_str("0x80").unwrap(), + rlp::encode(&receipt_0).to_vec(), + )?; + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + let inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code, + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 21032.into(), + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + Ok(inputs) +} + +fn dummy_inputs(inputs: &GenerationInputs) -> GenerationInputs { + GenerationInputs { + txn_number_before: inputs.txn_number_before+1, + gas_used_before: inputs.gas_used_after, + gas_used_after: inputs.gas_used_after, + signed_txn: None, + withdrawals: vec![], + tries: TrieInputs { + state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), + transactions_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.transactions_root)), + receipts_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.receipts_root)), + storage_tries: vec![], + }, + trie_roots_after: inputs.trie_roots_after, + checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, + contract_code: Default::default(), + block_metadata: inputs.block_metadata.clone(), + block_hashes: inputs.block_hashes.clone(), + } +} + +#[test] +fn test_two_to_one_aggregation() -> anyhow::Result<()> { + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let inputs0 = simple_transfer(1)?; + let dummy0 = dummy_inputs(&inputs0); + let inputs1 = simple_transfer(2)?; + let dummy1 = dummy_inputs(&inputs1); + + // Preprocess all circuits. + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[8..17, 8..15, 8..18, 8..15, 8..10, 8..13, 8..20], + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (root_proof0, pv0) = + all_circuits.prove_root(&all_stark, &config, inputs0, &mut timing, None)?; + all_circuits.verify_root(root_proof0.clone())?; + let (dummy_proof0, dummy_pv0) = + all_circuits.prove_root(&all_stark, &config, dummy0, &mut timing, None)?; + all_circuits.verify_root(dummy_proof0.clone())?; + let (root_proof1, pv1) = + all_circuits.prove_root(&all_stark, &config, inputs1, &mut timing, None)?; + all_circuits.verify_root(root_proof1.clone())?; + let (dummy_proof1, dummy_pv1) = + all_circuits.prove_root(&all_stark, &config, dummy1, &mut timing, None)?; + all_circuits.verify_root(dummy_proof1.clone())?; + + + let (agg_proof0, pv0) = all_circuits.prove_aggregation( + false, + &root_proof0, + pv0, + false, + &dummy_proof0, + dummy_pv0, + )?; + let (agg_proof1, pv1) = all_circuits.prove_aggregation( + false, + &root_proof1, + pv1, + false, + &dummy_proof1, + dummy_pv1, + )?; + + let proof = all_circuits.prove_two_to_one_aggregation( + &agg_proof0, + &agg_proof1, + pv0, + pv1, + )?; + all_circuits.verify_two_to_one_aggregation(&proof) +} + +fn eth_to_wei(eth: U256) -> U256 { + // 1 ether = 10^18 wei. + eth * U256::from(10).pow(18.into()) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +} From 18f4380607fa40413f8087c7cf12ba1f1b3de9cc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 25 Apr 2024 18:46:42 +0200 Subject: [PATCH 02/59] Comments --- .../src/fixed_recursive_verifier.rs | 38 +++++-------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 31187c063..93550ebc5 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -72,6 +72,7 @@ where /// The aggregation circuit, which verifies two proofs that can either be /// root or aggregation proofs. pub aggregation: AggregationCircuitData, + /// The two-to-one aggregation circuit, which verifies two unrelated aggregation proofs. pub two_to_one_aggregation: TwoToOneAggCircuitData, /// The block circuit, which verifies an aggregation root proof and an /// optional previous block proof. @@ -302,8 +303,8 @@ where } } -/// Data for the block circuit, which is used to generate a final block proof, -/// and compress it with an optional parent proof if present. +/// Data for the two-to-one aggregation circuit, which is used to generate a proof of two +/// unrelated aggregation proofs. #[derive(Eq, PartialEq, Debug)] pub struct TwoToOneAggCircuitData where @@ -1334,38 +1335,19 @@ where ) } - /// Create an aggregation proof, combining two contiguous proofs into a - /// single one. The combined proofs can either be transaction (aka root) - /// proofs, or other aggregation proofs, as long as their states are - /// contiguous, meaning that the final state of the left child proof is the - /// initial state of the right child proof. - /// - /// While regular transaction proofs can only assert validity of a single - /// transaction, aggregation proofs can cover an arbitrary range, up to - /// an entire block with all its transactions. + /// Create a two-to-one aggregation proof, combining two unrelated aggregation proofs + /// into a single one. /// /// # Arguments /// - /// - `lhs_is_agg`: a boolean indicating whether the left child proof is an - /// aggregation proof or - /// a regular transaction proof. - /// - `lhs_proof`: the left child proof. - /// - `lhs_public_values`: the public values associated to the right child - /// proof. - /// - `rhs_is_agg`: a boolean indicating whether the right child proof is an - /// aggregation proof or - /// a regular transaction proof. - /// - `rhs_proof`: the right child proof. - /// - `rhs_public_values`: the public values associated to the right child - /// proof. + /// - `proof0`: the first aggregation proof. + /// - `proof1`: the second aggregation proof. + /// - `pv0`: the public values associated to first proof. + /// - `pv1`: the public values associated to second proof. /// /// # Outputs /// - /// This method outputs a tuple of [`ProofWithPublicInputs`] and - /// its [`PublicValues`]. Only the proof with public inputs is necessary - /// for a verifier to assert correctness of the computation, - /// but the public values are output for the prover convenience, as these - /// are necessary during proof aggregation. + /// This method outputs a [`ProofWithPublicInputs`]. pub fn prove_two_to_one_aggregation( &self, proof0: &ProofWithPublicInputs, From 3eacaab8646d35a34c7dff797e14fefff7b410b8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 2 May 2024 17:13:03 +0200 Subject: [PATCH 03/59] Two-to-one block aggregation proof --- .../src/fixed_recursive_verifier.rs | 96 +++++++++++++++++-- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 93550ebc5..4d5ca9f0b 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -77,6 +77,8 @@ where /// The block circuit, which verifies an aggregation root proof and an /// optional previous block proof. pub block: BlockCircuitData, + /// The two-to-one block aggregation circuit, which verifies two unrelated block proofs. + pub two_to_one_block: TwoToOneAggCircuitData, /// Holds chains of circuits for each table and for each initial /// `degree_bits`. pub by_table: [RecursiveCircuitsForTable; NUM_TABLES], @@ -312,8 +314,8 @@ where C: GenericConfig, { pub circuit: CircuitData, - agg_proof0: ProofWithPublicInputsTarget, - agg_proof1: ProofWithPublicInputsTarget, + proof0: ProofWithPublicInputsTarget, + proof1: ProofWithPublicInputsTarget, pv0: PublicValuesTarget, pv1: PublicValuesTarget, } @@ -330,8 +332,8 @@ where generator_serializer: &dyn WitnessGeneratorSerializer, ) -> IoResult<()> { buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; - buffer.write_target_proof_with_public_inputs(&self.agg_proof0)?; - buffer.write_target_proof_with_public_inputs(&self.agg_proof1)?; + buffer.write_target_proof_with_public_inputs(&self.proof0)?; + buffer.write_target_proof_with_public_inputs(&self.proof1)?; self.pv0.to_buffer(buffer)?; self.pv1.to_buffer(buffer)?; Ok(()) @@ -349,8 +351,8 @@ where let pv1 = PublicValuesTarget::from_buffer(buffer)?; Ok(Self { circuit, - agg_proof0, - agg_proof1, + proof0: agg_proof0, + proof1: agg_proof1, pv0, pv1, }) @@ -431,6 +433,11 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; + let two_to_one_block = TwoToOneAggCircuitData::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; let by_table = match skip_tables { true => (0..NUM_TABLES) @@ -467,6 +474,7 @@ where aggregation, two_to_one_aggregation, block, + two_to_one_block, by_table, }) } @@ -557,11 +565,13 @@ where let aggregation = Self::create_aggregation_circuit(&root); let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); + let two_to_one_block = Self::create_two_to_one_block_circuit(&block); Self { root, aggregation, two_to_one_aggregation, block, + two_to_one_block, by_table, } } @@ -1357,8 +1367,8 @@ where ) -> anyhow::Result> { let mut inputs = PartialWitness::new(); - inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.agg_proof0, proof0); - inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.agg_proof1, proof1); + inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.proof0, proof0); + inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.proof1, proof1); set_public_value_targets(&mut inputs, &self.two_to_one_aggregation.pv0, &pv0).map_err( |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), @@ -1548,6 +1558,49 @@ where ) } + /// Create a two-to-one block aggregation proof, combining two unrelated block proofs + /// into a single one. + /// + /// # Arguments + /// + /// - `proof0`: the first block proof. + /// - `proof1`: the second block proof. + /// - `pv0`: the public values associated to first proof. + /// - `pv1`: the public values associated to second proof. + /// + /// # Outputs + /// + /// This method outputs a [`ProofWithPublicInputs`]. + pub fn prove_two_to_one_block( + &self, + proof0: &ProofWithPublicInputs, + proof1: &ProofWithPublicInputs, + pv0: PublicValues, + pv1: PublicValues, + ) -> anyhow::Result> { + let mut inputs = PartialWitness::new(); + + inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); + inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); + + set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err( + |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), + )?; + set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err( + |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), + )?; + + let proof = self.two_to_one_block.circuit.prove(inputs)?; + Ok(proof) + } + + pub fn verify_two_to_one_block( + &self, + proof: &ProofWithPublicInputs, + ) -> anyhow::Result<()> { + self.two_to_one_block.circuit.verify(proof.clone()) + } + fn create_two_to_one_agg_circuit( agg: &AggregationCircuitData, ) -> TwoToOneAggCircuitData { @@ -1564,8 +1617,31 @@ where TwoToOneAggCircuitData { circuit, - agg_proof0, - agg_proof1, + proof0: agg_proof0, + proof1: agg_proof1, + pv0, + pv1, + } + } + + fn create_two_to_one_block_circuit( + block: &BlockCircuitData, + ) -> TwoToOneAggCircuitData { + let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); + let pv0 = add_virtual_public_values(&mut builder); + let pv1 = add_virtual_public_values(&mut builder); + let block_proof0 = builder.add_virtual_proof_with_pis(&block.circuit.common); + let block_proof1 = builder.add_virtual_proof_with_pis(&block.circuit.common); + let block_verifier_data = builder.constant_verifier_data(&block.circuit.verifier_only); + builder.verify_proof::(&block_proof0, &block_verifier_data, &block.circuit.common); + builder.verify_proof::(&block_proof1, &block_verifier_data, &block.circuit.common); + + let circuit = builder.build::(); + + TwoToOneAggCircuitData { + circuit, + proof0: block_proof0, + proof1: block_proof1, pv0, pv1, } From 65707ed7208199d2fd82d0929a7dc28cbbc4936e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 3 May 2024 10:47:14 +0200 Subject: [PATCH 04/59] Minor --- .../src/fixed_recursive_verifier.rs | 30 ++++++++++--------- evm_arithmetization/tests/two_to_one_agg.rs | 29 +++++++++--------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 4d5ca9f0b..effe9708f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -72,12 +72,14 @@ where /// The aggregation circuit, which verifies two proofs that can either be /// root or aggregation proofs. pub aggregation: AggregationCircuitData, - /// The two-to-one aggregation circuit, which verifies two unrelated aggregation proofs. + /// The two-to-one aggregation circuit, which verifies two unrelated + /// aggregation proofs. pub two_to_one_aggregation: TwoToOneAggCircuitData, /// The block circuit, which verifies an aggregation root proof and an /// optional previous block proof. pub block: BlockCircuitData, - /// The two-to-one block aggregation circuit, which verifies two unrelated block proofs. + /// The two-to-one block aggregation circuit, which verifies two unrelated + /// block proofs. pub two_to_one_block: TwoToOneAggCircuitData, /// Holds chains of circuits for each table and for each initial /// `degree_bits`. @@ -305,8 +307,8 @@ where } } -/// Data for the two-to-one aggregation circuit, which is used to generate a proof of two -/// unrelated aggregation proofs. +/// Data for the two-to-one aggregation circuit, which is used to generate a +/// proof of two unrelated aggregation proofs. #[derive(Eq, PartialEq, Debug)] pub struct TwoToOneAggCircuitData where @@ -1345,8 +1347,8 @@ where ) } - /// Create a two-to-one aggregation proof, combining two unrelated aggregation proofs - /// into a single one. + /// Create a two-to-one aggregation proof, combining two unrelated + /// aggregation proofs into a single one. /// /// # Arguments /// @@ -1558,8 +1560,8 @@ where ) } - /// Create a two-to-one block aggregation proof, combining two unrelated block proofs - /// into a single one. + /// Create a two-to-one block aggregation proof, combining two unrelated + /// block proofs into a single one. /// /// # Arguments /// @@ -1583,12 +1585,12 @@ where inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); - set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err( - |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), - )?; - set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err( - |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), - )?; + set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; + set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; let proof = self.two_to_one_block.circuit.prove(inputs)?; Ok(proof) diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index e9229890d..418fdfb5f 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -45,7 +45,7 @@ fn simple_transfer(timestamp: u64) -> anyhow::Result { nibbles: sender_nibbles, value: rlp::encode(&sender_account_before).to_vec(), } - .into(); + .into(); let tries_before = TrieInputs { state_trie: state_trie_before, @@ -93,17 +93,17 @@ fn simple_transfer(timestamp: u64) -> anyhow::Result { nibbles: sender_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&sender_account_after).to_vec(), } - .into(); + .into(); children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { nibbles: to_nibbles.truncate_n_nibbles_front(1), value: rlp::encode(&to_account_after).to_vec(), } - .into(); + .into(); Node::Branch { children, value: vec![], } - .into() + .into() }; let receipt_0 = LegacyReceiptRlp { @@ -121,7 +121,7 @@ fn simple_transfer(timestamp: u64) -> anyhow::Result { nibbles: Nibbles::from_str("0x80").unwrap(), value: txn.to_vec(), } - .into(); + .into(); let trie_roots_after = TrieRoots { state_root: expected_state_trie_after.hash(), @@ -150,15 +150,19 @@ fn simple_transfer(timestamp: u64) -> anyhow::Result { fn dummy_inputs(inputs: &GenerationInputs) -> GenerationInputs { GenerationInputs { - txn_number_before: inputs.txn_number_before+1, + txn_number_before: inputs.txn_number_before + 1, gas_used_before: inputs.gas_used_after, gas_used_after: inputs.gas_used_after, signed_txn: None, withdrawals: vec![], tries: TrieInputs { state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), - transactions_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.transactions_root)), - receipts_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.receipts_root)), + transactions_trie: HashedPartialTrie::from(Node::Hash( + inputs.trie_roots_after.transactions_root, + )), + receipts_trie: HashedPartialTrie::from(Node::Hash( + inputs.trie_roots_after.receipts_root, + )), storage_tries: vec![], }, trie_roots_after: inputs.trie_roots_after, @@ -170,6 +174,7 @@ fn dummy_inputs(inputs: &GenerationInputs) -> GenerationInputs { } #[test] +#[ignore] fn test_two_to_one_aggregation() -> anyhow::Result<()> { let all_stark = AllStark::::default(); let config = StarkConfig::standard_fast_config(); @@ -200,7 +205,6 @@ fn test_two_to_one_aggregation() -> anyhow::Result<()> { all_circuits.prove_root(&all_stark, &config, dummy1, &mut timing, None)?; all_circuits.verify_root(dummy_proof1.clone())?; - let (agg_proof0, pv0) = all_circuits.prove_aggregation( false, &root_proof0, @@ -218,12 +222,7 @@ fn test_two_to_one_aggregation() -> anyhow::Result<()> { dummy_pv1, )?; - let proof = all_circuits.prove_two_to_one_aggregation( - &agg_proof0, - &agg_proof1, - pv0, - pv1, - )?; + let proof = all_circuits.prove_two_to_one_aggregation(&agg_proof0, &agg_proof1, pv0, pv1)?; all_circuits.verify_two_to_one_aggregation(&proof) } From e09a06f0490dcf2a9aa178089db642def1d5a8a0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 3 May 2024 10:56:09 +0200 Subject: [PATCH 05/59] Minor --- evm_arithmetization/src/fixed_recursive_verifier.rs | 2 +- evm_arithmetization/tests/two_to_one_agg.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index effe9708f..1cab465da 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -308,7 +308,7 @@ where } /// Data for the two-to-one aggregation circuit, which is used to generate a -/// proof of two unrelated aggregation proofs. +/// proof of two unrelated proofs. #[derive(Eq, PartialEq, Debug)] pub struct TwoToOneAggCircuitData where diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 418fdfb5f..7bc7b0ed4 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -19,7 +19,8 @@ type F = GoldilocksField; const D: usize = 2; type C = PoseidonGoldilocksConfig; -/// Test a simple token transfer to a new address. +/// Get `GenerationInputs` for a simple token transfer txn, where the block has +/// the given timestamp. fn simple_transfer(timestamp: u64) -> anyhow::Result { init_logger(); From 96d742c84e4014ee45574ba7ed9fdcbb8c2a6be0 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 7 May 2024 10:14:36 +0200 Subject: [PATCH 06/59] feat: Implement two-to-one Block Aggregation WIP: compute hashes directly from witness and check against public inputs WIP: add TwoToOneBlockAggCircuitData WIP: add test WIP: rewrite to use hasher circuitry WIP: test: can generate proofs of unrelated blocks WIP: test/refactor: generate multiple blocks WIP: test/refactor: autoformat WIP: refactor: use result iterator WIP: convert PIS WIP: feat: witness: set public input hashes WIP: feat: cache proofs in /tmp WIP: config: default to no cache WIP: bug: cache write-read assertion fails WIP: bug: prepare for more eyeballs WIP: bug: work on to_public_inputs WIP feat: private public inputs WIP feat: set pv targets WIP experiment: public input WIP refactor: clean up WIP feat: 1-level aggregation working WIP forgot: private public inputs WIP: use agg child structure WIP: split into IVC and binop WIP: split part2 into IVC and binop WIP: split part3 into IVC and binop WIP: ivc structure done WIP: wip wip WIP: ivc+binop WIP: after talking to Linda WIP: adjust num_public_inputs WIP: VirtualTarget index: 5 was set twice feat: assert on input values length experiment: minimize failing circuit feat: add selector for public values WIP: bug: add methods from branch `no_dummy_segment_no_pis` WIP: bug: first draft feat: verify 4-block aggregation test: add more tests --- .cargo/config.toml | 15 + Cargo.toml | 23 + .../src/fixed_recursive_verifier.rs | 746 +++++++++++++++++- evm_arithmetization/src/lib.rs | 1 + .../src/private_public_values.rs | 89 +++ evm_arithmetization/src/proof.rs | 47 ++ evm_arithmetization/tests/two_to_one_agg.rs | 545 ++++++++++++- 7 files changed, 1429 insertions(+), 37 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 evm_arithmetization/src/private_public_values.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..03b0dd41d --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,15 @@ +[build] +rustflags = ["-C", "target-cpu=native"] + +[env] +RUST_BACKTRACE = "1" +RUST_TEST_NOCAPTURE = "1" + +[term] +verbose = true +color = 'auto' + +[target.x86_64-unknown-linux-gnu] +linker = "clang" +#rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-fuse-ld=/usr/bin/mold", "-Z", "threads=2", "-C", "debuginfo=2"] +rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-fuse-ld=/usr/bin/mold", "-C", "debuginfo=2"] diff --git a/Cargo.toml b/Cargo.toml index 30ee73a25..aca640097 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,3 +108,26 @@ plonky2 = "0.2.2" plonky2_maybe_rayon = "0.2.0" plonky2_util = "0.2.0" starky = "0.4.0" + +[profile.release] +debug=true +incremental=true +debug-assertions=true +lto=false +overflow-checks=false + +[profile.test] +opt-level=3 +debug=true +incremental=true +debug-assertions=true +lto=false +overflow-checks=false + +[profile.dev] +opt-level=3 +debug=true +incremental=true +debug-assertions=true +lto=false +overflow-checks=false diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 1cab465da..db6703ae5 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -12,7 +12,7 @@ use plonky2::field::extension::Extendable; use plonky2::fri::FriParams; use plonky2::gates::constant::ConstantGate; use plonky2::gates::noop::NoopGate; -use plonky2::hash::hash_types::RichField; +use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField}; use plonky2::iop::challenger::RecursiveChallenger; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; @@ -20,7 +20,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, Hasher}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; use plonky2::recursion::dummy_circuit::cyclic_base_proof; @@ -38,6 +38,7 @@ use starky::stark::Stark; use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES}; use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; +use crate::private_public_values; use crate::proof::{ AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, TrieRoots, TrieRootsTarget, @@ -80,7 +81,8 @@ where pub block: BlockCircuitData, /// The two-to-one block aggregation circuit, which verifies two unrelated /// block proofs. - pub two_to_one_block: TwoToOneAggCircuitData, + //pub two_to_one_block_ivc: TwoToOneBlockIVCAggCircuitData, + pub two_to_one_block_binop: TwoToOneBlockBinopAggCircuitData, /// Holds chains of circuits for each table and for each initial /// `degree_bits`. pub by_table: [RecursiveCircuitsForTable; NUM_TABLES], @@ -361,6 +363,220 @@ where } } + + +/// Data for the two-to-one block circuit, which is used to generate a +/// proof of two unrelated proofs. +#[derive(Eq, PartialEq, Debug)] +pub struct TwoToOneBlockIVCAggCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub circuit: CircuitData, + ///// new structure + prev_proof: ProofWithPublicInputsTarget, + curr_proof: ProofWithPublicInputsTarget, + has_prev: BoolTarget, + curr_pv: PublicValuesTarget, + prev_pv_hash: HashOutTarget, + curr_pv_hash: HashOutTarget, + mix_pv_hash: HashOutTarget, + // TODO: do I need this? + cyclic_vk: VerifierCircuitTarget, +} + +// impl TwoToOneBlockIVCAggCircuitData +// 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)?; +// self.prev.to_buffer(buffer); +// self.curr.to_buffer(buffer); +// buffer.write_target_hash(&self.agg_pv_hash)?; +// buffer.write_target_verifier_circuit(&self.cyclic_vk)?; +// 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 lhs = BlockIVCAggChildTarget::from_buffer(buffer)?; +// let rhs = BlockIVCAggChildTarget::from_buffer(buffer)?; +// let mix_pv_hash = buffer.read_target_hash()?; +// let cyclic_vk = buffer.read_target_verifier_circuit()?; +// Ok(Self { +// circuit, +// prev: lhs, +// curr: rhs, +// mix_pv_hash, +// cyclic_vk, +// }) +// } + +// } + + +#[derive(Eq, PartialEq, Debug)] +struct BlockIVCAggChildTarget +{ + has_prev: BoolTarget, + agg_proof: ProofWithPublicInputsTarget, + block_proof: ProofWithPublicInputsTarget, + pv: PublicValuesTarget, + pv1: PublicValuesTarget, + pv_hash: HashOutTarget, +} + + +impl BlockIVCAggChildTarget { + // fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + // buffer.write_target_bool(self.has_prev)?; + // buffer.write_target_proof_with_public_inputs(&self.agg_proof)?; + // buffer.write_target_proof_with_public_inputs(&self.block_proof)?; + // buffer.write_target_hash(&self.pv_hash)?; + // 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 block_proof = buffer.read_target_proof_with_public_inputs()?; + // let pv_hash = buffer.read_target_hash()?; + // Ok(Self { + // has_prev: is_agg, + // agg_proof, + // block_proof, + // pv_hash, + // }) + // } + + fn public_values(&self, builder: &mut CircuitBuilder) -> PublicValuesTarget + where + F: RichField + Extendable, + C: GenericConfig, + { + let agg_pv = PublicValuesTarget::from_public_inputs(&self.agg_proof.public_inputs); + let block_pv = PublicValuesTarget::from_public_inputs(&self.block_proof.public_inputs); + PublicValuesTarget::select(builder, self.has_prev, agg_pv, block_pv) + } +} + + +/// Data for the two-to-one block circuit, which is used to generate a +/// proof of two unrelated proofs. +#[derive(Eq, PartialEq, Debug)] +pub struct TwoToOneBlockBinopAggCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub circuit: CircuitData, + lhs: BlockBinopAggChildTarget, + rhs: BlockBinopAggChildTarget, + mix_pv_hash: HashOutTarget, + dummy_pis: Vec, + cyclic_vk: VerifierCircuitTarget, +} + +impl TwoToOneBlockBinopAggCircuitData +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)?; + self.lhs.to_buffer(buffer); + self.rhs.to_buffer(buffer); + //buffer.write_target_hash(&self.mix_pv_hash)?; + buffer.write_target_verifier_circuit(&self.cyclic_vk)?; + 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 lhs = BlockBinopAggChildTarget::from_buffer(buffer)?; + let rhs = BlockBinopAggChildTarget::from_buffer(buffer)?; + let mix_pv_hash = buffer.read_target_hash()?; + let cyclic_vk = buffer.read_target_verifier_circuit()?; + Ok(Self { + circuit, + lhs, + rhs, + mix_pv_hash, + dummy_pis: todo!(), + cyclic_vk, + }) + } + +} + + +#[derive(Eq, PartialEq, Debug)] +struct BlockBinopAggChildTarget +{ + is_agg: BoolTarget, + agg_proof: ProofWithPublicInputsTarget, + block_proof: ProofWithPublicInputsTarget, + //pv_hash: HashOutTarget, +} + + +impl BlockBinopAggChildTarget { + 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.block_proof)?; + //buffer.write_target_hash(&self.pv_hash)?; + 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 block_proof = buffer.read_target_proof_with_public_inputs()?; + let pv_hash = buffer.read_target_hash()?; + Ok(Self { + is_agg, + agg_proof, + block_proof, + //pv_hash, + }) + } + + fn public_values>( + &self, + builder: &mut CircuitBuilder, + ) -> Vec { + zip_eq( + &self.agg_proof.public_inputs, + &self.block_proof.public_inputs, + ) + .map(|(&agg_pv, &block_pv)| builder.select(self.is_agg, agg_pv, block_pv)) + .collect() + } +} + + impl AllRecursiveCircuits where F: RichField + Extendable, @@ -435,11 +651,13 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let two_to_one_block = TwoToOneAggCircuitData::from_buffer( - &mut buffer, - gate_serializer, - generator_serializer, - )?; + let two_to_one_block_ivc = todo!(); + // TwoToOneBlockIVCAggCircuitData::from_buffer( + // &mut buffer, + // gate_serializer, + // generator_serializer, + // )?; + let two_to_one_block_binop = todo!(); let by_table = match skip_tables { true => (0..NUM_TABLES) @@ -476,7 +694,8 @@ where aggregation, two_to_one_aggregation, block, - two_to_one_block, + //two_to_one_block_ivc, + two_to_one_block_binop, by_table, }) } @@ -567,13 +786,21 @@ where let aggregation = Self::create_aggregation_circuit(&root); let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); - let two_to_one_block = Self::create_two_to_one_block_circuit(&block); + + //dbg!(&block.circuit.common, &aggregation.circuit.common); + //let two_to_one_block_ivc = Self::create_two_to_one_block_circuit_ivc(&block); + let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&by_table, &block); + + //dbg!(&block.circuit.common, &two_to_one_block_binop.circuit.common); + assert_eq!(&block.circuit.common, &two_to_one_block_binop.circuit.common); + Self { root, aggregation, two_to_one_aggregation, block, - two_to_one_block, + //two_to_one_block_ivc, + two_to_one_block_binop, by_table, } } @@ -738,6 +965,7 @@ where let mut builder = CircuitBuilder::::new(root.circuit.common.config.clone()); let public_values = add_virtual_public_values(&mut builder); let cyclic_vk = builder.add_verifier_data_public_inputs(); + let count_public_inputs = builder.num_public_inputs(); let lhs = Self::add_agg_child(&mut builder, root); let rhs = Self::add_agg_child(&mut builder, root); @@ -792,10 +1020,15 @@ where ); // Pad to match the root circuit's degree. + log::info!("Before padding: Aggregation: {} vs Root: {}.", builder.num_gates(),root.circuit.common.degree_bits()); while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { builder.add_gate(NoopGate, vec![]); } + log::info!("After padding: Aggregation: {} vs Root: {}.", builder.num_gates(), root.circuit.common.degree_bits()); + assert_eq!(count_public_inputs, builder.num_public_inputs()); + log::info!("AGG circuit Expected: {}, actual: {}", root.circuit.common.num_public_inputs, builder.num_public_inputs()); + assert_eq!(root.circuit.common.num_public_inputs, builder.num_public_inputs()); let circuit = builder.build::(); AggregationCircuitData { circuit, @@ -1367,6 +1600,11 @@ where pv0: PublicValues, pv1: PublicValues, ) -> anyhow::Result> { + let ppp0 = PublicValues::from_public_inputs(&proof0.public_inputs); + let ppp1 = PublicValues::from_public_inputs(&proof1.public_inputs); + debug_assert_eq!(pv0, ppp0); + debug_assert_eq!(pv1, ppp1); + let mut inputs = PartialWitness::new(); inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.proof0, proof0); @@ -1573,34 +1811,34 @@ where /// # Outputs /// /// This method outputs a [`ProofWithPublicInputs`]. - pub fn prove_two_to_one_block( - &self, - proof0: &ProofWithPublicInputs, - proof1: &ProofWithPublicInputs, - pv0: PublicValues, - pv1: PublicValues, - ) -> anyhow::Result> { - let mut inputs = PartialWitness::new(); - - inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); - inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); - - set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - - let proof = self.two_to_one_block.circuit.prove(inputs)?; - Ok(proof) - } + // pub fn prove_two_to_one_block( + // &self, + // proof0: &ProofWithPublicInputs, + // proof1: &ProofWithPublicInputs, + // pv0: PublicValues, + // pv1: PublicValues, + // ) -> anyhow::Result> { + // let mut inputs = PartialWitness::new(); + + // inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); + // inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); + + // set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err(|_| { + // anyhow::Error::msg("Invalid conversion when setting public values targets.") + // })?; + // set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err(|_| { + // anyhow::Error::msg("Invalid conversion when setting public values targets.") + // })?; + + // let proof = self.two_to_one_block.circuit.prove(inputs)?; + // Ok(proof) + // } pub fn verify_two_to_one_block( &self, proof: &ProofWithPublicInputs, ) -> anyhow::Result<()> { - self.two_to_one_block.circuit.verify(proof.clone()) + self.two_to_one_block_binop.circuit.verify(proof.clone()) } fn create_two_to_one_agg_circuit( @@ -1648,8 +1886,446 @@ where pv1, } } -} + /// This circuit follows the structure of [`create_block_circuit`]. This + /// circuit creates a IVC chain of unrelated blockproofs. In contract, it + /// does not have any of the check between the blocks and their public + /// values, because they are not related. However, it utilizes hashes of + /// public values to verify that the prover knew the corresponding public + /// value at prooftime. This part can be confusing: the public values are + /// kept hidden as part of the witness. Not because they are not public and + /// not because they are not known to the verifier, but rather to keep the + /// actual (registered) public values constant size, which is a requirement + /// to do cyclic recursion. Another thing, that can be confusing is that + /// `previous` here refers to the block, that came before and not to the + /// actual blockchain-order ancestor of the current block. The latter is + /// not a concern in this circuit. + fn create_two_to_one_block_circuit_ivc( + block_circuit: &BlockCircuitData + ) -> TwoToOneBlockIVCAggCircuitData + where + F: RichField + Extendable, + C: GenericConfig, + C::Hasher: AlgebraicHasher, + { + // Question: Do I need to adjust CommonCircuitData here like in [`create_block_circuit`]? + let expected_common_data = CommonCircuitData { + fri_params: FriParams { + degree_bits: 14, + ..block_circuit.circuit.common.fri_params.clone() + }, + ..block_circuit.circuit.common.clone() + }; + + let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); + + let check_prev = builder.add_virtual_bool_target_safe(); + + /// PublicInput: hash(pv0), hash(pv1), pv01_hash + let prev_pv_hash = builder.add_virtual_hash(); + let curr_pv_hash = builder.add_virtual_hash(); + let mix_pv_hash = builder.add_virtual_hash_public_input(); + + /// PrivateInput: p0, p1, pv0, pv1 + let prev_proof = builder.add_virtual_proof_with_pis(&expected_common_data); + let curr_proof = builder.add_virtual_proof_with_pis(&block_circuit.circuit.common); + + + // public inputs as structs + let curr_pv_target = add_virtual_public_values(&mut builder); + let curr_pv = PublicValuesTarget::from_public_inputs(&curr_proof.public_inputs); + + //connect pvs + //let public_values = add_virtual_public_values(&mut builder); + //let prev_pv = PublicValuesTarget::from_public_inputs(&prev_proof.public_inputs); + //let curr_pv = PublicValuesTarget::from_public_inputs(&curr_proof.public_inputs); + + + /// hashes + let prev_hash_circuit = builder.hash_n_to_hash_no_pad::(prev_proof.public_inputs.to_owned()); + let curr_hash_circuit = builder.hash_n_to_hash_no_pad::(curr_proof.public_inputs.to_owned()); + + let mut mix_vec = vec![]; + mix_vec.extend(&curr_hash_circuit.elements); + mix_vec.extend(&prev_hash_circuit.elements); + let mix_hash_circuit = builder.hash_n_to_hash_no_pad::(mix_vec); + + builder.connect_hashes(prev_pv_hash, curr_hash_circuit); + builder.connect_hashes(curr_pv_hash, prev_hash_circuit); + builder.connect_hashes(mix_pv_hash, mix_hash_circuit); + + + /// conditionally connect prev_pv_hash + // TODO what happens here? + // aka block verifier key + // let block_verifier_data = + // builder.constant_verifier_data(&block.circuit.verifier_data().verifier_only); + + // Verify previous block proof unless it is a dummy (cyclic base case) + let cyclic_vk = builder.add_verifier_data_public_inputs(); + let count_public_inputs = builder.num_public_inputs(); + builder + .conditionally_verify_cyclic_proof_or_dummy::( + check_prev, + &prev_proof, + &expected_common_data + ) + .expect("Failed to build cyclic recursion circuit for `prev`."); + + // Always verify current agg proof + let block_verifier_data = builder.constant_verifier_data(&block_circuit.circuit.verifier_only); + builder.verify_proof::(&curr_proof, &block_verifier_data, &block_circuit.circuit.common); + + // Question: Is this necessary here: Why/why not? Pad to match the root circuit's degree. + // while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { + // builder.add_gate(NoopGate, vec![]); + // } + + assert_eq!(count_public_inputs, builder.num_public_inputs()); + let circuit = builder.build::(); + + TwoToOneBlockIVCAggCircuitData { + circuit, + prev_proof, + curr_proof, + has_prev: check_prev, + curr_pv, + prev_pv_hash, + curr_pv_hash, + mix_pv_hash, + cyclic_vk, + } + } + + + /// Create a two-to-one block aggregation proof, combining two unrelated + /// block proofs into a single one. + /// + /// # Arguments + /// + /// - `proof0`: the first block proof including vectorized public inputs. + /// - `proof1`: the second block proof including vectorized public inputs. + /// - `lhs_proof`: the left child proof. + /// - `lhs_public_values`: the public values associated to the right child + /// proof. + /// - `rhs_is_agg`: a boolean indicating whether the right child proof is an + /// aggregation proof or + /// a regular transaction proof. + /// - `rhs_proof`: the right child proof. + /// - `rhs_public_values`: the public values associated to the right child + /// proof. + /// + /// # Outputs + /// + /// This method outputs a [`ProofWithPublicInputs`]. + /// + /// This method outputs a tuple of [`ProofWithPublicInputs`] and + /// its [`PublicValues`]. Only the proof with public inputs is necessary + /// for a verifier to assert correctness of the computation, + /// but the public values are output for the prover convenience, as these + /// are necessary during proof aggregation. + // pub fn prove_two_to_one_block_ivc( + // &self, + // opt_prev_proof: Option<&ProofWithPublicInputs>, + // curr_proof: &ProofWithPublicInputs, + // ) -> anyhow::Result> { + // let mut witness = PartialWitness::new(); + + // let has_prev = opt_prev_proof.is_some(); + // witness.set_bool_target(self.two_to_one_block_ivc.has_prev, has_prev); + + // if let Some(prev_proof) = opt_prev_proof { + // // get hashes and combine. lhs: `[0..4]`, rhs: `[4..8]`, mix: `[8..12]`, + // // cyclic_vk?, ??? + // let prev_pv_hash = HashOut { + // elements: prev_proof.public_inputs[8..12].try_into()?, + // }; + // witness.set_hash_target(self.two_to_one_block_ivc.prev_pv_hash, prev_pv_hash); + + // // select .. + // witness + // .set_proof_with_pis_target(&self.two_to_one_block_ivc.prev_proof, prev_proof); + // } else { + // // when no prev proof + // witness.set_proof_with_pis_target( + // &self.block.parent_block_proof, + // &cyclic_base_proof( + // &self.block.circuit.common, + // &self.block.circuit.verifier_only, + // todo!(), + // ), + // ); + + + // } + + // let curr_pv_hash = HashOut { + // elements: curr_proof.public_inputs[8..12].try_into()?, + // }; + // let mix_pv_hash = C::InnerHasher::two_to_one(todo!(), curr_pv_hash); + + // // set hashes + // witness.set_hash_target(self.two_to_one_block_ivc.curr_pv_hash, curr_pv_hash); + // witness.set_hash_target(self.two_to_one_block_ivc.mix_pv_hash, mix_pv_hash); + + // // set proofs + // witness + // .set_proof_with_pis_target(&self.two_to_one_block_ivc.curr_proof, curr_proof); + + // witness + // .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); + + + // // this is not used, but needs to be set to satisfy check in cyclic prover. + // set_public_value_targets( + // &mut witness, + // &self.two_to_one_block_ivc.curr_pv, + // &PublicValues::from_public_inputs(&curr_proof.public_inputs), + // ) + // .map_err(|_| { + // anyhow::Error::msg("Invalid conversion when setting public values targets.") + // })?; + + + // // prove + // let proof = self.two_to_one_block_ivc.circuit.prove(witness)?; + // Ok(proof) + // } + + + // pub fn verify_two_to_one_block_ivc( + // &self, + // proof: &ProofWithPublicInputs, + // ) -> anyhow::Result<()> { + // self.two_to_one_block_ivc.circuit.verify(proof.clone()); + // // Note that this does not enforce that the inner circuit uses the correct verification key. + // // This is not possible to check in this recursive circuit, since we do not know the + // // verification key until after we build it. Verifiers must separately call + // // `check_cyclic_proof_verifier_data`, in addition to verifying a recursive proof, to check + // // that the verification key matches. + + // // todo + // let verifier_data = &self.two_to_one_block_ivc.circuit.verifier_data(); + // check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) + // } + + pub fn prove_two_to_one_block_binop( + &self, + lhs: &ProofWithPublicInputs, + lhs_is_agg: bool, + rhs: &ProofWithPublicInputs, + rhs_is_agg: bool, + ) -> anyhow::Result> { + let mut witness = PartialWitness::new(); + + // if lhs_is_agg && rhs_is_agg { + let junk_pis = &self.two_to_one_block_binop.dummy_pis; + witness.set_target_arr(&junk_pis , &vec![F::ZERO; junk_pis.len()]); + //} + + Self::set_dummy_if_necessary( + &self.two_to_one_block_binop.lhs, + lhs_is_agg, + &self.two_to_one_block_binop.circuit, + &mut witness, + &lhs, + ); + + Self::set_dummy_if_necessary( + &self.two_to_one_block_binop.rhs, + rhs_is_agg, + &self.two_to_one_block_binop.circuit, + &mut witness, + &rhs, + ); + + witness.set_verifier_data_target( + &self.two_to_one_block_binop.cyclic_vk, + &self.two_to_one_block_binop.circuit.verifier_only, + ); + //dbg!(&self.two_to_one_block_binop.circuit.verifier_only); + + let lhs_pv_hash = C::InnerHasher::hash_no_pad(&lhs.public_inputs); + let rhs_pv_hash = C::InnerHasher::hash_no_pad(&rhs.public_inputs); + let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); + witness.set_hash_target(self.two_to_one_block_binop.mix_pv_hash, mix_pv_hash); + + + // prove + let proof = self.two_to_one_block_binop.circuit.prove(witness)?; + Ok(proof) + } + + pub fn verify_two_to_one_block_binop( + &self, + proof: &ProofWithPublicInputs, + ) -> anyhow::Result<()> { + self.two_to_one_block_binop.circuit.verify(proof.clone()); + // Note that this does not enforce that the inner circuit uses the correct verification key. + // This is not possible to check in this recursive circuit, since we do not know the + // verification key until after we build it. Verifiers must separately call + // `check_cyclic_proof_verifier_data`, in addition to verifying a recursive proof, to check + // that the verification key matches. + + // todo + let verifier_data = &self.two_to_one_block_binop.circuit.verifier_data(); + check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) + } + + fn add_block_agg_child( + builder: &mut CircuitBuilder, + block: &BlockCircuitData, + ) -> BlockBinopAggChildTarget { + let count_public_inputs = builder.num_public_inputs(); + let block_common = &block.circuit.common; + let block_vk = builder.constant_verifier_data(&block.circuit.verifier_only); + let is_agg = builder.add_virtual_bool_target_safe(); + // block_common should be use for agg_proof because they are similar + let agg_proof = builder.add_virtual_proof_with_pis(block_common); + let block_proof = builder.add_virtual_proof_with_pis(block_common); + builder + .conditionally_verify_cyclic_proof::( + is_agg, &agg_proof, &block_proof, &block_vk, block_common, + ) + .expect("Failed to build cyclic recursion circuit"); + assert_eq!(count_public_inputs, builder.num_public_inputs()); + BlockBinopAggChildTarget { + is_agg, + agg_proof, + block_proof, + // pv_hash, + } + } + + fn create_two_to_one_block_circuit_binop( + by_table: &[RecursiveCircuitsForTable; NUM_TABLES], + block: &BlockCircuitData, + ) -> TwoToOneBlockBinopAggCircuitData + where + F: RichField + Extendable, + C: GenericConfig, + C::Hasher: AlgebraicHasher, + { + let mut builder = CircuitBuilder::::new(block.circuit.common.config.clone()); + + let mut padding_pis = vec![]; + // magic numbers derived from failing assertion at end of this function. + while builder.num_public_inputs() < block.circuit.common.num_public_inputs-(2337-2269) { + let target = builder.add_virtual_public_input(); + padding_pis.push(target); + } + + // let cap_len = block.public_values.mem_before.mem_cap.0.len(); + /// PublicInput: hash(pv0), hash(pv1), pv01_hash + // let mix_pv_hash = builder.add_virtual_hash_public_input(); + let cyclic_vk = builder.add_verifier_data_public_inputs(); + // making sure we do not add public inputs after this point + let count_public_inputs = builder.num_public_inputs(); + + let lhs = Self::add_block_agg_child(&mut builder, &block); + let rhs = Self::add_block_agg_child(&mut builder, &block); + + let a = lhs.public_values(&mut builder); + let b = rhs.public_values(&mut builder); + + let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(a); + let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(b); + let mut mix_vec = vec![]; + mix_vec.extend(&lhs_pv_hash.elements); + mix_vec.extend(&rhs_pv_hash.elements); + let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); + // let mix_pv_hash = builder.constant_hash(HashOut::ZERO); + + //builder.connect_hashes(lhs.pv_hash, lhs_hash_circuit); + //builder.connect_hashes(rhs.pv_hash, rhs_hash_circuit); + //builder.connect_hashes(mix_pv_hash, mix_hash_circuit); + + // Pad to match the block circuit's degree. + log::info!("Before padding: TwoToOneBlockBinop: {} vs Block: {}.", builder.num_gates(), block.circuit.common.degree_bits()); + // while log2_ceil(builder.num_gates()) < block.circuit.common.degree_bits() { + // builder.add_gate(NoopGate, vec![]); + // } + log::info!("After padding: TwoToOneBlockBinop: {} vs Block: {}.", builder.num_gates(), block.circuit.common.degree_bits()); + + log::info!("Expected: {}, actual: {}", block.circuit.common.num_public_inputs, builder.num_public_inputs()); + + assert_eq!(count_public_inputs, builder.num_public_inputs()); + assert_eq!(block.circuit.common.num_public_inputs, builder.num_public_inputs(), "Block aggregation circuit and block base case circuit must have same number of public inputs."); + dbg!("Now attempting to build circuit."); + let circuit = builder.build::(); + TwoToOneBlockBinopAggCircuitData { + circuit, + lhs, + rhs, + mix_pv_hash, + dummy_pis: padding_pis, + cyclic_vk, + } + } + + /// 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: &BlockBinopAggChildTarget, + 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.block_proof, proof); + } +} /// A map between initial degree sizes and their associated shrinking recursion /// circuits. #[derive(Eq, PartialEq, Debug)] diff --git a/evm_arithmetization/src/lib.rs b/evm_arithmetization/src/lib.rs index b9236ae96..75b852fa9 100644 --- a/evm_arithmetization/src/lib.rs +++ b/evm_arithmetization/src/lib.rs @@ -200,6 +200,7 @@ mod get_challenges; pub mod proof; pub mod prover; pub mod recursive_verifier; +pub mod private_public_values; pub mod verifier; // Witness generation diff --git a/evm_arithmetization/src/private_public_values.rs b/evm_arithmetization/src/private_public_values.rs new file mode 100644 index 000000000..8e6616fc4 --- /dev/null +++ b/evm_arithmetization/src/private_public_values.rs @@ -0,0 +1,89 @@ +use plonky2::{field::extension::Extendable, hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; +use crate::proof::{BlockHashesTarget, BlockMetadataTarget, ExtraBlockDataTarget, PublicValuesTarget, TrieRootsTarget}; + +/// This file contains copies of the same functions in [`recursive_verifier.rs`], but without the registering the targets. +/// This is useful when we want to supply public values privately into a circuit. + +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); + PublicValuesTarget { + trie_roots_before, + trie_roots_after, + block_metadata, + block_hashes, + extra_block_data, + } +} + +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, 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_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_bloom, + } +} + +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, 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, + } +} \ No newline at end of file diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 27ee09141..955a503a6 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -279,6 +279,9 @@ pub struct PublicValuesTarget { } impl PublicValuesTarget { + pub(crate) const SIZE: usize = TrieRootsTarget::SIZE + TrieRootsTarget::SIZE + + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE + ExtraBlockDataTarget::SIZE; + /// Serializes public value targets. pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { let TrieRootsTarget { @@ -435,6 +438,17 @@ impl PublicValuesTarget { } } + // Todo + // maybe should be `impl Into for PublicValuesTarget`. + pub(crate) fn to_public_inputs(&self, res: &mut Vec) { + self.trie_roots_before.to_public_inputs(res); + self.trie_roots_after.to_public_inputs(res); + self.block_metadata.to_public_inputs(res); + self.block_hashes.to_public_inputs(res); + self.extra_block_data.to_public_inputs(res); + assert_eq!(res.len(), PublicValuesTarget::SIZE); + } + /// Returns the public values in `pv0` or `pv1` depending on `condition`. pub(crate) fn select, const D: usize>( builder: &mut CircuitBuilder, @@ -510,6 +524,12 @@ impl TrieRootsTarget { } } + pub(crate) fn to_public_inputs(&self, res: &mut Vec) { + res.extend(self.state_root); + res.extend(self.transactions_root); + res.extend(self.receipts_root); + } + /// If `condition`, returns the trie hashes in `tr0`, /// otherwise returns the trie hashes in `tr1`. pub(crate) fn select, const D: usize>( @@ -608,6 +628,20 @@ impl BlockMetadataTarget { } } + pub(crate) fn to_public_inputs(&self, res: &mut Vec) { + res.extend(self.block_beneficiary); + res.push(self.block_timestamp); + res.push(self.block_number); + res.push(self.block_difficulty); + res.extend(self.block_random); + res.push(self.block_gaslimit); + res.push(self.block_chain_id); + res.extend(self.block_base_fee); + res.push(self.block_gas_used); + res.extend(self.block_bloom); + } + + /// If `condition`, returns the block metadata in `bm0`, /// otherwise returns the block metadata in `bm1`. pub(crate) fn select, const D: usize>( @@ -701,6 +735,11 @@ impl BlockHashesTarget { } } + pub(crate) fn to_public_inputs(&self, res: &mut Vec) { + res.extend(&self.prev_hashes); + res.extend(&self.cur_hash); + } + /// If `condition`, returns the block hashes in `bm0`, /// otherwise returns the block hashes in `bm1`. pub(crate) fn select, const D: usize>( @@ -778,6 +817,14 @@ impl ExtraBlockDataTarget { } } + pub(crate) fn to_public_inputs(&self, res: &mut Vec) { + res.extend(self.checkpoint_state_trie_root); + res.push(self.txn_number_before); + res.push(self.txn_number_after); + res.push(self.gas_used_before); + res.push(self.gas_used_after); + } + /// If `condition`, returns the extra block data in `ed0`, /// otherwise returns the extra block data in `ed1`. pub(crate) fn select, const D: usize>( diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 7bc7b0ed4..965055c96 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -1,11 +1,12 @@ use std::collections::HashMap; +use std::{env, fs}; use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; -use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; +use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; use hex_literal::hex; use keccak_hash::keccak; @@ -13,11 +14,19 @@ use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; +use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; +// use std::error::Error; + type F = GoldilocksField; const D: usize = 2; type C = PoseidonGoldilocksConfig; +type PwPIS = ProofWithPublicInputs; + +/// Set this to true to cache blocks in `/tmp``. This is intended mainly for developer experience and not for CI testing. +const CACHE_TEST_BLOCKS: bool = true; + /// Get `GenerationInputs` for a simple token transfer txn, where the block has /// the given timestamp. @@ -188,7 +197,7 @@ fn test_two_to_one_aggregation() -> anyhow::Result<()> { // Preprocess all circuits. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[8..17, 8..15, 8..18, 8..15, 8..10, 8..13, 8..20], + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], &config, ); @@ -227,6 +236,538 @@ fn test_two_to_one_aggregation() -> anyhow::Result<()> { all_circuits.verify_two_to_one_aggregation(&proof) } +/// Get `GenerationInputs` for a simple token transfer txn, where the block has +/// the given timestamp. +fn empty_transfer(timestamp: u64) -> anyhow::Result { + init_logger(); + + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); + let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); + + let sender_state_key = keccak(sender); + let to_state_key = keccak(to); + + let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); + let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); + + let sender_account_before = AccountRlp { + nonce: 5.into(), + balance: eth_to_wei(100_000.into()), + storage_root: HashedPartialTrie::from(Node::Empty).hash(), + code_hash: keccak([]), + }; + let to_account_before = AccountRlp::default(); + + let state_trie_before: HashedPartialTrie = Node::Leaf { + nibbles: sender_nibbles, + value: rlp::encode(&sender_account_before).to_vec(), + } + .into(); + let checkpoint_state_trie_root = state_trie_before.hash(); + assert_eq!(checkpoint_state_trie_root, hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into()); + + let tries_before = TrieInputs { + state_trie: HashedPartialTrie::from(Node::Empty), + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + storage_tries: vec![], + }; + + // Generated using a little py-evm script. + let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); + let value = U256::from(100u32); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: timestamp.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: 0.into(), + block_bloom: [0.into(); 8], + }; + + let contract_code = HashMap::new(); + + let expected_state_trie_after: HashedPartialTrie = { + let txdata_gas = 2 * 16; + let gas_used = 21_000 + txdata_gas; + + let sender_account_after = AccountRlp { + balance: sender_account_before.balance - value - gas_used * 10, + nonce: sender_account_before.nonce + 1, + ..sender_account_before + }; + let to_account_after = AccountRlp { + balance: value, + ..to_account_before + }; + + let mut children = core::array::from_fn(|_| Node::Empty.into()); + children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { + nibbles: sender_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&sender_account_after).to_vec(), + } + .into(); + children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { + nibbles: to_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&to_account_after).to_vec(), + } + .into(); + Node::Branch { + children, + value: vec![], + } + .into() + }; + + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: 21032.into(), + bloom: vec![0; 256].into(), + logs: vec![], + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert( + Nibbles::from_str("0x80").unwrap(), + rlp::encode(&receipt_0).to_vec(), + )?; + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let _trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + + let tries_after = TrieRoots { + state_root: tries_before.state_trie.hash(), + transactions_root: tries_before.transactions_trie.hash(), + receipts_root: tries_before.receipts_trie.hash(), + }; + let inputs = GenerationInputs { + signed_txn: None, + withdrawals: vec![], + tries: tries_before.clone(), + trie_roots_after: tries_after, + contract_code, + checkpoint_state_trie_root: tries_before.state_trie.hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 0.into(), + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + Ok(inputs) +} + +fn get_test_block_proof( + timestamp: u64, + timing: &mut TimingTree, + all_circuits: &AllRecursiveCircuits, + all_stark: &AllStark, + config: &StarkConfig, +) -> anyhow::Result> { + log::info!("Stage 0"); + log::info!("Generating proof of block {}", timestamp); + let inputs0 = empty_transfer(timestamp)?; + let inputs = inputs0.clone(); + let dummy0 = GenerationInputs { + txn_number_before: inputs.txn_number_before, + gas_used_before: inputs.gas_used_after, + gas_used_after: inputs.gas_used_after, + signed_txn: None, + withdrawals: vec![], + tries: TrieInputs { + state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), + transactions_trie: HashedPartialTrie::from(Node::Hash( + inputs.trie_roots_after.transactions_root, + )), + receipts_trie: HashedPartialTrie::from(Node::Hash( + inputs.trie_roots_after.receipts_root, + )), + storage_tries: vec![], + }, + trie_roots_after: inputs.trie_roots_after, + checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, + contract_code: Default::default(), + block_metadata: inputs.block_metadata.clone(), + block_hashes: inputs.block_hashes.clone(), + }; + log::info!("{:#?}", inputs0); + log::info!("{:#?}", dummy0); + log::info!("Stage 1"); + + let (root_proof0, pv0) = all_circuits.prove_root(&all_stark, &config, inputs0, timing, None)?; + all_circuits.verify_root(root_proof0.clone())?; + let (dummy_proof0, dummy_pv0) = + all_circuits.prove_root(&all_stark, &config, dummy0, timing, None)?; + all_circuits.verify_root(dummy_proof0.clone())?; + + log::info!("Stage 2"); + let (agg_proof0, pv0) = all_circuits.prove_aggregation( + false, + &root_proof0, + pv0, + false, + &dummy_proof0, + dummy_pv0, + )?; + + log::info!("Stage 3: Verify aggregation"); + all_circuits.verify_aggregation(&agg_proof0)?; + + log::info!("Stage 4: Check public values"); + // 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); + assert_eq!( + pv0.trie_roots_before.state_root, + pv0.extra_block_data.checkpoint_state_trie_root + ); + + log::info!("Stage 5: Prove Block"); + let (block_proof0, 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(), + )?; + + let pv_block = PublicValues::from_public_inputs(&block_proof0.public_inputs); + assert_eq!(block_public_values, pv_block); + + Ok(block_proof0) +} + +/// Caches proofs in `/tmp/zk_evm_test_blocks/`. +fn get_test_block_proof_cached( + timestamp: u64, + timing: &mut TimingTree, + all_circuits: &AllRecursiveCircuits, + all_stark: &AllStark, + config: &StarkConfig, +) -> anyhow::Result>{ + log::info!("Getting proof of block {}", timestamp); + + // 1. Setup path + let mut path = env::temp_dir(); + path.push("zk_evm_test"); + path.push(format!("test_block_{timestamp}.bpf")); + log::info!("{:#?}", path); + + // 2. Read cached block from disc and return early. + if CACHE_TEST_BLOCKS && path.try_exists()? && fs::File::open(path.clone())?.metadata()?.len() > 0 { + let raw_block = fs::read(path)?; + return ProofWithPublicInputs::from_bytes( + raw_block, + &all_circuits.block.circuit.common, + ); + } + + // 3. Compute new block proof. + let block_proof = get_test_block_proof(timestamp, timing, all_circuits, all_stark, config)?; + all_circuits.verify_block(&block_proof)?; + + // 4. Write block to disc cache and validate. + if CACHE_TEST_BLOCKS { + // write to tmp + let raw_block = ProofWithPublicInputs::to_bytes(&block_proof); + + if let Some(p) = path.parent() { + fs::create_dir_all(p)? + }; + fs::write(path.clone(), &raw_block)?; + log::info!("Succesfully wrote blockproof to {:#?}", path); + + // Todo: move to file with `from_bytes` + let written_block = fs::read(path.clone())?; + assert_eq!(&raw_block, &written_block); + let restored_block = ProofWithPublicInputs::from_bytes( + written_block, + &all_circuits.block.circuit.common, + )?; + assert_eq!(block_proof, restored_block); + log::info!("Succesfully validated blockproof from {:#?}", path); + } + + return Ok(block_proof); +} + + + +#[test] +/// Run: RUST_BACKTRACE=1 RUSTFLAGS="-Ctarget-cpu=native -g -Z threads 8" +/// cargo test --release -- --nocapture three_to_one Aggregate a sequential +/// proof containing three proofs with the structure `((A,B),C)`. We take the +/// previous example and extend it. +/// +/// A B C +/// \ / / +/// (A,B) / +/// \ / +/// ((A,B),C) +fn test_three_to_one_block_aggregation_ivc() -> anyhow::Result<()> { + init_logger(); + log::info!("Meta Stage 0: Setup"); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + // Preprocess all circuits. + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + + log::info!("Meta Stage 1: Compute block proofs"); + let some_timestamps = [127, 42, 65]; + let unrelated_block_proofs = some_timestamps + .iter() + .map(|&ts| { + get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) + }) + .collect::>>()?; + + log::info!("Meta Stage 2: Verify block proofs"); + unrelated_block_proofs + .iter() + .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + + log::info!("Meta Stage 3: Aggregate block proofs"); + let bp = unrelated_block_proofs; + + // let aggproof0 = all_circuits.prove_two_to_one_block_ivc(None, &bp[0])?; + // all_circuits.verify_two_to_one_block_ivc(&aggproof0)?; + + // let aggproof01 = all_circuits.prove_two_to_one_block_ivc(Some(&aggproof0), &bp[1])?; + // all_circuits.verify_two_to_one_block_ivc(&aggproof01)?; + + // let aggproof012 = all_circuits.prove_two_to_one_block_ivc(Some(&aggproof01), &bp[2])?; + // all_circuits.verify_two_to_one_block_ivc(&aggproof012)?; + assert!(false, "Hoooray!!, 3-block aggregation was verified"); + Ok(()) +} + + + +/// Run: RUST_BACKTRACE=1 RUSTFLAGS="-Ctarget-cpu=native -g -Z threads 8" +/// cargo test --release -- --nocapture four_to_one +/// Aggregate a sequential /// proof containing three proofs with the structure `((A,B),(C,D))`. +/// +/// A B C D Blockproofs (base case) +/// \ / \ / +/// (A, B) (C, D) Two-to-one block aggregation proofs +/// \ / +/// ((A,B), (C,D)) Two-to-one block aggregation proofs +#[test] +fn test_block_aggregation_binop_4_blocks() -> anyhow::Result<()> { + init_logger(); + log::info!("Meta Stage 0: Setup"); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + // Preprocess all circuits. + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + + log::info!("Meta Stage 1: Compute block proofs"); + let some_timestamps = [127, 42, 65, 43]; + let unrelated_block_proofs = some_timestamps + .iter() + .map(|&ts| { + get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) + }) + .collect::>>()?; + + log::info!("Meta Stage 2: Verify block proofs"); + unrelated_block_proofs + .iter() + .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + + log::info!("Meta Stage 3: Aggregate block proofs"); + let bp = unrelated_block_proofs; + + let aggproof01 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block_binop(&aggproof01)?; + + let aggproof23 = all_circuits.prove_two_to_one_block_binop(&bp[2], false, &bp[3], false)?; + all_circuits.verify_two_to_one_block_binop(&aggproof23)?; + + let aggproof0123 = all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &aggproof23, true)?; + all_circuits.verify_two_to_one_block_binop(&aggproof0123)?; + + Ok(()) +} + + + +#[test] +fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { + init_logger(); + log::info!("Meta Stage 0: Setup"); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + // Preprocess all circuits. + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + + log::info!("Meta Stage 1: Compute block proofs"); + let some_timestamps = [42, 42]; + let unrelated_block_proofs = some_timestamps + .iter() + .map(|&ts| { + get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) + }) + .collect::>>()?; + + log::info!("Meta Stage 2: Verify block proofs"); + unrelated_block_proofs + .iter() + .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + + log::info!("Meta Stage 3: Aggregate block proofs"); + let bp = unrelated_block_proofs; + + let aggproof_42_42 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block_binop(&aggproof_42_42)?; + + Ok(()) +} + + +/// Run: RUST_BACKTRACE=1 RUSTFLAGS="-Ctarget-cpu=native -g -Z threads 8" +/// cargo test --release -- --nocapture four_to_one +/// Aggregate a sequential /// proof containing three proofs with the structure `((A,B),(C,D))`. +/// +/// A B C Blockproofs (base case) +/// \ / / +/// (A, B) / Two-to-one block aggregation proofs +/// \ / +/// ((A,B), C) Two-to-one block aggregation proofs +#[test] +fn test_block_aggregation_binop_foldleft() -> anyhow::Result<()> { + init_logger(); + log::info!("Meta Stage 0: Setup"); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + // Preprocess all circuits. + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + + log::info!("Meta Stage 1: Compute block proofs"); + let some_timestamps = [65, 127, 42]; + let unrelated_block_proofs = some_timestamps + .iter() + .map(|&ts| { + get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) + }) + .collect::>>()?; + + log::info!("Meta Stage 2: Verify block proofs"); + unrelated_block_proofs + .iter() + .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + + log::info!("Meta Stage 3: Aggregate block proofs"); + let bp = unrelated_block_proofs; + + let aggproof01 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block_binop(&aggproof01)?; + + let aggproof012 = all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &bp[2], false)?; + all_circuits.verify_two_to_one_block_binop(&aggproof012)?; + + Ok(()) +} + + +/// +/// A B C Blockproofs (base case) +/// \ \ / +/// \ (B,C) Two-to-one block aggregation proofs +/// \ / +/// (A,(B, C)) Two-to-one block aggregation proofs +#[test] +fn test_block_aggregation_binop_foldright() -> anyhow::Result<()> { + init_logger(); + log::info!("Meta Stage 0: Setup"); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + // Preprocess all circuits. + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &config, + ); + + let mut timing = TimingTree::new("prove root first", log::Level::Info); + + log::info!("Meta Stage 1: Compute block proofs"); + let some_timestamps = [65, 127, 42]; + let unrelated_block_proofs = some_timestamps + .iter() + .map(|&ts| { + get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) + }) + .collect::>>()?; + + log::info!("Meta Stage 2: Verify block proofs"); + unrelated_block_proofs + .iter() + .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + + log::info!("Meta Stage 3: Aggregate block proofs"); + let bp = unrelated_block_proofs; + + let aggproof12 = all_circuits.prove_two_to_one_block_binop(&bp[1], false, &bp[2], false)?; + all_circuits.verify_two_to_one_block_binop(&aggproof12)?; + + let aggproof012 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &aggproof12, true)?; + all_circuits.verify_two_to_one_block_binop(&aggproof012)?; + + Ok(()) +} + + + + + + + + + fn eth_to_wei(eth: U256) -> U256 { // 1 ether = 10^18 wei. eth * U256::from(10).pow(18.into()) From f275b01d4cac5e2aa81e51ef68190bb422df6f44 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 14:42:08 +0800 Subject: [PATCH 07/59] cleanup(tests) --- evm_arithmetization/tests/two_to_one_agg.rs | 179 ++++++-------------- 1 file changed, 51 insertions(+), 128 deletions(-) diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 965055c96..b6ed68b8f 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use std::{env, fs}; use std::str::FromStr; +use std::{env, fs}; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; @@ -17,16 +17,13 @@ use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; -// use std::error::Error; - type F = GoldilocksField; const D: usize = 2; type C = PoseidonGoldilocksConfig; -type PwPIS = ProofWithPublicInputs; - -/// Set this to true to cache blocks in `/tmp``. This is intended mainly for developer experience and not for CI testing. -const CACHE_TEST_BLOCKS: bool = true; +/// Set this to true to cache blocks in `/tmp``. This is intended mainly for +/// developer experience and not for CI testing. +const CACHE_TEST_BLOCKS: bool = false; /// Get `GenerationInputs` for a simple token transfer txn, where the block has /// the given timestamp. @@ -236,6 +233,15 @@ fn test_two_to_one_aggregation() -> anyhow::Result<()> { all_circuits.verify_two_to_one_aggregation(&proof) } +fn eth_to_wei(eth: U256) -> U256 { + // 1 ether = 10^18 wei. + eth * U256::from(10).pow(18.into()) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +} + /// Get `GenerationInputs` for a simple token transfer txn, where the block has /// the given timestamp. fn empty_transfer(timestamp: u64) -> anyhow::Result { @@ -265,7 +271,10 @@ fn empty_transfer(timestamp: u64) -> anyhow::Result { } .into(); let checkpoint_state_trie_root = state_trie_before.hash(); - assert_eq!(checkpoint_state_trie_root, hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into()); + assert_eq!( + checkpoint_state_trie_root, + hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into() + ); let tries_before = TrieInputs { state_trie: HashedPartialTrie::from(Node::Empty), @@ -458,7 +467,7 @@ fn get_test_block_proof_cached( all_circuits: &AllRecursiveCircuits, all_stark: &AllStark, config: &StarkConfig, -) -> anyhow::Result>{ +) -> anyhow::Result> { log::info!("Getting proof of block {}", timestamp); // 1. Setup path @@ -468,12 +477,12 @@ fn get_test_block_proof_cached( log::info!("{:#?}", path); // 2. Read cached block from disc and return early. - if CACHE_TEST_BLOCKS && path.try_exists()? && fs::File::open(path.clone())?.metadata()?.len() > 0 { + if CACHE_TEST_BLOCKS + && path.try_exists()? + && fs::File::open(path.clone())?.metadata()?.len() > 0 + { let raw_block = fs::read(path)?; - return ProofWithPublicInputs::from_bytes( - raw_block, - &all_circuits.block.circuit.common, - ); + return ProofWithPublicInputs::from_bytes(raw_block, &all_circuits.block.circuit.common); } // 3. Compute new block proof. @@ -494,10 +503,8 @@ fn get_test_block_proof_cached( // Todo: move to file with `from_bytes` let written_block = fs::read(path.clone())?; assert_eq!(&raw_block, &written_block); - let restored_block = ProofWithPublicInputs::from_bytes( - written_block, - &all_circuits.block.circuit.common, - )?; + let restored_block = + ProofWithPublicInputs::from_bytes(written_block, &all_circuits.block.circuit.common)?; assert_eq!(block_proof, restored_block); log::info!("Succesfully validated blockproof from {:#?}", path); } @@ -505,68 +512,8 @@ fn get_test_block_proof_cached( return Ok(block_proof); } - - -#[test] -/// Run: RUST_BACKTRACE=1 RUSTFLAGS="-Ctarget-cpu=native -g -Z threads 8" -/// cargo test --release -- --nocapture three_to_one Aggregate a sequential -/// proof containing three proofs with the structure `((A,B),C)`. We take the -/// previous example and extend it. -/// -/// A B C -/// \ / / -/// (A,B) / -/// \ / -/// ((A,B),C) -fn test_three_to_one_block_aggregation_ivc() -> anyhow::Result<()> { - init_logger(); - log::info!("Meta Stage 0: Setup"); - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - - log::info!("Meta Stage 1: Compute block proofs"); - let some_timestamps = [127, 42, 65]; - let unrelated_block_proofs = some_timestamps - .iter() - .map(|&ts| { - get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) - }) - .collect::>>()?; - - log::info!("Meta Stage 2: Verify block proofs"); - unrelated_block_proofs - .iter() - .map(|bp| all_circuits.verify_block(bp)).collect::>()?; - - log::info!("Meta Stage 3: Aggregate block proofs"); - let bp = unrelated_block_proofs; - - // let aggproof0 = all_circuits.prove_two_to_one_block_ivc(None, &bp[0])?; - // all_circuits.verify_two_to_one_block_ivc(&aggproof0)?; - - // let aggproof01 = all_circuits.prove_two_to_one_block_ivc(Some(&aggproof0), &bp[1])?; - // all_circuits.verify_two_to_one_block_ivc(&aggproof01)?; - - // let aggproof012 = all_circuits.prove_two_to_one_block_ivc(Some(&aggproof01), &bp[2])?; - // all_circuits.verify_two_to_one_block_ivc(&aggproof012)?; - assert!(false, "Hoooray!!, 3-block aggregation was verified"); - Ok(()) -} - - - -/// Run: RUST_BACKTRACE=1 RUSTFLAGS="-Ctarget-cpu=native -g -Z threads 8" -/// cargo test --release -- --nocapture four_to_one -/// Aggregate a sequential /// proof containing three proofs with the structure `((A,B),(C,D))`. +/// Aggregate a sequential /// proof containing three proofs with the structure +/// `((A,B),(C,D))`. /// /// A B C D Blockproofs (base case) /// \ / \ / @@ -593,15 +540,14 @@ fn test_block_aggregation_binop_4_blocks() -> anyhow::Result<()> { let some_timestamps = [127, 42, 65, 43]; let unrelated_block_proofs = some_timestamps .iter() - .map(|&ts| { - get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) - }) - .collect::>>()?; + .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) + .collect::>>>()?; log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + .map(|bp| all_circuits.verify_block(bp)) + .collect::>()?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -612,14 +558,13 @@ fn test_block_aggregation_binop_4_blocks() -> anyhow::Result<()> { let aggproof23 = all_circuits.prove_two_to_one_block_binop(&bp[2], false, &bp[3], false)?; all_circuits.verify_two_to_one_block_binop(&aggproof23)?; - let aggproof0123 = all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &aggproof23, true)?; + let aggproof0123 = + all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &aggproof23, true)?; all_circuits.verify_two_to_one_block_binop(&aggproof0123)?; Ok(()) } - - #[test] fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { init_logger(); @@ -640,15 +585,14 @@ fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { let some_timestamps = [42, 42]; let unrelated_block_proofs = some_timestamps .iter() - .map(|&ts| { - get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) - }) - .collect::>>()?; + .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) + .collect::>>>()?; log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + .map(|bp| all_circuits.verify_block(bp)) + .collect::>()?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -659,10 +603,8 @@ fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { Ok(()) } - -/// Run: RUST_BACKTRACE=1 RUSTFLAGS="-Ctarget-cpu=native -g -Z threads 8" -/// cargo test --release -- --nocapture four_to_one -/// Aggregate a sequential /// proof containing three proofs with the structure `((A,B),(C,D))`. +/// Aggregate a sequential /// proof containing three proofs with the structure +/// `((A,B),(C,D))`. /// /// A B C Blockproofs (base case) /// \ / / @@ -689,15 +631,14 @@ fn test_block_aggregation_binop_foldleft() -> anyhow::Result<()> { let some_timestamps = [65, 127, 42]; let unrelated_block_proofs = some_timestamps .iter() - .map(|&ts| { - get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) - }) - .collect::>>()?; + .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) + .collect::>>>()?; log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + .map(|bp| all_circuits.verify_block(bp)) + .collect::>()?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -705,14 +646,13 @@ fn test_block_aggregation_binop_foldleft() -> anyhow::Result<()> { let aggproof01 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; all_circuits.verify_two_to_one_block_binop(&aggproof01)?; - let aggproof012 = all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &bp[2], false)?; + let aggproof012 = + all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &bp[2], false)?; all_circuits.verify_two_to_one_block_binop(&aggproof012)?; Ok(()) } - -/// /// A B C Blockproofs (base case) /// \ \ / /// \ (B,C) Two-to-one block aggregation proofs @@ -738,15 +678,14 @@ fn test_block_aggregation_binop_foldright() -> anyhow::Result<()> { let some_timestamps = [65, 127, 42]; let unrelated_block_proofs = some_timestamps .iter() - .map(|&ts| { - get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config) - }) - .collect::>>()?; + .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) + .collect::>>>()?; log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)).collect::>()?; + .map(|bp| all_circuits.verify_block(bp)) + .collect::>()?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -754,25 +693,9 @@ fn test_block_aggregation_binop_foldright() -> anyhow::Result<()> { let aggproof12 = all_circuits.prove_two_to_one_block_binop(&bp[1], false, &bp[2], false)?; all_circuits.verify_two_to_one_block_binop(&aggproof12)?; - let aggproof012 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &aggproof12, true)?; + let aggproof012 = + all_circuits.prove_two_to_one_block_binop(&bp[0], false, &aggproof12, true)?; all_circuits.verify_two_to_one_block_binop(&aggproof012)?; Ok(()) } - - - - - - - - - -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} From 2e2d39b26644c2a4e443e0646a9a2aba54ac3415 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 14:54:02 +0800 Subject: [PATCH 08/59] cleanup(tests): obey Clippy --- evm_arithmetization/tests/two_to_one_agg.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index b6ed68b8f..33affe18f 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -419,10 +419,10 @@ fn get_test_block_proof( log::info!("{:#?}", dummy0); log::info!("Stage 1"); - let (root_proof0, pv0) = all_circuits.prove_root(&all_stark, &config, inputs0, timing, None)?; + let (root_proof0, pv0) = all_circuits.prove_root(all_stark, config, inputs0, timing, None)?; all_circuits.verify_root(root_proof0.clone())?; let (dummy_proof0, dummy_pv0) = - all_circuits.prove_root(&all_stark, &config, dummy0, timing, None)?; + all_circuits.prove_root(all_stark, config, dummy0, timing, None)?; all_circuits.verify_root(dummy_proof0.clone())?; log::info!("Stage 2"); @@ -509,7 +509,7 @@ fn get_test_block_proof_cached( log::info!("Succesfully validated blockproof from {:#?}", path); } - return Ok(block_proof); + Ok(block_proof) } /// Aggregate a sequential /// proof containing three proofs with the structure @@ -546,8 +546,7 @@ fn test_block_aggregation_binop_4_blocks() -> anyhow::Result<()> { log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)) - .collect::>()?; + .try_for_each(|bp| all_circuits.verify_block(bp))?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -591,8 +590,7 @@ fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)) - .collect::>()?; + .try_for_each(|bp| all_circuits.verify_block(bp))?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -637,8 +635,7 @@ fn test_block_aggregation_binop_foldleft() -> anyhow::Result<()> { log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)) - .collect::>()?; + .try_for_each(|bp| all_circuits.verify_block(bp))?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; @@ -684,8 +681,7 @@ fn test_block_aggregation_binop_foldright() -> anyhow::Result<()> { log::info!("Meta Stage 2: Verify block proofs"); unrelated_block_proofs .iter() - .map(|bp| all_circuits.verify_block(bp)) - .collect::>()?; + .try_for_each(|bp| all_circuits.verify_block(bp))?; log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; From 275fcd1dab0a1732d30020b91cabace43b187176 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 14:59:13 +0800 Subject: [PATCH 09/59] cleanup: remove unneeded experiment --- .../src/fixed_recursive_verifier.rs | 1 - evm_arithmetization/src/lib.rs | 1 - .../src/private_public_values.rs | 89 ------------------- 3 files changed, 91 deletions(-) delete mode 100644 evm_arithmetization/src/private_public_values.rs diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index db6703ae5..76dee9a84 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -38,7 +38,6 @@ use starky::stark::Stark; use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES}; use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; -use crate::private_public_values; use crate::proof::{ AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, TrieRoots, TrieRootsTarget, diff --git a/evm_arithmetization/src/lib.rs b/evm_arithmetization/src/lib.rs index 75b852fa9..b9236ae96 100644 --- a/evm_arithmetization/src/lib.rs +++ b/evm_arithmetization/src/lib.rs @@ -200,7 +200,6 @@ mod get_challenges; pub mod proof; pub mod prover; pub mod recursive_verifier; -pub mod private_public_values; pub mod verifier; // Witness generation diff --git a/evm_arithmetization/src/private_public_values.rs b/evm_arithmetization/src/private_public_values.rs deleted file mode 100644 index 8e6616fc4..000000000 --- a/evm_arithmetization/src/private_public_values.rs +++ /dev/null @@ -1,89 +0,0 @@ -use plonky2::{field::extension::Extendable, hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; -use crate::proof::{BlockHashesTarget, BlockMetadataTarget, ExtraBlockDataTarget, PublicValuesTarget, TrieRootsTarget}; - -/// This file contains copies of the same functions in [`recursive_verifier.rs`], but without the registering the targets. -/// This is useful when we want to supply public values privately into a circuit. - -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); - PublicValuesTarget { - trie_roots_before, - trie_roots_after, - block_metadata, - block_hashes, - extra_block_data, - } -} - -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, 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_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_bloom, - } -} - -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, 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, - } -} \ No newline at end of file From 4def18227a37f171bd8d3d9a127037b9f3cb08c5 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 15:31:25 +0800 Subject: [PATCH 10/59] cleanup: remove vector to public inputs --- evm_arithmetization/src/proof.rs | 47 -------------------------------- 1 file changed, 47 deletions(-) diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 955a503a6..27ee09141 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -279,9 +279,6 @@ pub struct PublicValuesTarget { } impl PublicValuesTarget { - pub(crate) const SIZE: usize = TrieRootsTarget::SIZE + TrieRootsTarget::SIZE - + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE + ExtraBlockDataTarget::SIZE; - /// Serializes public value targets. pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { let TrieRootsTarget { @@ -438,17 +435,6 @@ impl PublicValuesTarget { } } - // Todo - // maybe should be `impl Into for PublicValuesTarget`. - pub(crate) fn to_public_inputs(&self, res: &mut Vec) { - self.trie_roots_before.to_public_inputs(res); - self.trie_roots_after.to_public_inputs(res); - self.block_metadata.to_public_inputs(res); - self.block_hashes.to_public_inputs(res); - self.extra_block_data.to_public_inputs(res); - assert_eq!(res.len(), PublicValuesTarget::SIZE); - } - /// Returns the public values in `pv0` or `pv1` depending on `condition`. pub(crate) fn select, const D: usize>( builder: &mut CircuitBuilder, @@ -524,12 +510,6 @@ impl TrieRootsTarget { } } - pub(crate) fn to_public_inputs(&self, res: &mut Vec) { - res.extend(self.state_root); - res.extend(self.transactions_root); - res.extend(self.receipts_root); - } - /// If `condition`, returns the trie hashes in `tr0`, /// otherwise returns the trie hashes in `tr1`. pub(crate) fn select, const D: usize>( @@ -628,20 +608,6 @@ impl BlockMetadataTarget { } } - pub(crate) fn to_public_inputs(&self, res: &mut Vec) { - res.extend(self.block_beneficiary); - res.push(self.block_timestamp); - res.push(self.block_number); - res.push(self.block_difficulty); - res.extend(self.block_random); - res.push(self.block_gaslimit); - res.push(self.block_chain_id); - res.extend(self.block_base_fee); - res.push(self.block_gas_used); - res.extend(self.block_bloom); - } - - /// If `condition`, returns the block metadata in `bm0`, /// otherwise returns the block metadata in `bm1`. pub(crate) fn select, const D: usize>( @@ -735,11 +701,6 @@ impl BlockHashesTarget { } } - pub(crate) fn to_public_inputs(&self, res: &mut Vec) { - res.extend(&self.prev_hashes); - res.extend(&self.cur_hash); - } - /// If `condition`, returns the block hashes in `bm0`, /// otherwise returns the block hashes in `bm1`. pub(crate) fn select, const D: usize>( @@ -817,14 +778,6 @@ impl ExtraBlockDataTarget { } } - pub(crate) fn to_public_inputs(&self, res: &mut Vec) { - res.extend(self.checkpoint_state_trie_root); - res.push(self.txn_number_before); - res.push(self.txn_number_after); - res.push(self.gas_used_before); - res.push(self.gas_used_after); - } - /// If `condition`, returns the extra block data in `ed0`, /// otherwise returns the extra block data in `ed1`. pub(crate) fn select, const D: usize>( From a67ba6e3aef3f67908782f38e694fc6845ab9294 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 23:10:45 +0800 Subject: [PATCH 11/59] cleanup: remove IVC and re-add two_to_one_block --- .../src/fixed_recursive_verifier.rs | 402 ++---------------- 1 file changed, 39 insertions(+), 363 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 76dee9a84..b141b0fcc 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -80,7 +80,7 @@ where pub block: BlockCircuitData, /// The two-to-one block aggregation circuit, which verifies two unrelated /// block proofs. - //pub two_to_one_block_ivc: TwoToOneBlockIVCAggCircuitData, + pub two_to_one_block: TwoToOneAggCircuitData, pub two_to_one_block_binop: TwoToOneBlockBinopAggCircuitData, /// Holds chains of circuits for each table and for each initial /// `degree_bits`. @@ -362,116 +362,6 @@ where } } - - -/// Data for the two-to-one block circuit, which is used to generate a -/// proof of two unrelated proofs. -#[derive(Eq, PartialEq, Debug)] -pub struct TwoToOneBlockIVCAggCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - pub circuit: CircuitData, - ///// new structure - prev_proof: ProofWithPublicInputsTarget, - curr_proof: ProofWithPublicInputsTarget, - has_prev: BoolTarget, - curr_pv: PublicValuesTarget, - prev_pv_hash: HashOutTarget, - curr_pv_hash: HashOutTarget, - mix_pv_hash: HashOutTarget, - // TODO: do I need this? - cyclic_vk: VerifierCircuitTarget, -} - -// impl TwoToOneBlockIVCAggCircuitData -// 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)?; -// self.prev.to_buffer(buffer); -// self.curr.to_buffer(buffer); -// buffer.write_target_hash(&self.agg_pv_hash)?; -// buffer.write_target_verifier_circuit(&self.cyclic_vk)?; -// 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 lhs = BlockIVCAggChildTarget::from_buffer(buffer)?; -// let rhs = BlockIVCAggChildTarget::from_buffer(buffer)?; -// let mix_pv_hash = buffer.read_target_hash()?; -// let cyclic_vk = buffer.read_target_verifier_circuit()?; -// Ok(Self { -// circuit, -// prev: lhs, -// curr: rhs, -// mix_pv_hash, -// cyclic_vk, -// }) -// } - -// } - - -#[derive(Eq, PartialEq, Debug)] -struct BlockIVCAggChildTarget -{ - has_prev: BoolTarget, - agg_proof: ProofWithPublicInputsTarget, - block_proof: ProofWithPublicInputsTarget, - pv: PublicValuesTarget, - pv1: PublicValuesTarget, - pv_hash: HashOutTarget, -} - - -impl BlockIVCAggChildTarget { - // fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - // buffer.write_target_bool(self.has_prev)?; - // buffer.write_target_proof_with_public_inputs(&self.agg_proof)?; - // buffer.write_target_proof_with_public_inputs(&self.block_proof)?; - // buffer.write_target_hash(&self.pv_hash)?; - // 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 block_proof = buffer.read_target_proof_with_public_inputs()?; - // let pv_hash = buffer.read_target_hash()?; - // Ok(Self { - // has_prev: is_agg, - // agg_proof, - // block_proof, - // pv_hash, - // }) - // } - - fn public_values(&self, builder: &mut CircuitBuilder) -> PublicValuesTarget - where - F: RichField + Extendable, - C: GenericConfig, - { - let agg_pv = PublicValuesTarget::from_public_inputs(&self.agg_proof.public_inputs); - let block_pv = PublicValuesTarget::from_public_inputs(&self.block_proof.public_inputs); - PublicValuesTarget::select(builder, self.has_prev, agg_pv, block_pv) - } -} - - /// Data for the two-to-one block circuit, which is used to generate a /// proof of two unrelated proofs. #[derive(Eq, PartialEq, Debug)] @@ -650,12 +540,11 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let two_to_one_block_ivc = todo!(); - // TwoToOneBlockIVCAggCircuitData::from_buffer( - // &mut buffer, - // gate_serializer, - // generator_serializer, - // )?; + let two_to_one_block = TwoToOneAggCircuitData::::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; let two_to_one_block_binop = todo!(); let by_table = match skip_tables { @@ -693,7 +582,7 @@ where aggregation, two_to_one_aggregation, block, - //two_to_one_block_ivc, + two_to_one_block, two_to_one_block_binop, by_table, }) @@ -785,20 +674,16 @@ where let aggregation = Self::create_aggregation_circuit(&root); let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); - - //dbg!(&block.circuit.common, &aggregation.circuit.common); - //let two_to_one_block_ivc = Self::create_two_to_one_block_circuit_ivc(&block); + let two_to_one_block = Self::create_two_to_one_block_circuit(&block); let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&by_table, &block); - - //dbg!(&block.circuit.common, &two_to_one_block_binop.circuit.common); - assert_eq!(&block.circuit.common, &two_to_one_block_binop.circuit.common); + debug_assert_eq!(&block.circuit.common, &two_to_one_block_binop.circuit.common); Self { root, aggregation, two_to_one_aggregation, block, - //two_to_one_block_ivc, + two_to_one_block, two_to_one_block_binop, by_table, } @@ -1810,34 +1695,34 @@ where /// # Outputs /// /// This method outputs a [`ProofWithPublicInputs`]. - // pub fn prove_two_to_one_block( - // &self, - // proof0: &ProofWithPublicInputs, - // proof1: &ProofWithPublicInputs, - // pv0: PublicValues, - // pv1: PublicValues, - // ) -> anyhow::Result> { - // let mut inputs = PartialWitness::new(); - - // inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); - // inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); - - // set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err(|_| { - // anyhow::Error::msg("Invalid conversion when setting public values targets.") - // })?; - // set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err(|_| { - // anyhow::Error::msg("Invalid conversion when setting public values targets.") - // })?; - - // let proof = self.two_to_one_block.circuit.prove(inputs)?; - // Ok(proof) - // } + pub fn prove_two_to_one_block( + &self, + proof0: &ProofWithPublicInputs, + proof1: &ProofWithPublicInputs, + pv0: PublicValues, + pv1: PublicValues, + ) -> anyhow::Result> { + let mut inputs = PartialWitness::new(); + + inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); + inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); + + set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; + set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; + + let proof = self.two_to_one_block.circuit.prove(inputs)?; + Ok(proof) + } pub fn verify_two_to_one_block( &self, proof: &ProofWithPublicInputs, ) -> anyhow::Result<()> { - self.two_to_one_block_binop.circuit.verify(proof.clone()) + self.two_to_one_block.circuit.verify(proof.clone()) } fn create_two_to_one_agg_circuit( @@ -1886,228 +1771,19 @@ where } } - /// This circuit follows the structure of [`create_block_circuit`]. This - /// circuit creates a IVC chain of unrelated blockproofs. In contract, it - /// does not have any of the check between the blocks and their public - /// values, because they are not related. However, it utilizes hashes of - /// public values to verify that the prover knew the corresponding public - /// value at prooftime. This part can be confusing: the public values are - /// kept hidden as part of the witness. Not because they are not public and - /// not because they are not known to the verifier, but rather to keep the - /// actual (registered) public values constant size, which is a requirement - /// to do cyclic recursion. Another thing, that can be confusing is that - /// `previous` here refers to the block, that came before and not to the - /// actual blockchain-order ancestor of the current block. The latter is - /// not a concern in this circuit. - fn create_two_to_one_block_circuit_ivc( - block_circuit: &BlockCircuitData - ) -> TwoToOneBlockIVCAggCircuitData - where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, - { - // Question: Do I need to adjust CommonCircuitData here like in [`create_block_circuit`]? - let expected_common_data = CommonCircuitData { - fri_params: FriParams { - degree_bits: 14, - ..block_circuit.circuit.common.fri_params.clone() - }, - ..block_circuit.circuit.common.clone() - }; - - let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); - - let check_prev = builder.add_virtual_bool_target_safe(); - - /// PublicInput: hash(pv0), hash(pv1), pv01_hash - let prev_pv_hash = builder.add_virtual_hash(); - let curr_pv_hash = builder.add_virtual_hash(); - let mix_pv_hash = builder.add_virtual_hash_public_input(); - - /// PrivateInput: p0, p1, pv0, pv1 - let prev_proof = builder.add_virtual_proof_with_pis(&expected_common_data); - let curr_proof = builder.add_virtual_proof_with_pis(&block_circuit.circuit.common); - - - // public inputs as structs - let curr_pv_target = add_virtual_public_values(&mut builder); - let curr_pv = PublicValuesTarget::from_public_inputs(&curr_proof.public_inputs); - - //connect pvs - //let public_values = add_virtual_public_values(&mut builder); - //let prev_pv = PublicValuesTarget::from_public_inputs(&prev_proof.public_inputs); - //let curr_pv = PublicValuesTarget::from_public_inputs(&curr_proof.public_inputs); - - - /// hashes - let prev_hash_circuit = builder.hash_n_to_hash_no_pad::(prev_proof.public_inputs.to_owned()); - let curr_hash_circuit = builder.hash_n_to_hash_no_pad::(curr_proof.public_inputs.to_owned()); - - let mut mix_vec = vec![]; - mix_vec.extend(&curr_hash_circuit.elements); - mix_vec.extend(&prev_hash_circuit.elements); - let mix_hash_circuit = builder.hash_n_to_hash_no_pad::(mix_vec); - - builder.connect_hashes(prev_pv_hash, curr_hash_circuit); - builder.connect_hashes(curr_pv_hash, prev_hash_circuit); - builder.connect_hashes(mix_pv_hash, mix_hash_circuit); - - - /// conditionally connect prev_pv_hash - // TODO what happens here? - // aka block verifier key - // let block_verifier_data = - // builder.constant_verifier_data(&block.circuit.verifier_data().verifier_only); - - // Verify previous block proof unless it is a dummy (cyclic base case) - let cyclic_vk = builder.add_verifier_data_public_inputs(); - let count_public_inputs = builder.num_public_inputs(); - builder - .conditionally_verify_cyclic_proof_or_dummy::( - check_prev, - &prev_proof, - &expected_common_data - ) - .expect("Failed to build cyclic recursion circuit for `prev`."); - - // Always verify current agg proof - let block_verifier_data = builder.constant_verifier_data(&block_circuit.circuit.verifier_only); - builder.verify_proof::(&curr_proof, &block_verifier_data, &block_circuit.circuit.common); - - // Question: Is this necessary here: Why/why not? Pad to match the root circuit's degree. - // while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { - // builder.add_gate(NoopGate, vec![]); - // } - - assert_eq!(count_public_inputs, builder.num_public_inputs()); - let circuit = builder.build::(); - - TwoToOneBlockIVCAggCircuitData { - circuit, - prev_proof, - curr_proof, - has_prev: check_prev, - curr_pv, - prev_pv_hash, - curr_pv_hash, - mix_pv_hash, - cyclic_vk, - } - } - - - /// Create a two-to-one block aggregation proof, combining two unrelated - /// block proofs into a single one. + /// This aggregates block proofs in a manner similar to a binary operator. + /// Visually: `(lhs, lhs_is_agg) BinOp (rhs, rhs_is_agg)`. /// /// # Arguments /// - /// - `proof0`: the first block proof including vectorized public inputs. - /// - `proof1`: the second block proof including vectorized public inputs. - /// - `lhs_proof`: the left child proof. - /// - `lhs_public_values`: the public values associated to the right child - /// proof. - /// - `rhs_is_agg`: a boolean indicating whether the right child proof is an - /// aggregation proof or - /// a regular transaction proof. - /// - `rhs_proof`: the right child proof. - /// - `rhs_public_values`: the public values associated to the right child - /// proof. + /// - `lhs`: a proof of either a block or previous aggregation proof. + /// - `lhs_is_agg`: specify which case `lhs` was. + /// - `rhs`: a proof of either a block or previous aggregation proof. + /// - `rhs_is_agg`: specify which case `rhs` was. /// /// # Outputs /// /// This method outputs a [`ProofWithPublicInputs`]. - /// - /// This method outputs a tuple of [`ProofWithPublicInputs`] and - /// its [`PublicValues`]. Only the proof with public inputs is necessary - /// for a verifier to assert correctness of the computation, - /// but the public values are output for the prover convenience, as these - /// are necessary during proof aggregation. - // pub fn prove_two_to_one_block_ivc( - // &self, - // opt_prev_proof: Option<&ProofWithPublicInputs>, - // curr_proof: &ProofWithPublicInputs, - // ) -> anyhow::Result> { - // let mut witness = PartialWitness::new(); - - // let has_prev = opt_prev_proof.is_some(); - // witness.set_bool_target(self.two_to_one_block_ivc.has_prev, has_prev); - - // if let Some(prev_proof) = opt_prev_proof { - // // get hashes and combine. lhs: `[0..4]`, rhs: `[4..8]`, mix: `[8..12]`, - // // cyclic_vk?, ??? - // let prev_pv_hash = HashOut { - // elements: prev_proof.public_inputs[8..12].try_into()?, - // }; - // witness.set_hash_target(self.two_to_one_block_ivc.prev_pv_hash, prev_pv_hash); - - // // select .. - // witness - // .set_proof_with_pis_target(&self.two_to_one_block_ivc.prev_proof, prev_proof); - // } else { - // // when no prev proof - // witness.set_proof_with_pis_target( - // &self.block.parent_block_proof, - // &cyclic_base_proof( - // &self.block.circuit.common, - // &self.block.circuit.verifier_only, - // todo!(), - // ), - // ); - - - // } - - // let curr_pv_hash = HashOut { - // elements: curr_proof.public_inputs[8..12].try_into()?, - // }; - // let mix_pv_hash = C::InnerHasher::two_to_one(todo!(), curr_pv_hash); - - // // set hashes - // witness.set_hash_target(self.two_to_one_block_ivc.curr_pv_hash, curr_pv_hash); - // witness.set_hash_target(self.two_to_one_block_ivc.mix_pv_hash, mix_pv_hash); - - // // set proofs - // witness - // .set_proof_with_pis_target(&self.two_to_one_block_ivc.curr_proof, curr_proof); - - // witness - // .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); - - - // // this is not used, but needs to be set to satisfy check in cyclic prover. - // set_public_value_targets( - // &mut witness, - // &self.two_to_one_block_ivc.curr_pv, - // &PublicValues::from_public_inputs(&curr_proof.public_inputs), - // ) - // .map_err(|_| { - // anyhow::Error::msg("Invalid conversion when setting public values targets.") - // })?; - - - // // prove - // let proof = self.two_to_one_block_ivc.circuit.prove(witness)?; - // Ok(proof) - // } - - - // pub fn verify_two_to_one_block_ivc( - // &self, - // proof: &ProofWithPublicInputs, - // ) -> anyhow::Result<()> { - // self.two_to_one_block_ivc.circuit.verify(proof.clone()); - // // Note that this does not enforce that the inner circuit uses the correct verification key. - // // This is not possible to check in this recursive circuit, since we do not know the - // // verification key until after we build it. Verifiers must separately call - // // `check_cyclic_proof_verifier_data`, in addition to verifying a recursive proof, to check - // // that the verification key matches. - - // // todo - // let verifier_data = &self.two_to_one_block_ivc.circuit.verifier_data(); - // check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) - // } - pub fn prove_two_to_one_block_binop( &self, lhs: &ProofWithPublicInputs, From 30342ca7fcd00ddca342765ce5772853c3bb3eff Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 23:14:20 +0800 Subject: [PATCH 12/59] cleanup: fix de/serialization --- .../src/fixed_recursive_verifier.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index b141b0fcc..292d2b80c 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -390,9 +390,10 @@ where generator_serializer: &dyn WitnessGeneratorSerializer, ) -> IoResult<()> { buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; - self.lhs.to_buffer(buffer); - self.rhs.to_buffer(buffer); - //buffer.write_target_hash(&self.mix_pv_hash)?; + self.lhs.to_buffer(buffer)?; + self.rhs.to_buffer(buffer)?; + buffer.write_target_vec(&self.dummy_pis)?; + buffer.write_target_hash(&self.mix_pv_hash)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; Ok(()) } @@ -406,13 +407,14 @@ where let lhs = BlockBinopAggChildTarget::from_buffer(buffer)?; let rhs = BlockBinopAggChildTarget::from_buffer(buffer)?; let mix_pv_hash = buffer.read_target_hash()?; + let dummy_pis = buffer.read_target_vec()?; let cyclic_vk = buffer.read_target_verifier_circuit()?; Ok(Self { circuit, lhs, rhs, mix_pv_hash, - dummy_pis: todo!(), + dummy_pis, cyclic_vk, }) } @@ -426,16 +428,13 @@ struct BlockBinopAggChildTarget is_agg: BoolTarget, agg_proof: ProofWithPublicInputsTarget, block_proof: ProofWithPublicInputsTarget, - //pv_hash: HashOutTarget, } - impl BlockBinopAggChildTarget { 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.block_proof)?; - //buffer.write_target_hash(&self.pv_hash)?; Ok(()) } @@ -443,12 +442,10 @@ impl BlockBinopAggChildTarget { let is_agg = buffer.read_target_bool()?; let agg_proof = buffer.read_target_proof_with_public_inputs()?; let block_proof = buffer.read_target_proof_with_public_inputs()?; - let pv_hash = buffer.read_target_hash()?; Ok(Self { is_agg, agg_proof, block_proof, - //pv_hash, }) } From 4a9b1ca50816e0f3dfb049590f7a3b87714cf7a7 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 23:17:13 +0800 Subject: [PATCH 13/59] cleanup: add comments --- .../src/fixed_recursive_verifier.rs | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 292d2b80c..ca2be8c14 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1790,10 +1790,8 @@ where ) -> anyhow::Result> { let mut witness = PartialWitness::new(); - // if lhs_is_agg && rhs_is_agg { - let junk_pis = &self.two_to_one_block_binop.dummy_pis; - witness.set_target_arr(&junk_pis , &vec![F::ZERO; junk_pis.len()]); - //} + let dummy_pis = &self.two_to_one_block_binop.dummy_pis; + witness.set_target_arr(&dummy_pis , &vec![F::ZERO; dummy_pis.len()]); Self::set_dummy_if_necessary( &self.two_to_one_block_binop.lhs, @@ -1815,42 +1813,52 @@ where &self.two_to_one_block_binop.cyclic_vk, &self.two_to_one_block_binop.circuit.verifier_only, ); - //dbg!(&self.two_to_one_block_binop.circuit.verifier_only); let lhs_pv_hash = C::InnerHasher::hash_no_pad(&lhs.public_inputs); let rhs_pv_hash = C::InnerHasher::hash_no_pad(&rhs.public_inputs); let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); witness.set_hash_target(self.two_to_one_block_binop.mix_pv_hash, mix_pv_hash); - - // prove let proof = self.two_to_one_block_binop.circuit.prove(witness)?; Ok(proof) } + /// Method to verify an existing proof + /// + /// # Arguments + /// + /// - `proof`: The proof generated with `prove_two_to_one_block_binop`. + /// + /// # Outputs + /// + /// Returns whether the proof was valid or not. pub fn verify_two_to_one_block_binop( &self, proof: &ProofWithPublicInputs, ) -> anyhow::Result<()> { self.two_to_one_block_binop.circuit.verify(proof.clone()); - // Note that this does not enforce that the inner circuit uses the correct verification key. - // This is not possible to check in this recursive circuit, since we do not know the - // verification key until after we build it. Verifiers must separately call - // `check_cyclic_proof_verifier_data`, in addition to verifying a recursive proof, to check - // that the verification key matches. - - // todo let verifier_data = &self.two_to_one_block_binop.circuit.verifier_data(); check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } + /// Helper method to construct one of the two circuits representing unrelated proofs + /// + /// # Arguments + /// + /// - `builder`: The circuit builder object. + /// - `block_circuit_data`: Circuit data describing the blocks that can be + /// aggregated. + /// + /// # Outputs + /// + /// Returns a `BlockBinopAggChildTarget` object. fn add_block_agg_child( builder: &mut CircuitBuilder, - block: &BlockCircuitData, + block_circuit_data: &BlockCircuitData, ) -> BlockBinopAggChildTarget { let count_public_inputs = builder.num_public_inputs(); - let block_common = &block.circuit.common; - let block_vk = builder.constant_verifier_data(&block.circuit.verifier_only); + let block_common = &block_circuit_data.circuit.common; + let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); let is_agg = builder.add_virtual_bool_target_safe(); // block_common should be use for agg_proof because they are similar let agg_proof = builder.add_virtual_proof_with_pis(block_common); @@ -1880,57 +1888,49 @@ where { let mut builder = CircuitBuilder::::new(block.circuit.common.config.clone()); - let mut padding_pis = vec![]; - // magic numbers derived from failing assertion at end of this function. + let mut dummy_pis = vec![]; + // The magic numbers derived from failing assertion at end of this function. while builder.num_public_inputs() < block.circuit.common.num_public_inputs-(2337-2269) { let target = builder.add_virtual_public_input(); - padding_pis.push(target); + dummy_pis.push(target); } - // let cap_len = block.public_values.mem_before.mem_cap.0.len(); - /// PublicInput: hash(pv0), hash(pv1), pv01_hash - // let mix_pv_hash = builder.add_virtual_hash_public_input(); let cyclic_vk = builder.add_verifier_data_public_inputs(); - // making sure we do not add public inputs after this point + // Avoid accidentally adding public inputs after calling [`add_verifier_data_public_inputs`]. let count_public_inputs = builder.num_public_inputs(); let lhs = Self::add_block_agg_child(&mut builder, &block); let rhs = Self::add_block_agg_child(&mut builder, &block); - let a = lhs.public_values(&mut builder); - let b = rhs.public_values(&mut builder); + let lhs_public_values = lhs.public_values(&mut builder); + let rhs_public_values = rhs.public_values(&mut builder); - let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(a); - let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(b); + let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values); + let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values); let mut mix_vec = vec![]; mix_vec.extend(&lhs_pv_hash.elements); mix_vec.extend(&rhs_pv_hash.elements); let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); - // let mix_pv_hash = builder.constant_hash(HashOut::ZERO); - //builder.connect_hashes(lhs.pv_hash, lhs_hash_circuit); - //builder.connect_hashes(rhs.pv_hash, rhs_hash_circuit); - //builder.connect_hashes(mix_pv_hash, mix_hash_circuit); - - // Pad to match the block circuit's degree. - log::info!("Before padding: TwoToOneBlockBinop: {} vs Block: {}.", builder.num_gates(), block.circuit.common.degree_bits()); - // while log2_ceil(builder.num_gates()) < block.circuit.common.degree_bits() { - // builder.add_gate(NoopGate, vec![]); - // } - log::info!("After padding: TwoToOneBlockBinop: {} vs Block: {}.", builder.num_gates(), block.circuit.common.degree_bits()); + debug_assert_eq!( + count_public_inputs, + builder.num_public_inputs(), + "Public inputs were registered after called `add_verifier_data_public_inputs`" + ); - log::info!("Expected: {}, actual: {}", block.circuit.common.num_public_inputs, builder.num_public_inputs()); + debug_assert_eq!( + block.circuit.common.num_public_inputs, + builder.num_public_inputs(), + "The block aggregation circuit and the block circuit must agree on the number of public inputs." + ); - assert_eq!(count_public_inputs, builder.num_public_inputs()); - assert_eq!(block.circuit.common.num_public_inputs, builder.num_public_inputs(), "Block aggregation circuit and block base case circuit must have same number of public inputs."); - dbg!("Now attempting to build circuit."); let circuit = builder.build::(); TwoToOneBlockBinopAggCircuitData { circuit, lhs, rhs, mix_pv_hash, - dummy_pis: padding_pis, + dummy_pis, cyclic_vk, } } From fb29be86146b3dc917ef149783428a4913fc1cf7 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 24 Jun 2024 23:59:20 +0800 Subject: [PATCH 14/59] cleanup: remove checks --- .../src/fixed_recursive_verifier.rs | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index ca2be8c14..bd810c65b 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -418,13 +418,10 @@ where cyclic_vk, }) } - } - #[derive(Eq, PartialEq, Debug)] -struct BlockBinopAggChildTarget -{ +struct BlockBinopAggChildTarget { is_agg: BoolTarget, agg_proof: ProofWithPublicInputsTarget, block_proof: ProofWithPublicInputsTarget, @@ -462,7 +459,6 @@ impl BlockBinopAggChildTarget { } } - impl AllRecursiveCircuits where F: RichField + Extendable, @@ -537,7 +533,7 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let two_to_one_block = TwoToOneAggCircuitData::::from_buffer( + let two_to_one_block = TwoToOneAggCircuitData::from_buffer( &mut buffer, gate_serializer, generator_serializer, @@ -673,7 +669,10 @@ where let block = Self::create_block_circuit(&aggregation); let two_to_one_block = Self::create_two_to_one_block_circuit(&block); let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&by_table, &block); - debug_assert_eq!(&block.circuit.common, &two_to_one_block_binop.circuit.common); + debug_assert_eq!( + &block.circuit.common, + &two_to_one_block_binop.circuit.common + ); Self { root, @@ -901,15 +900,10 @@ where ); // Pad to match the root circuit's degree. - log::info!("Before padding: Aggregation: {} vs Root: {}.", builder.num_gates(),root.circuit.common.degree_bits()); while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { builder.add_gate(NoopGate, vec![]); } - log::info!("After padding: Aggregation: {} vs Root: {}.", builder.num_gates(), root.circuit.common.degree_bits()); - assert_eq!(count_public_inputs, builder.num_public_inputs()); - log::info!("AGG circuit Expected: {}, actual: {}", root.circuit.common.num_public_inputs, builder.num_public_inputs()); - assert_eq!(root.circuit.common.num_public_inputs, builder.num_public_inputs()); let circuit = builder.build::(); AggregationCircuitData { circuit, @@ -1481,11 +1475,6 @@ where pv0: PublicValues, pv1: PublicValues, ) -> anyhow::Result> { - let ppp0 = PublicValues::from_public_inputs(&proof0.public_inputs); - let ppp1 = PublicValues::from_public_inputs(&proof1.public_inputs); - debug_assert_eq!(pv0, ppp0); - debug_assert_eq!(pv1, ppp1); - let mut inputs = PartialWitness::new(); inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.proof0, proof0); @@ -1791,7 +1780,7 @@ where let mut witness = PartialWitness::new(); let dummy_pis = &self.two_to_one_block_binop.dummy_pis; - witness.set_target_arr(&dummy_pis , &vec![F::ZERO; dummy_pis.len()]); + witness.set_target_arr(&dummy_pis, &vec![F::ZERO; dummy_pis.len()]); Self::set_dummy_if_necessary( &self.two_to_one_block_binop.lhs, @@ -1841,7 +1830,8 @@ where check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } - /// Helper method to construct one of the two circuits representing unrelated proofs + /// Helper method to construct one of the two circuits representing + /// unrelated proofs /// /// # Arguments /// @@ -1865,7 +1855,11 @@ where let block_proof = builder.add_virtual_proof_with_pis(block_common); builder .conditionally_verify_cyclic_proof::( - is_agg, &agg_proof, &block_proof, &block_vk, block_common, + is_agg, + &agg_proof, + &block_proof, + &block_vk, + block_common, ) .expect("Failed to build cyclic recursion circuit"); assert_eq!(count_public_inputs, builder.num_public_inputs()); @@ -1890,13 +1884,14 @@ where let mut dummy_pis = vec![]; // The magic numbers derived from failing assertion at end of this function. - while builder.num_public_inputs() < block.circuit.common.num_public_inputs-(2337-2269) { + while builder.num_public_inputs() < block.circuit.common.num_public_inputs - (2337 - 2269) { let target = builder.add_virtual_public_input(); dummy_pis.push(target); } let cyclic_vk = builder.add_verifier_data_public_inputs(); - // Avoid accidentally adding public inputs after calling [`add_verifier_data_public_inputs`]. + // Avoid accidentally adding public inputs after calling + // [`add_verifier_data_public_inputs`]. let count_public_inputs = builder.num_public_inputs(); let lhs = Self::add_block_agg_child(&mut builder, &block); From 6fd87f765783b46d827fa456b6454b81cde2583d Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 00:04:44 +0800 Subject: [PATCH 15/59] cleanup: remove unrelated change --- evm_arithmetization/tests/log_opcode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm_arithmetization/tests/log_opcode.rs b/evm_arithmetization/tests/log_opcode.rs index 31ec4c834..75cdd44f5 100644 --- a/evm_arithmetization/tests/log_opcode.rs +++ b/evm_arithmetization/tests/log_opcode.rs @@ -562,7 +562,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { signed_txn: Some(txn_2.to_vec()), withdrawals: vec![], tries: tries_before, - trie_roots_after, + trie_roots_after: trie_roots_after.clone(), contract_code, checkpoint_state_trie_root, block_metadata: block_1_metadata, From 2edfb0a29f4e4eaf70c0e51cd0d68529104423c4 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 00:05:41 +0800 Subject: [PATCH 16/59] cleanup: remove unused parameter and todo --- evm_arithmetization/src/fixed_recursive_verifier.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index bd810c65b..4b277836c 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -538,7 +538,11 @@ where gate_serializer, generator_serializer, )?; - let two_to_one_block_binop = todo!(); + let two_to_one_block_binop = TwoToOneBlockBinopAggCircuitData::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; let by_table = match skip_tables { true => (0..NUM_TABLES) @@ -668,7 +672,7 @@ where let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); let two_to_one_block = Self::create_two_to_one_block_circuit(&block); - let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&by_table, &block); + let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&block); debug_assert_eq!( &block.circuit.common, &two_to_one_block_binop.circuit.common @@ -1872,7 +1876,6 @@ where } fn create_two_to_one_block_circuit_binop( - by_table: &[RecursiveCircuitsForTable; NUM_TABLES], block: &BlockCircuitData, ) -> TwoToOneBlockBinopAggCircuitData where From 3a2b59cd75dd84c70000cb2a31d147ac40d9bc4e Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 00:06:22 +0800 Subject: [PATCH 17/59] feat: enable caching as default --- evm_arithmetization/tests/two_to_one_agg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 33affe18f..e15ca5da0 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -23,7 +23,7 @@ type C = PoseidonGoldilocksConfig; /// Set this to true to cache blocks in `/tmp``. This is intended mainly for /// developer experience and not for CI testing. -const CACHE_TEST_BLOCKS: bool = false; +const CACHE_TEST_BLOCKS: bool = true; /// Get `GenerationInputs` for a simple token transfer txn, where the block has /// the given timestamp. From 65af25f8032b3b59cb2c79f7ee4c536e5b9a5057 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 00:15:10 +0800 Subject: [PATCH 18/59] cleanup: remove previous block aggregation implementation --- .../src/fixed_recursive_verifier.rs | 75 ------------------- 1 file changed, 75 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 4b277836c..cb65b4f7f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -80,7 +80,6 @@ where pub block: BlockCircuitData, /// The two-to-one block aggregation circuit, which verifies two unrelated /// block proofs. - pub two_to_one_block: TwoToOneAggCircuitData, pub two_to_one_block_binop: TwoToOneBlockBinopAggCircuitData, /// Holds chains of circuits for each table and for each initial /// `degree_bits`. @@ -533,11 +532,6 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let two_to_one_block = TwoToOneAggCircuitData::from_buffer( - &mut buffer, - gate_serializer, - generator_serializer, - )?; let two_to_one_block_binop = TwoToOneBlockBinopAggCircuitData::from_buffer( &mut buffer, gate_serializer, @@ -579,7 +573,6 @@ where aggregation, two_to_one_aggregation, block, - two_to_one_block, two_to_one_block_binop, by_table, }) @@ -671,7 +664,6 @@ where let aggregation = Self::create_aggregation_circuit(&root); let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); - let two_to_one_block = Self::create_two_to_one_block_circuit(&block); let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&block); debug_assert_eq!( &block.circuit.common, @@ -683,7 +675,6 @@ where aggregation, two_to_one_aggregation, block, - two_to_one_block, two_to_one_block_binop, by_table, } @@ -1672,49 +1663,6 @@ where ) } - /// Create a two-to-one block aggregation proof, combining two unrelated - /// block proofs into a single one. - /// - /// # Arguments - /// - /// - `proof0`: the first block proof. - /// - `proof1`: the second block proof. - /// - `pv0`: the public values associated to first proof. - /// - `pv1`: the public values associated to second proof. - /// - /// # Outputs - /// - /// This method outputs a [`ProofWithPublicInputs`]. - pub fn prove_two_to_one_block( - &self, - proof0: &ProofWithPublicInputs, - proof1: &ProofWithPublicInputs, - pv0: PublicValues, - pv1: PublicValues, - ) -> anyhow::Result> { - let mut inputs = PartialWitness::new(); - - inputs.set_proof_with_pis_target(&self.two_to_one_block.proof0, proof0); - inputs.set_proof_with_pis_target(&self.two_to_one_block.proof1, proof1); - - set_public_value_targets(&mut inputs, &self.two_to_one_block.pv0, &pv0).map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - set_public_value_targets(&mut inputs, &self.two_to_one_block.pv1, &pv1).map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - - let proof = self.two_to_one_block.circuit.prove(inputs)?; - Ok(proof) - } - - pub fn verify_two_to_one_block( - &self, - proof: &ProofWithPublicInputs, - ) -> anyhow::Result<()> { - self.two_to_one_block.circuit.verify(proof.clone()) - } - fn create_two_to_one_agg_circuit( agg: &AggregationCircuitData, ) -> TwoToOneAggCircuitData { @@ -1738,29 +1686,6 @@ where } } - fn create_two_to_one_block_circuit( - block: &BlockCircuitData, - ) -> TwoToOneAggCircuitData { - let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); - let pv0 = add_virtual_public_values(&mut builder); - let pv1 = add_virtual_public_values(&mut builder); - let block_proof0 = builder.add_virtual_proof_with_pis(&block.circuit.common); - let block_proof1 = builder.add_virtual_proof_with_pis(&block.circuit.common); - let block_verifier_data = builder.constant_verifier_data(&block.circuit.verifier_only); - builder.verify_proof::(&block_proof0, &block_verifier_data, &block.circuit.common); - builder.verify_proof::(&block_proof1, &block_verifier_data, &block.circuit.common); - - let circuit = builder.build::(); - - TwoToOneAggCircuitData { - circuit, - proof0: block_proof0, - proof1: block_proof1, - pv0, - pv1, - } - } - /// This aggregates block proofs in a manner similar to a binary operator. /// Visually: `(lhs, lhs_is_agg) BinOp (rhs, rhs_is_agg)`. /// From eff1a080b0f19215770715687b0ba7fa892c8077 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 00:28:30 +0800 Subject: [PATCH 19/59] refactor: simplify naming --- .../src/fixed_recursive_verifier.rs | 79 +++++++++---------- evm_arithmetization/tests/two_to_one_agg.rs | 35 ++++---- 2 files changed, 54 insertions(+), 60 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index cb65b4f7f..d1d3a0f65 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -80,7 +80,7 @@ where pub block: BlockCircuitData, /// The two-to-one block aggregation circuit, which verifies two unrelated /// block proofs. - pub two_to_one_block_binop: TwoToOneBlockBinopAggCircuitData, + pub two_to_one_block: TwoToOneBlockCircuitData, /// Holds chains of circuits for each table and for each initial /// `degree_bits`. pub by_table: [RecursiveCircuitsForTable; NUM_TABLES], @@ -364,20 +364,20 @@ where /// Data for the two-to-one block circuit, which is used to generate a /// proof of two unrelated proofs. #[derive(Eq, PartialEq, Debug)] -pub struct TwoToOneBlockBinopAggCircuitData +pub struct TwoToOneBlockCircuitData where F: RichField + Extendable, C: GenericConfig, { pub circuit: CircuitData, - lhs: BlockBinopAggChildTarget, - rhs: BlockBinopAggChildTarget, + lhs: TwoToOneBlockChildTarget, + rhs: TwoToOneBlockChildTarget, mix_pv_hash: HashOutTarget, dummy_pis: Vec, cyclic_vk: VerifierCircuitTarget, } -impl TwoToOneBlockBinopAggCircuitData +impl TwoToOneBlockCircuitData where F: RichField + Extendable, C: GenericConfig, @@ -403,8 +403,8 @@ where generator_serializer: &dyn WitnessGeneratorSerializer, ) -> IoResult { let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; - let lhs = BlockBinopAggChildTarget::from_buffer(buffer)?; - let rhs = BlockBinopAggChildTarget::from_buffer(buffer)?; + let lhs = TwoToOneBlockChildTarget::from_buffer(buffer)?; + let rhs = TwoToOneBlockChildTarget::from_buffer(buffer)?; let mix_pv_hash = buffer.read_target_hash()?; let dummy_pis = buffer.read_target_vec()?; let cyclic_vk = buffer.read_target_verifier_circuit()?; @@ -420,13 +420,13 @@ where } #[derive(Eq, PartialEq, Debug)] -struct BlockBinopAggChildTarget { +struct TwoToOneBlockChildTarget { is_agg: BoolTarget, agg_proof: ProofWithPublicInputsTarget, block_proof: ProofWithPublicInputsTarget, } -impl BlockBinopAggChildTarget { +impl TwoToOneBlockChildTarget { 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)?; @@ -532,7 +532,7 @@ where )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let two_to_one_block_binop = TwoToOneBlockBinopAggCircuitData::from_buffer( + let two_to_one_block = TwoToOneBlockCircuitData::from_buffer( &mut buffer, gate_serializer, generator_serializer, @@ -573,7 +573,7 @@ where aggregation, two_to_one_aggregation, block, - two_to_one_block_binop, + two_to_one_block, by_table, }) } @@ -664,18 +664,15 @@ where let aggregation = Self::create_aggregation_circuit(&root); let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); - let two_to_one_block_binop = Self::create_two_to_one_block_circuit_binop(&block); - debug_assert_eq!( - &block.circuit.common, - &two_to_one_block_binop.circuit.common - ); + let two_to_one_block = Self::create_two_to_one_block_circuit(&block); + debug_assert_eq!(&block.circuit.common, &two_to_one_block.circuit.common); Self { root, aggregation, two_to_one_aggregation, block, - two_to_one_block_binop, + two_to_one_block, by_table, } } @@ -1699,7 +1696,7 @@ where /// # Outputs /// /// This method outputs a [`ProofWithPublicInputs`]. - pub fn prove_two_to_one_block_binop( + pub fn prove_two_to_one_block( &self, lhs: &ProofWithPublicInputs, lhs_is_agg: bool, @@ -1708,36 +1705,36 @@ where ) -> anyhow::Result> { let mut witness = PartialWitness::new(); - let dummy_pis = &self.two_to_one_block_binop.dummy_pis; + let dummy_pis = &self.two_to_one_block.dummy_pis; witness.set_target_arr(&dummy_pis, &vec![F::ZERO; dummy_pis.len()]); Self::set_dummy_if_necessary( - &self.two_to_one_block_binop.lhs, + &self.two_to_one_block.lhs, lhs_is_agg, - &self.two_to_one_block_binop.circuit, + &self.two_to_one_block.circuit, &mut witness, &lhs, ); Self::set_dummy_if_necessary( - &self.two_to_one_block_binop.rhs, + &self.two_to_one_block.rhs, rhs_is_agg, - &self.two_to_one_block_binop.circuit, + &self.two_to_one_block.circuit, &mut witness, &rhs, ); witness.set_verifier_data_target( - &self.two_to_one_block_binop.cyclic_vk, - &self.two_to_one_block_binop.circuit.verifier_only, + &self.two_to_one_block.cyclic_vk, + &self.two_to_one_block.circuit.verifier_only, ); let lhs_pv_hash = C::InnerHasher::hash_no_pad(&lhs.public_inputs); let rhs_pv_hash = C::InnerHasher::hash_no_pad(&rhs.public_inputs); let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); - witness.set_hash_target(self.two_to_one_block_binop.mix_pv_hash, mix_pv_hash); + witness.set_hash_target(self.two_to_one_block.mix_pv_hash, mix_pv_hash); - let proof = self.two_to_one_block_binop.circuit.prove(witness)?; + let proof = self.two_to_one_block.circuit.prove(witness)?; Ok(proof) } @@ -1745,17 +1742,17 @@ where /// /// # Arguments /// - /// - `proof`: The proof generated with `prove_two_to_one_block_binop`. + /// - `proof`: The proof generated with `prove_two_to_one_block`. /// /// # Outputs /// /// Returns whether the proof was valid or not. - pub fn verify_two_to_one_block_binop( + pub fn verify_two_to_one_block( &self, proof: &ProofWithPublicInputs, ) -> anyhow::Result<()> { - self.two_to_one_block_binop.circuit.verify(proof.clone()); - let verifier_data = &self.two_to_one_block_binop.circuit.verifier_data(); + self.two_to_one_block.circuit.verify(proof.clone()); + let verifier_data = &self.two_to_one_block.circuit.verifier_data(); check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } @@ -1770,11 +1767,11 @@ where /// /// # Outputs /// - /// Returns a `BlockBinopAggChildTarget` object. - fn add_block_agg_child( + /// Returns a [`TwoToOneBlockChildTarget`] object. + fn add_block_child( builder: &mut CircuitBuilder, block_circuit_data: &BlockCircuitData, - ) -> BlockBinopAggChildTarget { + ) -> TwoToOneBlockChildTarget { let count_public_inputs = builder.num_public_inputs(); let block_common = &block_circuit_data.circuit.common; let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); @@ -1792,7 +1789,7 @@ where ) .expect("Failed to build cyclic recursion circuit"); assert_eq!(count_public_inputs, builder.num_public_inputs()); - BlockBinopAggChildTarget { + TwoToOneBlockChildTarget { is_agg, agg_proof, block_proof, @@ -1800,9 +1797,9 @@ where } } - fn create_two_to_one_block_circuit_binop( + fn create_two_to_one_block_circuit( block: &BlockCircuitData, - ) -> TwoToOneBlockBinopAggCircuitData + ) -> TwoToOneBlockCircuitData where F: RichField + Extendable, C: GenericConfig, @@ -1822,8 +1819,8 @@ where // [`add_verifier_data_public_inputs`]. let count_public_inputs = builder.num_public_inputs(); - let lhs = Self::add_block_agg_child(&mut builder, &block); - let rhs = Self::add_block_agg_child(&mut builder, &block); + let lhs = Self::add_block_child(&mut builder, &block); + let rhs = Self::add_block_child(&mut builder, &block); let lhs_public_values = lhs.public_values(&mut builder); let rhs_public_values = rhs.public_values(&mut builder); @@ -1848,7 +1845,7 @@ where ); let circuit = builder.build::(); - TwoToOneBlockBinopAggCircuitData { + TwoToOneBlockCircuitData { circuit, lhs, rhs, @@ -1901,7 +1898,7 @@ where /// 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: &BlockBinopAggChildTarget, + agg_child: &TwoToOneBlockChildTarget, is_agg: bool, circuit: &CircuitData, agg_inputs: &mut PartialWitness, diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index e15ca5da0..4f0eec61f 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -551,15 +551,14 @@ fn test_block_aggregation_binop_4_blocks() -> anyhow::Result<()> { log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; - let aggproof01 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; - all_circuits.verify_two_to_one_block_binop(&aggproof01)?; + let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block(&aggproof01)?; - let aggproof23 = all_circuits.prove_two_to_one_block_binop(&bp[2], false, &bp[3], false)?; - all_circuits.verify_two_to_one_block_binop(&aggproof23)?; + let aggproof23 = all_circuits.prove_two_to_one_block(&bp[2], false, &bp[3], false)?; + all_circuits.verify_two_to_one_block(&aggproof23)?; - let aggproof0123 = - all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &aggproof23, true)?; - all_circuits.verify_two_to_one_block_binop(&aggproof0123)?; + let aggproof0123 = all_circuits.prove_two_to_one_block(&aggproof01, true, &aggproof23, true)?; + all_circuits.verify_two_to_one_block(&aggproof0123)?; Ok(()) } @@ -595,8 +594,8 @@ fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; - let aggproof_42_42 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; - all_circuits.verify_two_to_one_block_binop(&aggproof_42_42)?; + let aggproof_42_42 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block(&aggproof_42_42)?; Ok(()) } @@ -640,12 +639,11 @@ fn test_block_aggregation_binop_foldleft() -> anyhow::Result<()> { log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; - let aggproof01 = all_circuits.prove_two_to_one_block_binop(&bp[0], false, &bp[1], false)?; - all_circuits.verify_two_to_one_block_binop(&aggproof01)?; + let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block(&aggproof01)?; - let aggproof012 = - all_circuits.prove_two_to_one_block_binop(&aggproof01, true, &bp[2], false)?; - all_circuits.verify_two_to_one_block_binop(&aggproof012)?; + let aggproof012 = all_circuits.prove_two_to_one_block(&aggproof01, true, &bp[2], false)?; + all_circuits.verify_two_to_one_block(&aggproof012)?; Ok(()) } @@ -686,12 +684,11 @@ fn test_block_aggregation_binop_foldright() -> anyhow::Result<()> { log::info!("Meta Stage 3: Aggregate block proofs"); let bp = unrelated_block_proofs; - let aggproof12 = all_circuits.prove_two_to_one_block_binop(&bp[1], false, &bp[2], false)?; - all_circuits.verify_two_to_one_block_binop(&aggproof12)?; + let aggproof12 = all_circuits.prove_two_to_one_block(&bp[1], false, &bp[2], false)?; + all_circuits.verify_two_to_one_block(&aggproof12)?; - let aggproof012 = - all_circuits.prove_two_to_one_block_binop(&bp[0], false, &aggproof12, true)?; - all_circuits.verify_two_to_one_block_binop(&aggproof012)?; + let aggproof012 = all_circuits.prove_two_to_one_block(&bp[0], false, &aggproof12, true)?; + all_circuits.verify_two_to_one_block(&aggproof012)?; Ok(()) } From ccc10519e61baa71ebcee5b919886edafb00aec2 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 00:58:41 +0800 Subject: [PATCH 20/59] refactor: improve naming --- .../src/fixed_recursive_verifier.rs | 43 ++++++++++++------- evm_arithmetization/tests/two_to_one_agg.rs | 1 - 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index d1d3a0f65..0602d756e 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1683,7 +1683,8 @@ where } } - /// This aggregates block proofs in a manner similar to a binary operator. + /// Aggregates two block or aggregation proofs in a manner similar to a + /// binary operator. /// Visually: `(lhs, lhs_is_agg) BinOp (rhs, rhs_is_agg)`. /// /// # Arguments @@ -1695,7 +1696,7 @@ where /// /// # Outputs /// - /// This method outputs a [`ProofWithPublicInputs`]. + /// Returns a [`ProofWithPublicInputs`]. pub fn prove_two_to_one_block( &self, lhs: &ProofWithPublicInputs, @@ -1738,7 +1739,7 @@ where Ok(proof) } - /// Method to verify an existing proof + /// Verifies an existing block aggregation proof /// /// # Arguments /// @@ -1756,8 +1757,8 @@ where check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } - /// Helper method to construct one of the two circuits representing - /// unrelated proofs + /// Setup a new part of a circuit to handle a new unrelated proof. + /// Helper method for [`create_two_to_one_block_circuit`]. /// /// # Arguments /// @@ -1768,7 +1769,7 @@ where /// # Outputs /// /// Returns a [`TwoToOneBlockChildTarget`] object. - fn add_block_child( + fn add_two_to_one_block_child( builder: &mut CircuitBuilder, block_circuit_data: &BlockCircuitData, ) -> TwoToOneBlockChildTarget { @@ -1776,7 +1777,8 @@ where let block_common = &block_circuit_data.circuit.common; let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); let is_agg = builder.add_virtual_bool_target_safe(); - // block_common should be use for agg_proof because they are similar + // Note: `block_common` should also be used for `agg_proof` here because they + // are equal, and `agg_proof` is not available yet. let agg_proof = builder.add_virtual_proof_with_pis(block_common); let block_proof = builder.add_virtual_proof_with_pis(block_common); builder @@ -1793,23 +1795,34 @@ where is_agg, agg_proof, block_proof, - // pv_hash, } } + /// Create two-to-one block aggregation circuit. + /// + /// # Arguments + /// + /// - `block_circuit`: circuit data for the block circuit, that constitues + /// the base case for aggregation. + /// + /// # Outputs + /// + /// Returns a [`TwoToOneBlockCircuitData`]. fn create_two_to_one_block_circuit( - block: &BlockCircuitData, + block_circuit: &BlockCircuitData, ) -> TwoToOneBlockCircuitData where F: RichField + Extendable, C: GenericConfig, C::Hasher: AlgebraicHasher, { - let mut builder = CircuitBuilder::::new(block.circuit.common.config.clone()); + let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); let mut dummy_pis = vec![]; - // The magic numbers derived from failing assertion at end of this function. - while builder.num_public_inputs() < block.circuit.common.num_public_inputs - (2337 - 2269) { + // The magic numbers were derived from the assertion at end of this function. + while builder.num_public_inputs() + < block_circuit.circuit.common.num_public_inputs - (2337 - 2269) + { let target = builder.add_virtual_public_input(); dummy_pis.push(target); } @@ -1819,8 +1832,8 @@ where // [`add_verifier_data_public_inputs`]. let count_public_inputs = builder.num_public_inputs(); - let lhs = Self::add_block_child(&mut builder, &block); - let rhs = Self::add_block_child(&mut builder, &block); + let lhs = Self::add_two_to_one_block_child(&mut builder, &block_circuit); + let rhs = Self::add_two_to_one_block_child(&mut builder, &block_circuit); let lhs_public_values = lhs.public_values(&mut builder); let rhs_public_values = rhs.public_values(&mut builder); @@ -1839,7 +1852,7 @@ where ); debug_assert_eq!( - block.circuit.common.num_public_inputs, + block_circuit.circuit.common.num_public_inputs, builder.num_public_inputs(), "The block aggregation circuit and the block circuit must agree on the number of public inputs." ); diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 4f0eec61f..522d10534 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -500,7 +500,6 @@ fn get_test_block_proof_cached( fs::write(path.clone(), &raw_block)?; log::info!("Succesfully wrote blockproof to {:#?}", path); - // Todo: move to file with `from_bytes` let written_block = fs::read(path.clone())?; assert_eq!(&raw_block, &written_block); let restored_block = From 7a9b1b409fa274a0e668c39d83810ae8989c3eb5 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 01:04:34 +0800 Subject: [PATCH 21/59] cleanup: remove custom build config --- .cargo/config.toml | 15 --------------- Cargo.toml | 23 ----------------------- 2 files changed, 38 deletions(-) delete mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 03b0dd41d..000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,15 +0,0 @@ -[build] -rustflags = ["-C", "target-cpu=native"] - -[env] -RUST_BACKTRACE = "1" -RUST_TEST_NOCAPTURE = "1" - -[term] -verbose = true -color = 'auto' - -[target.x86_64-unknown-linux-gnu] -linker = "clang" -#rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-fuse-ld=/usr/bin/mold", "-Z", "threads=2", "-C", "debuginfo=2"] -rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-fuse-ld=/usr/bin/mold", "-C", "debuginfo=2"] diff --git a/Cargo.toml b/Cargo.toml index aca640097..30ee73a25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,26 +108,3 @@ plonky2 = "0.2.2" plonky2_maybe_rayon = "0.2.0" plonky2_util = "0.2.0" starky = "0.4.0" - -[profile.release] -debug=true -incremental=true -debug-assertions=true -lto=false -overflow-checks=false - -[profile.test] -opt-level=3 -debug=true -incremental=true -debug-assertions=true -lto=false -overflow-checks=false - -[profile.dev] -opt-level=3 -debug=true -incremental=true -debug-assertions=true -lto=false -overflow-checks=false From 05e8381ea2ff00ac56372e54b10e617885af5a77 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 01:14:26 +0800 Subject: [PATCH 22/59] cleanup: undo this change --- evm_arithmetization/src/proof.rs | 2 +- evm_arithmetization/tests/two_to_one_agg.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 27ee09141..338eda2fa 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -97,7 +97,7 @@ impl PublicValues { } /// Trie hashes. -#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct TrieRoots { /// State trie hash. pub state_root: H256, diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 522d10534..756cd417f 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -172,7 +172,7 @@ fn dummy_inputs(inputs: &GenerationInputs) -> GenerationInputs { )), storage_tries: vec![], }, - trie_roots_after: inputs.trie_roots_after, + trie_roots_after: inputs.trie_roots_after.clone(), checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, contract_code: Default::default(), block_metadata: inputs.block_metadata.clone(), From 0fb7c06fef2c390b3fb76e880b0704d56d0cb4fd Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 01:20:09 +0800 Subject: [PATCH 23/59] cleanup: obey Clippy --- evm_arithmetization/src/fixed_recursive_verifier.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 0602d756e..5819878fc 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1707,14 +1707,14 @@ where let mut witness = PartialWitness::new(); let dummy_pis = &self.two_to_one_block.dummy_pis; - witness.set_target_arr(&dummy_pis, &vec![F::ZERO; dummy_pis.len()]); + witness.set_target_arr(dummy_pis, &vec![F::ZERO; dummy_pis.len()]); Self::set_dummy_if_necessary( &self.two_to_one_block.lhs, lhs_is_agg, &self.two_to_one_block.circuit, &mut witness, - &lhs, + lhs, ); Self::set_dummy_if_necessary( @@ -1722,7 +1722,7 @@ where rhs_is_agg, &self.two_to_one_block.circuit, &mut witness, - &rhs, + rhs, ); witness.set_verifier_data_target( @@ -1832,8 +1832,8 @@ where // [`add_verifier_data_public_inputs`]. let count_public_inputs = builder.num_public_inputs(); - let lhs = Self::add_two_to_one_block_child(&mut builder, &block_circuit); - let rhs = Self::add_two_to_one_block_child(&mut builder, &block_circuit); + let lhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); + let rhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); let lhs_public_values = lhs.public_values(&mut builder); let rhs_public_values = rhs.public_values(&mut builder); From 47e2622e6d568a71d408f5764f35758c995ae910 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 16:42:15 +0800 Subject: [PATCH 24/59] cleanup: remove two-to-one aggregation --- .../src/fixed_recursive_verifier.rs | 77 ------- evm_arithmetization/tests/two_to_one_agg.rs | 208 ------------------ 2 files changed, 285 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 5819878fc..4db4e8f49 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -72,9 +72,6 @@ where /// The aggregation circuit, which verifies two proofs that can either be /// root or aggregation proofs. pub aggregation: AggregationCircuitData, - /// The two-to-one aggregation circuit, which verifies two unrelated - /// aggregation proofs. - pub two_to_one_aggregation: TwoToOneAggCircuitData, /// The block circuit, which verifies an aggregation root proof and an /// optional previous block proof. pub block: BlockCircuitData, @@ -525,11 +522,6 @@ where gate_serializer, generator_serializer, )?; - let two_to_one_aggregation = TwoToOneAggCircuitData::from_buffer( - &mut buffer, - gate_serializer, - generator_serializer, - )?; let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; let two_to_one_block = TwoToOneBlockCircuitData::from_buffer( @@ -571,7 +563,6 @@ where Ok(Self { root, aggregation, - two_to_one_aggregation, block, two_to_one_block, by_table, @@ -662,7 +653,6 @@ where ]; let root = Self::create_root_circuit(&by_table, stark_config); let aggregation = Self::create_aggregation_circuit(&root); - let two_to_one_aggregation = Self::create_two_to_one_agg_circuit(&aggregation); let block = Self::create_block_circuit(&aggregation); let two_to_one_block = Self::create_two_to_one_block_circuit(&block); debug_assert_eq!(&block.circuit.common, &two_to_one_block.circuit.common); @@ -670,7 +660,6 @@ where Self { root, aggregation, - two_to_one_aggregation, block, two_to_one_block, by_table, @@ -1447,49 +1436,6 @@ where ) } - /// Create a two-to-one aggregation proof, combining two unrelated - /// aggregation proofs into a single one. - /// - /// # Arguments - /// - /// - `proof0`: the first aggregation proof. - /// - `proof1`: the second aggregation proof. - /// - `pv0`: the public values associated to first proof. - /// - `pv1`: the public values associated to second proof. - /// - /// # Outputs - /// - /// This method outputs a [`ProofWithPublicInputs`]. - pub fn prove_two_to_one_aggregation( - &self, - proof0: &ProofWithPublicInputs, - proof1: &ProofWithPublicInputs, - pv0: PublicValues, - pv1: PublicValues, - ) -> anyhow::Result> { - let mut inputs = PartialWitness::new(); - - inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.proof0, proof0); - inputs.set_proof_with_pis_target(&self.two_to_one_aggregation.proof1, proof1); - - set_public_value_targets(&mut inputs, &self.two_to_one_aggregation.pv0, &pv0).map_err( - |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), - )?; - set_public_value_targets(&mut inputs, &self.two_to_one_aggregation.pv1, &pv1).map_err( - |_| anyhow::Error::msg("Invalid conversion when setting public values targets."), - )?; - - let proof = self.two_to_one_aggregation.circuit.prove(inputs)?; - Ok(proof) - } - - pub fn verify_two_to_one_aggregation( - &self, - proof: &ProofWithPublicInputs, - ) -> anyhow::Result<()> { - self.two_to_one_aggregation.circuit.verify(proof.clone()) - } - /// Create a final block proof, once all transactions of a given block have /// been combined into a single aggregation proof. /// @@ -1660,29 +1606,6 @@ where ) } - fn create_two_to_one_agg_circuit( - agg: &AggregationCircuitData, - ) -> TwoToOneAggCircuitData { - let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); - let pv0 = add_virtual_public_values(&mut builder); - let pv1 = add_virtual_public_values(&mut builder); - let agg_proof0 = builder.add_virtual_proof_with_pis(&agg.circuit.common); - let agg_proof1 = builder.add_virtual_proof_with_pis(&agg.circuit.common); - let agg_verifier_data = builder.constant_verifier_data(&agg.circuit.verifier_only); - builder.verify_proof::(&agg_proof0, &agg_verifier_data, &agg.circuit.common); - builder.verify_proof::(&agg_proof1, &agg_verifier_data, &agg.circuit.common); - - let circuit = builder.build::(); - - TwoToOneAggCircuitData { - circuit, - proof0: agg_proof0, - proof1: agg_proof1, - pv0, - pv1, - } - } - /// Aggregates two block or aggregation proofs in a manner similar to a /// binary operator. /// Visually: `(lhs, lhs_is_agg) BinOp (rhs, rhs_is_agg)`. diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs index 756cd417f..5b0e9e941 100644 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ b/evm_arithmetization/tests/two_to_one_agg.rs @@ -25,214 +25,6 @@ type C = PoseidonGoldilocksConfig; /// developer experience and not for CI testing. const CACHE_TEST_BLOCKS: bool = true; -/// Get `GenerationInputs` for a simple token transfer txn, where the block has -/// the given timestamp. -fn simple_transfer(timestamp: u64) -> anyhow::Result { - init_logger(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - let to_account_before = AccountRlp::default(); - - let state_trie_before = Node::Leaf { - nibbles: sender_nibbles, - value: rlp::encode(&sender_account_before).to_vec(), - } - .into(); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], - }; - - // Generated using a little py-evm script. - let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); - let value = U256::from(100u32); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: timestamp.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: 21032.into(), - block_bloom: [0.into(); 8], - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - - let expected_state_trie_after: HashedPartialTrie = { - let txdata_gas = 2 * 16; - let gas_used = 21_000 + txdata_gas; - - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value - gas_used * 10, - nonce: sender_account_before.nonce + 1, - ..sender_account_before - }; - let to_account_after = AccountRlp { - balance: value, - ..to_account_before - }; - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_after).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_after).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - .into() - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 21032.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - )?; - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 21032.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - Ok(inputs) -} - -fn dummy_inputs(inputs: &GenerationInputs) -> GenerationInputs { - GenerationInputs { - txn_number_before: inputs.txn_number_before + 1, - gas_used_before: inputs.gas_used_after, - gas_used_after: inputs.gas_used_after, - signed_txn: None, - withdrawals: vec![], - tries: TrieInputs { - state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), - transactions_trie: HashedPartialTrie::from(Node::Hash( - inputs.trie_roots_after.transactions_root, - )), - receipts_trie: HashedPartialTrie::from(Node::Hash( - inputs.trie_roots_after.receipts_root, - )), - storage_tries: vec![], - }, - trie_roots_after: inputs.trie_roots_after.clone(), - checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, - contract_code: Default::default(), - block_metadata: inputs.block_metadata.clone(), - block_hashes: inputs.block_hashes.clone(), - } -} - -#[test] -#[ignore] -fn test_two_to_one_aggregation() -> anyhow::Result<()> { - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let inputs0 = simple_transfer(1)?; - let dummy0 = dummy_inputs(&inputs0); - let inputs1 = simple_transfer(2)?; - let dummy1 = dummy_inputs(&inputs1); - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - let (root_proof0, pv0) = - all_circuits.prove_root(&all_stark, &config, inputs0, &mut timing, None)?; - all_circuits.verify_root(root_proof0.clone())?; - let (dummy_proof0, dummy_pv0) = - all_circuits.prove_root(&all_stark, &config, dummy0, &mut timing, None)?; - all_circuits.verify_root(dummy_proof0.clone())?; - let (root_proof1, pv1) = - all_circuits.prove_root(&all_stark, &config, inputs1, &mut timing, None)?; - all_circuits.verify_root(root_proof1.clone())?; - let (dummy_proof1, dummy_pv1) = - all_circuits.prove_root(&all_stark, &config, dummy1, &mut timing, None)?; - all_circuits.verify_root(dummy_proof1.clone())?; - - let (agg_proof0, pv0) = all_circuits.prove_aggregation( - false, - &root_proof0, - pv0, - false, - &dummy_proof0, - dummy_pv0, - )?; - let (agg_proof1, pv1) = all_circuits.prove_aggregation( - false, - &root_proof1, - pv1, - false, - &dummy_proof1, - dummy_pv1, - )?; - - let proof = all_circuits.prove_two_to_one_aggregation(&agg_proof0, &agg_proof1, pv0, pv1)?; - all_circuits.verify_two_to_one_aggregation(&proof) -} - fn eth_to_wei(eth: U256) -> U256 { // 1 ether = 10^18 wei. eth * U256::from(10).pow(18.into()) From 9569a4c5e9f04180964864fffe05a0d2781afbeb Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 17:18:38 +0800 Subject: [PATCH 25/59] review: resolve small issues --- evm_arithmetization/src/fixed_recursive_verifier.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 4db4e8f49..c06746ae1 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -305,7 +305,7 @@ where } /// Data for the two-to-one aggregation circuit, which is used to generate a -/// proof of two unrelated proofs. +/// proof of two unrelated block proofs. #[derive(Eq, PartialEq, Debug)] pub struct TwoToOneAggCircuitData where @@ -826,6 +826,7 @@ where let mut builder = CircuitBuilder::::new(root.circuit.common.config.clone()); let public_values = add_virtual_public_values(&mut builder); let cyclic_vk = builder.add_verifier_data_public_inputs(); + #[cfg(debug_assertions)] let count_public_inputs = builder.num_public_inputs(); let lhs = Self::add_agg_child(&mut builder, root); let rhs = Self::add_agg_child(&mut builder, root); @@ -1662,7 +1663,7 @@ where Ok(proof) } - /// Verifies an existing block aggregation proof + /// Verifies an existing block aggregation proof. /// /// # Arguments /// @@ -1696,6 +1697,7 @@ where builder: &mut CircuitBuilder, block_circuit_data: &BlockCircuitData, ) -> TwoToOneBlockChildTarget { + #[cfg(debug_assertions)] let count_public_inputs = builder.num_public_inputs(); let block_common = &block_circuit_data.circuit.common; let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); @@ -1713,7 +1715,7 @@ where block_common, ) .expect("Failed to build cyclic recursion circuit"); - assert_eq!(count_public_inputs, builder.num_public_inputs()); + debug_assert_eq!(count_public_inputs, builder.num_public_inputs()); TwoToOneBlockChildTarget { is_agg, agg_proof, @@ -1753,6 +1755,7 @@ where let cyclic_vk = builder.add_verifier_data_public_inputs(); // Avoid accidentally adding public inputs after calling // [`add_verifier_data_public_inputs`]. + #[cfg(debug_assertions)] let count_public_inputs = builder.num_public_inputs(); let lhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); @@ -1771,7 +1774,7 @@ where debug_assert_eq!( count_public_inputs, builder.num_public_inputs(), - "Public inputs were registered after called `add_verifier_data_public_inputs`" + "Public inputs were registered after calling `add_verifier_data_public_inputs`." ); debug_assert_eq!( @@ -1849,7 +1852,7 @@ where agg_inputs, &agg_child.agg_proof, proof, - ) + ); } agg_inputs.set_proof_with_pis_target(&agg_child.block_proof, proof); } From 5ee277a8de07374564cea175945deb066f0c81ad Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 17:26:41 +0800 Subject: [PATCH 26/59] fixup! cleanup: remove two-to-one aggregation --- .../src/fixed_recursive_verifier.rs | 54 ------------------- 1 file changed, 54 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index c06746ae1..9e2467a1e 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -304,60 +304,6 @@ where } } -/// Data for the two-to-one aggregation circuit, which is used to generate a -/// proof of two unrelated block proofs. -#[derive(Eq, PartialEq, Debug)] -pub struct TwoToOneAggCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - pub circuit: CircuitData, - proof0: ProofWithPublicInputsTarget, - proof1: ProofWithPublicInputsTarget, - pv0: PublicValuesTarget, - pv1: PublicValuesTarget, -} - -impl TwoToOneAggCircuitData -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.proof0)?; - buffer.write_target_proof_with_public_inputs(&self.proof1)?; - self.pv0.to_buffer(buffer)?; - self.pv1.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 agg_proof0 = buffer.read_target_proof_with_public_inputs()?; - let agg_proof1 = buffer.read_target_proof_with_public_inputs()?; - let pv0 = PublicValuesTarget::from_buffer(buffer)?; - let pv1 = PublicValuesTarget::from_buffer(buffer)?; - Ok(Self { - circuit, - proof0: agg_proof0, - proof1: agg_proof1, - pv0, - pv1, - }) - } -} - /// Data for the two-to-one block circuit, which is used to generate a /// proof of two unrelated proofs. #[derive(Eq, PartialEq, Debug)] From 5a75191f8c003aa2c5a395a7eb43b812f53e40c8 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 17:31:34 +0800 Subject: [PATCH 27/59] review: make order consistent --- .../src/fixed_recursive_verifier.rs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 9e2467a1e..3d7a9e967 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -980,6 +980,120 @@ where } } + /// Create two-to-one block aggregation circuit. + /// + /// # Arguments + /// + /// - `block_circuit`: circuit data for the block circuit, that constitues + /// the base case for aggregation. + /// + /// # Outputs + /// + /// Returns a [`TwoToOneBlockCircuitData`]. + fn create_two_to_one_block_circuit( + block_circuit: &BlockCircuitData, + ) -> TwoToOneBlockCircuitData + where + F: RichField + Extendable, + C: GenericConfig, + C::Hasher: AlgebraicHasher, + { + let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); + + let mut dummy_pis = vec![]; + // The magic numbers were derived from the assertion at end of this function. + while builder.num_public_inputs() + < block_circuit.circuit.common.num_public_inputs - (2337 - 2269) + { + let target = builder.add_virtual_public_input(); + dummy_pis.push(target); + } + + let cyclic_vk = builder.add_verifier_data_public_inputs(); + // Avoid accidentally adding public inputs after calling + // [`add_verifier_data_public_inputs`]. + #[cfg(debug_assertions)] + let count_public_inputs = builder.num_public_inputs(); + + let lhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); + let rhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); + + let lhs_public_values = lhs.public_values(&mut builder); + let rhs_public_values = rhs.public_values(&mut builder); + + let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values); + let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values); + let mut mix_vec = vec![]; + mix_vec.extend(&lhs_pv_hash.elements); + mix_vec.extend(&rhs_pv_hash.elements); + let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); + + debug_assert_eq!( + count_public_inputs, + builder.num_public_inputs(), + "Public inputs were registered after calling `add_verifier_data_public_inputs`." + ); + + debug_assert_eq!( + block_circuit.circuit.common.num_public_inputs, + builder.num_public_inputs(), + "The block aggregation circuit and the block circuit must agree on the number of public inputs." + ); + + let circuit = builder.build::(); + TwoToOneBlockCircuitData { + circuit, + lhs, + rhs, + mix_pv_hash, + dummy_pis, + cyclic_vk, + } + } + + + /// Setup a new part of a circuit to handle a new unrelated proof. + /// Helper method for [`create_two_to_one_block_circuit`]. + /// + /// # Arguments + /// + /// - `builder`: The circuit builder object. + /// - `block_circuit_data`: Circuit data describing the blocks that can be + /// aggregated. + /// + /// # Outputs + /// + /// Returns a [`TwoToOneBlockChildTarget`] object. + fn add_two_to_one_block_child( + builder: &mut CircuitBuilder, + block_circuit_data: &BlockCircuitData, + ) -> TwoToOneBlockChildTarget { + #[cfg(debug_assertions)] + let count_public_inputs = builder.num_public_inputs(); + let block_common = &block_circuit_data.circuit.common; + let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); + let is_agg = builder.add_virtual_bool_target_safe(); + // Note: `block_common` should also be used for `agg_proof` here because they + // are equal, and `agg_proof` is not available yet. + let agg_proof = builder.add_virtual_proof_with_pis(block_common); + let block_proof = builder.add_virtual_proof_with_pis(block_common); + builder + .conditionally_verify_cyclic_proof::( + is_agg, + &agg_proof, + &block_proof, + &block_vk, + block_common, + ) + .expect("Failed to build cyclic recursion circuit"); + debug_assert_eq!(count_public_inputs, builder.num_public_inputs()); + TwoToOneBlockChildTarget { + is_agg, + agg_proof, + block_proof, + } + } + /// Connect the 256 block hashes between two blocks fn connect_block_hashes( builder: &mut CircuitBuilder, From 642161c42f43a8fb4eb4f9b657e97d8d1273aa88 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 18:31:35 +0800 Subject: [PATCH 28/59] fixup! review: make order consistent --- .../src/fixed_recursive_verifier.rs | 155 ------------------ 1 file changed, 155 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 3d7a9e967..259c00070 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1051,49 +1051,6 @@ where } } - - /// Setup a new part of a circuit to handle a new unrelated proof. - /// Helper method for [`create_two_to_one_block_circuit`]. - /// - /// # Arguments - /// - /// - `builder`: The circuit builder object. - /// - `block_circuit_data`: Circuit data describing the blocks that can be - /// aggregated. - /// - /// # Outputs - /// - /// Returns a [`TwoToOneBlockChildTarget`] object. - fn add_two_to_one_block_child( - builder: &mut CircuitBuilder, - block_circuit_data: &BlockCircuitData, - ) -> TwoToOneBlockChildTarget { - #[cfg(debug_assertions)] - let count_public_inputs = builder.num_public_inputs(); - let block_common = &block_circuit_data.circuit.common; - let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); - let is_agg = builder.add_virtual_bool_target_safe(); - // Note: `block_common` should also be used for `agg_proof` here because they - // are equal, and `agg_proof` is not available yet. - let agg_proof = builder.add_virtual_proof_with_pis(block_common); - let block_proof = builder.add_virtual_proof_with_pis(block_common); - builder - .conditionally_verify_cyclic_proof::( - is_agg, - &agg_proof, - &block_proof, - &block_vk, - block_common, - ) - .expect("Failed to build cyclic recursion circuit"); - debug_assert_eq!(count_public_inputs, builder.num_public_inputs()); - TwoToOneBlockChildTarget { - is_agg, - agg_proof, - block_proof, - } - } - /// Connect the 256 block hashes between two blocks fn connect_block_hashes( builder: &mut CircuitBuilder, @@ -1741,118 +1698,6 @@ where check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } - /// Setup a new part of a circuit to handle a new unrelated proof. - /// Helper method for [`create_two_to_one_block_circuit`]. - /// - /// # Arguments - /// - /// - `builder`: The circuit builder object. - /// - `block_circuit_data`: Circuit data describing the blocks that can be - /// aggregated. - /// - /// # Outputs - /// - /// Returns a [`TwoToOneBlockChildTarget`] object. - fn add_two_to_one_block_child( - builder: &mut CircuitBuilder, - block_circuit_data: &BlockCircuitData, - ) -> TwoToOneBlockChildTarget { - #[cfg(debug_assertions)] - let count_public_inputs = builder.num_public_inputs(); - let block_common = &block_circuit_data.circuit.common; - let block_vk = builder.constant_verifier_data(&block_circuit_data.circuit.verifier_only); - let is_agg = builder.add_virtual_bool_target_safe(); - // Note: `block_common` should also be used for `agg_proof` here because they - // are equal, and `agg_proof` is not available yet. - let agg_proof = builder.add_virtual_proof_with_pis(block_common); - let block_proof = builder.add_virtual_proof_with_pis(block_common); - builder - .conditionally_verify_cyclic_proof::( - is_agg, - &agg_proof, - &block_proof, - &block_vk, - block_common, - ) - .expect("Failed to build cyclic recursion circuit"); - debug_assert_eq!(count_public_inputs, builder.num_public_inputs()); - TwoToOneBlockChildTarget { - is_agg, - agg_proof, - block_proof, - } - } - - /// Create two-to-one block aggregation circuit. - /// - /// # Arguments - /// - /// - `block_circuit`: circuit data for the block circuit, that constitues - /// the base case for aggregation. - /// - /// # Outputs - /// - /// Returns a [`TwoToOneBlockCircuitData`]. - fn create_two_to_one_block_circuit( - block_circuit: &BlockCircuitData, - ) -> TwoToOneBlockCircuitData - where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, - { - let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); - - let mut dummy_pis = vec![]; - // The magic numbers were derived from the assertion at end of this function. - while builder.num_public_inputs() - < block_circuit.circuit.common.num_public_inputs - (2337 - 2269) - { - let target = builder.add_virtual_public_input(); - dummy_pis.push(target); - } - - let cyclic_vk = builder.add_verifier_data_public_inputs(); - // Avoid accidentally adding public inputs after calling - // [`add_verifier_data_public_inputs`]. - #[cfg(debug_assertions)] - let count_public_inputs = builder.num_public_inputs(); - - let lhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); - let rhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); - - let lhs_public_values = lhs.public_values(&mut builder); - let rhs_public_values = rhs.public_values(&mut builder); - - let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values); - let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values); - let mut mix_vec = vec![]; - mix_vec.extend(&lhs_pv_hash.elements); - mix_vec.extend(&rhs_pv_hash.elements); - let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); - - debug_assert_eq!( - count_public_inputs, - builder.num_public_inputs(), - "Public inputs were registered after calling `add_verifier_data_public_inputs`." - ); - - debug_assert_eq!( - block_circuit.circuit.common.num_public_inputs, - builder.num_public_inputs(), - "The block aggregation circuit and the block circuit must agree on the number of public inputs." - ); - - let circuit = builder.build::(); - TwoToOneBlockCircuitData { - circuit, - lhs, - rhs, - mix_pv_hash, - dummy_pis, - cyclic_vk, - } - } /// Used in the case of a non aggregation transaction child. /// Creates dummy public inputs to set the cyclic vk to the aggregation From 174c4aebdf8f182cab4b29401cad012696d9bced Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 18:32:50 +0800 Subject: [PATCH 29/59] review: merge aggregation children --- .../src/fixed_recursive_verifier.rs | 118 ++++++++++-------- 1 file changed, 67 insertions(+), 51 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 259c00070..f9964622f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -155,6 +155,15 @@ where } } +impl HasCircuitData for RootCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + fn circuit_data(&self) -> &CircuitData { + &self.circuit + } +} /// 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. @@ -214,14 +223,14 @@ where struct AggregationChildTarget { is_agg: BoolTarget, agg_proof: ProofWithPublicInputsTarget, - evm_proof: ProofWithPublicInputsTarget, + base_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.base_proof)?; Ok(()) } @@ -244,6 +253,18 @@ impl AggregationChildTarget { let evm_pv = PublicValuesTarget::from_public_inputs(&self.evm_proof.public_inputs); PublicValuesTarget::select(builder, self.is_agg, agg_pv, evm_pv) } + + fn public_values_vec>( + &self, + builder: &mut CircuitBuilder, + ) -> Vec { + zip_eq( + &self.agg_proof.public_inputs, + &self.base_proof.public_inputs, + ) + .map(|(&agg_pv, &block_pv)| builder.select(self.is_agg, agg_pv, block_pv)) + .collect() + } } /// Data for the block circuit, which is used to generate a final block proof, @@ -313,13 +334,24 @@ where C: GenericConfig, { pub circuit: CircuitData, - lhs: TwoToOneBlockChildTarget, - rhs: TwoToOneBlockChildTarget, + lhs: AggregationChildTarget, + rhs: AggregationChildTarget, mix_pv_hash: HashOutTarget, dummy_pis: Vec, cyclic_vk: VerifierCircuitTarget, } +impl HasCircuitData for BlockCircuitData +where + F: RichField + Extendable, + C: GenericConfig, + { + fn circuit_data(&self) -> &CircuitData { + &self.circuit + } + } + + impl TwoToOneBlockCircuitData where F: RichField + Extendable, @@ -346,8 +378,8 @@ where generator_serializer: &dyn WitnessGeneratorSerializer, ) -> IoResult { let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; - let lhs = TwoToOneBlockChildTarget::from_buffer(buffer)?; - let rhs = TwoToOneBlockChildTarget::from_buffer(buffer)?; + let lhs = AggregationChildTarget::from_buffer(buffer)?; + let rhs = AggregationChildTarget::from_buffer(buffer)?; let mix_pv_hash = buffer.read_target_hash()?; let dummy_pis = buffer.read_target_vec()?; let cyclic_vk = buffer.read_target_verifier_circuit()?; @@ -362,44 +394,8 @@ where } } -#[derive(Eq, PartialEq, Debug)] -struct TwoToOneBlockChildTarget { - is_agg: BoolTarget, - agg_proof: ProofWithPublicInputsTarget, - block_proof: ProofWithPublicInputsTarget, -} -impl TwoToOneBlockChildTarget { - 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.block_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 block_proof = buffer.read_target_proof_with_public_inputs()?; - Ok(Self { - is_agg, - agg_proof, - block_proof, - }) - } - - fn public_values>( - &self, - builder: &mut CircuitBuilder, - ) -> Vec { - zip_eq( - &self.agg_proof.public_inputs, - &self.block_proof.public_inputs, - ) - .map(|(&agg_pv, &block_pv)| builder.select(self.is_agg, agg_pv, block_pv)) - .collect() - } -} impl AllRecursiveCircuits where @@ -880,12 +876,24 @@ where builder.connect(lhs.gas_used_after, rhs.gas_used_before); } - fn add_agg_child( + /// Setup a new part of a circuit to handle a new unrelated proof. + /// Helper method for [`create_two_to_one_block_circuit`]. + /// + /// # Arguments + /// + /// - `builder`: The circuit builder object. + /// - `block_circuit_data`: Circuit data describing the blocks that can be + /// aggregated. + /// + /// # Outputs + /// + /// Returns a [`TwoToOneBlockChildTarget`] object. + fn add_agg_child>( builder: &mut CircuitBuilder, - root: &RootCircuitData, + root: &T, ) -> AggregationChildTarget { - let common = &root.circuit.common; - let root_vk = builder.constant_verifier_data(&root.circuit.verifier_only); + let common = &root.circuit_data().common; + let root_vk = builder.constant_verifier_data(&root.circuit_data().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); @@ -1015,11 +1023,11 @@ where #[cfg(debug_assertions)] let count_public_inputs = builder.num_public_inputs(); - let lhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); - let rhs = Self::add_two_to_one_block_child(&mut builder, block_circuit); + let lhs = Self::add_agg_child(&mut builder, block_circuit); + let rhs = Self::add_agg_child(&mut builder, block_circuit); - let lhs_public_values = lhs.public_values(&mut builder); - let rhs_public_values = rhs.public_values(&mut builder); + let lhs_public_values = lhs.public_values_vec(&mut builder); + let rhs_public_values = rhs.public_values_vec(&mut builder); let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values); let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values); @@ -1742,7 +1750,7 @@ where /// 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: &TwoToOneBlockChildTarget, + agg_child: &AggregationChildTarget, is_agg: bool, circuit: &CircuitData, agg_inputs: &mut PartialWitness, @@ -2018,3 +2026,11 @@ fn shrinking_config() -> CircuitConfig { ..CircuitConfig::standard_recursion_config() } } + +trait HasCircuitData +where + F: RichField + Extendable, + C: GenericConfig, +{ + fn circuit_data(&self) -> &CircuitData; +} From 2b1d86829bf2224193e38e5082e1e272b5fc94f3 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 25 Jun 2024 18:33:21 +0800 Subject: [PATCH 30/59] review: rename `evm_proof` to `base_proof` --- evm_arithmetization/src/fixed_recursive_verifier.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index f9964622f..dc0bfd718 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -241,7 +241,7 @@ impl AggregationChildTarget { Ok(Self { is_agg, agg_proof, - evm_proof, + base_proof: evm_proof, }) } @@ -250,7 +250,7 @@ impl AggregationChildTarget { builder: &mut CircuitBuilder, ) -> PublicValuesTarget { let agg_pv = PublicValuesTarget::from_public_inputs(&self.agg_proof.public_inputs); - let evm_pv = PublicValuesTarget::from_public_inputs(&self.evm_proof.public_inputs); + let evm_pv = PublicValuesTarget::from_public_inputs(&self.base_proof.public_inputs); PublicValuesTarget::select(builder, self.is_agg, agg_pv, evm_pv) } @@ -905,7 +905,7 @@ where AggregationChildTarget { is_agg, agg_proof, - evm_proof, + base_proof: evm_proof, } } @@ -1409,11 +1409,11 @@ where agg_inputs.set_bool_target(self.aggregation.lhs.is_agg, lhs_is_agg); agg_inputs.set_proof_with_pis_target(&self.aggregation.lhs.agg_proof, lhs_proof); - agg_inputs.set_proof_with_pis_target(&self.aggregation.lhs.evm_proof, lhs_proof); + agg_inputs.set_proof_with_pis_target(&self.aggregation.lhs.base_proof, lhs_proof); agg_inputs.set_bool_target(self.aggregation.rhs.is_agg, rhs_is_agg); agg_inputs.set_proof_with_pis_target(&self.aggregation.rhs.agg_proof, rhs_proof); - agg_inputs.set_proof_with_pis_target(&self.aggregation.rhs.evm_proof, rhs_proof); + agg_inputs.set_proof_with_pis_target(&self.aggregation.rhs.base_proof, rhs_proof); agg_inputs.set_verifier_data_target( &self.aggregation.cyclic_vk, @@ -1767,7 +1767,7 @@ where proof, ); } - agg_inputs.set_proof_with_pis_target(&agg_child.block_proof, proof); + agg_inputs.set_proof_with_pis_target(&agg_child.base_proof, proof); } } /// A map between initial degree sizes and their associated shrinking recursion From c51c2359b30043f0ed27a8dafa4f733790b9d81c Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 00:56:08 +0800 Subject: [PATCH 31/59] review: squash tests --- .../src/fixed_recursive_verifier.rs | 8 +- evm_arithmetization/tests/two_to_one_block.rs | 344 ++++++++++++++++++ 2 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 evm_arithmetization/tests/two_to_one_block.rs diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index dc0bfd718..b01288e9c 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1632,15 +1632,13 @@ where ) } - /// Aggregates two block or aggregation proofs in a manner similar to a - /// binary operator. - /// Visually: `(lhs, lhs_is_agg) BinOp (rhs, rhs_is_agg)`. + /// Aggregates two proofs in manner similar to [`prove_aggregation`]. /// /// # Arguments /// - /// - `lhs`: a proof of either a block or previous aggregation proof. + /// - `lhs`: a proof of either a block or previous aggregation. /// - `lhs_is_agg`: specify which case `lhs` was. - /// - `rhs`: a proof of either a block or previous aggregation proof. + /// - `rhs`: a proof of either a block or previous aggregation. /// - `rhs_is_agg`: specify which case `rhs` was. /// /// # Outputs diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs new file mode 100644 index 000000000..a648c4d7e --- /dev/null +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -0,0 +1,344 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use ethereum_types::{Address, BigEndianHash, H256, U256}; +use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; +use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; +use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; +use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; +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::PoseidonGoldilocksConfig; +use plonky2::plonk::proof::ProofWithPublicInputs; +use plonky2::util::timing::TimingTree; + +type F = GoldilocksField; +const D: usize = 2; +type C = PoseidonGoldilocksConfig; + +fn eth_to_wei(eth: U256) -> U256 { + // 1 ether = 10^18 wei. + eth * U256::from(10).pow(18.into()) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +} + +/// Get `GenerationInputs` for a simple token transfer txn, where the block has +/// the given timestamp. +fn empty_transfer(timestamp: u64) -> anyhow::Result { + init_logger(); + + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); + let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); + + let sender_state_key = keccak(sender); + let to_state_key = keccak(to); + + let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); + let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); + + let sender_account_before = AccountRlp { + nonce: 5.into(), + balance: eth_to_wei(100_000.into()), + storage_root: HashedPartialTrie::from(Node::Empty).hash(), + code_hash: keccak([]), + }; + let to_account_before = AccountRlp::default(); + + let state_trie_before: HashedPartialTrie = Node::Leaf { + nibbles: sender_nibbles, + value: rlp::encode(&sender_account_before).to_vec(), + } + .into(); + let checkpoint_state_trie_root = state_trie_before.hash(); + assert_eq!( + checkpoint_state_trie_root, + hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into() + ); + + let tries_before = TrieInputs { + state_trie: HashedPartialTrie::from(Node::Empty), + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + storage_tries: vec![], + }; + + // Generated using a little py-evm script. + let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); + let value = U256::from(100u32); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: timestamp.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: 0.into(), + block_bloom: [0.into(); 8], + }; + + let contract_code = HashMap::new(); + + let expected_state_trie_after: HashedPartialTrie = { + let txdata_gas = 2 * 16; + let gas_used = 21_000 + txdata_gas; + + let sender_account_after = AccountRlp { + balance: sender_account_before.balance - value - gas_used * 10, + nonce: sender_account_before.nonce + 1, + ..sender_account_before + }; + let to_account_after = AccountRlp { + balance: value, + ..to_account_before + }; + + let mut children = core::array::from_fn(|_| Node::Empty.into()); + children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { + nibbles: sender_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&sender_account_after).to_vec(), + } + .into(); + children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { + nibbles: to_nibbles.truncate_n_nibbles_front(1), + value: rlp::encode(&to_account_after).to_vec(), + } + .into(); + Node::Branch { + children, + value: vec![], + } + .into() + }; + + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: 21032.into(), + bloom: vec![0; 256].into(), + logs: vec![], + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert( + Nibbles::from_str("0x80").unwrap(), + rlp::encode(&receipt_0).to_vec(), + )?; + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let _trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + + let tries_after = TrieRoots { + state_root: tries_before.state_trie.hash(), + transactions_root: tries_before.transactions_trie.hash(), + receipts_root: tries_before.receipts_trie.hash(), + }; + let inputs = GenerationInputs { + signed_txn: None, + withdrawals: vec![], + tries: tries_before.clone(), + trie_roots_after: tries_after, + contract_code, + checkpoint_state_trie_root: tries_before.state_trie.hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 0.into(), + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + Ok(inputs) +} + +fn get_test_block_proof( + timestamp: u64, + timing: &mut TimingTree, + all_circuits: &AllRecursiveCircuits, + all_stark: &AllStark, + config: &StarkConfig, +) -> anyhow::Result> { + log::info!("Stage 0"); + log::info!("Generating proof of block {}", timestamp); + let inputs0 = empty_transfer(timestamp)?; + let inputs = inputs0.clone(); + let dummy0 = GenerationInputs { + txn_number_before: inputs.txn_number_before, + gas_used_before: inputs.gas_used_after, + gas_used_after: inputs.gas_used_after, + signed_txn: None, + withdrawals: vec![], + tries: TrieInputs { + state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), + transactions_trie: HashedPartialTrie::from(Node::Hash( + inputs.trie_roots_after.transactions_root, + )), + receipts_trie: HashedPartialTrie::from(Node::Hash( + inputs.trie_roots_after.receipts_root, + )), + storage_tries: vec![], + }, + trie_roots_after: inputs.trie_roots_after, + checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, + contract_code: Default::default(), + block_metadata: inputs.block_metadata.clone(), + block_hashes: inputs.block_hashes.clone(), + }; + log::info!("{:#?}", inputs0); + log::info!("{:#?}", dummy0); + log::info!("Stage 1"); + + let (root_proof0, pv0) = all_circuits.prove_root(all_stark, config, inputs0, timing, None)?; + all_circuits.verify_root(root_proof0.clone())?; + let (dummy_proof0, dummy_pv0) = + all_circuits.prove_root(all_stark, config, dummy0, timing, None)?; + all_circuits.verify_root(dummy_proof0.clone())?; + + log::info!("Stage 2"); + let (agg_proof0, pv0) = all_circuits.prove_aggregation( + false, + &root_proof0, + pv0, + false, + &dummy_proof0, + dummy_pv0, + )?; + + log::info!("Stage 3: Verify aggregation"); + all_circuits.verify_aggregation(&agg_proof0)?; + + log::info!("Stage 4: Check public values"); + // 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); + assert_eq!( + pv0.trie_roots_before.state_root, + pv0.extra_block_data.checkpoint_state_trie_root + ); + + log::info!("Stage 5: Prove Block"); + let (block_proof0, 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(), + )?; + + let pv_block = PublicValues::from_public_inputs(&block_proof0.public_inputs); + assert_eq!(block_public_values, pv_block); + + Ok(block_proof0) +} + +#[ignore] +#[test] +fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { + init_logger(); + let some_timestamps = [127, 42, 65, 43]; + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &config, + ); + let mut timing = TimingTree::new("prove root first", log::Level::Info); + + let unrelated_block_proofs = some_timestamps + .iter() + .map(|&ts| get_test_block_proof(ts, &mut timing, &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)?; + all_circuits.verify_two_to_one_block(&aggproof_42_42)?; + } + + { + // Aggregate a sequential proof containing three proofs with the structure + // `((A,B),(C,D))`. + // + // A B C D Blockproofs (base case) + // \ / \ / + // (A, B) (C, D) Two-to-one block aggregation proofs + // \ / + // ((A,B), (C,D)) Two-to-one block aggregation proofs + let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block(&aggproof01)?; + + let aggproof23 = all_circuits.prove_two_to_one_block(&bp[2], false, &bp[3], false)?; + all_circuits.verify_two_to_one_block(&aggproof23)?; + + let aggproof0123 = + all_circuits.prove_two_to_one_block(&aggproof01, true, &aggproof23, true)?; + all_circuits.verify_two_to_one_block(&aggproof0123)?; + } + + { + // Foldleft + // Aggregate a sequential /// proof containing three proofs with the structure + // `((A,B),(C,D))`. + // + // A B C Blockproofs (base case) + // \ / / + // (A, B) / Two-to-one block aggregation proofs + // \ / + // ((A,B), C) Two-to-one block aggregation proofs + + let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; + all_circuits.verify_two_to_one_block(&aggproof01)?; + + let aggproof012 = all_circuits.prove_two_to_one_block(&aggproof01, true, &bp[2], false)?; + all_circuits.verify_two_to_one_block(&aggproof012)?; + + let aggproof0123 = + all_circuits.prove_two_to_one_block(&aggproof012, true, &bp[3], false)?; + all_circuits.verify_two_to_one_block(&aggproof0123)?; + } + + { + // Foldright + // A B C Blockproofs (base case) + // \ \ / + // \ (B,C) Two-to-one block aggregation proofs + // \ / + // (A,(B, C)) Two-to-one block aggregation proofs + + let aggproof23 = all_circuits.prove_two_to_one_block(&bp[2], false, &bp[3], false)?; + all_circuits.verify_two_to_one_block(&aggproof23)?; + + let aggproof123 = all_circuits.prove_two_to_one_block(&bp[1], false, &aggproof23, true)?; + all_circuits.verify_two_to_one_block(&aggproof123)?; + + let aggproof0123 = + all_circuits.prove_two_to_one_block(&bp[0], false, &aggproof123, true)?; + all_circuits.verify_two_to_one_block(&aggproof0123)?; + } + + Ok(()) +} From aa26e76b3901bdb1c956dde06223e19a23d2fafc Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 14:15:52 +0800 Subject: [PATCH 32/59] review: investigate padding length --- .../src/fixed_recursive_verifier.rs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index b01288e9c..09cb42cd9 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -12,7 +12,7 @@ use plonky2::field::extension::Extendable; use plonky2::fri::FriParams; use plonky2::gates::constant::ConstantGate; use plonky2::gates::noop::NoopGate; -use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField}; +use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField, NUM_HASH_OUT_ELTS}; use plonky2::iop::challenger::RecursiveChallenger; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; @@ -1009,9 +1009,28 @@ where let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); let mut dummy_pis = vec![]; - // The magic numbers were derived from the assertion at end of this function. - while builder.num_public_inputs() - < block_circuit.circuit.common.num_public_inputs - (2337 - 2269) + // The magic numbers were derived from the assertion at end of this function. 2337-2269 = 68 + let magic_number = block_circuit.circuit.common.num_public_inputs - (2337 - 2269); + + // while builder.num_public_inputs() < block_circuit.circuit.common.num_public_inputs - (2337 - 2269){} + + + dbg!(block_circuit.circuit.verifier_only.constants_sigmas_cap.len()); + dbg!(::HASH_SIZE); + dbg!(NUM_HASH_OUT_ELTS); + dbg!(builder.num_public_inputs()); + dbg!(block_circuit.circuit.common.num_public_inputs ); + dbg!(builder.config.fri_config.cap_height); + dbg!(builder.config.fri_config.num_cap_elements()); + dbg!(1< Date: Wed, 26 Jun 2024 14:43:27 +0800 Subject: [PATCH 33/59] review: investigate padding length part 2 --- evm_arithmetization/src/fixed_recursive_verifier.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 09cb42cd9..43cc7f8a3 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1014,7 +1014,6 @@ where // while builder.num_public_inputs() < block_circuit.circuit.common.num_public_inputs - (2337 - 2269){} - dbg!(block_circuit.circuit.verifier_only.constants_sigmas_cap.len()); dbg!(::HASH_SIZE); dbg!(NUM_HASH_OUT_ELTS); @@ -1026,15 +1025,20 @@ where dbg!(block_circuit.circuit.verifier_only.circuit_digest.elements.len() ); dbg!(block_circuit.circuit.common.config.fri_config.cap_height); - let mut padding = block_circuit.circuit.verifier_only.circuit_digest.elements.len() + (1<< block_circuit.circuit.common.config.fri_config.cap_height)*(NUM_HASH_OUT_ELTS); - dbg!(padding); + // The number of PIS that will be added after padding by `builder.add_verifier_data_public_inputs()`. + let verification_key_len = block_circuit.circuit.verifier_only.circuit_digest.elements.len() + (1<< block_circuit.circuit.common.config.fri_config.cap_height)*(NUM_HASH_OUT_ELTS); + // 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; + padding -= verification_key_len; + padding -= builder.num_public_inputs(); for _ in 0..padding { let target = builder.add_virtual_public_input(); dummy_pis.push(target); } + assert!(false); let cyclic_vk = builder.add_verifier_data_public_inputs(); // Avoid accidentally adding public inputs after calling From 7789de03c9908d77d85a60343d25d79bcd18b431 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 14:44:52 +0800 Subject: [PATCH 34/59] review: remove magic numbers --- .../src/fixed_recursive_verifier.rs | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 43cc7f8a3..74541f25e 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1008,37 +1008,27 @@ where { let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); - let mut dummy_pis = vec![]; - // The magic numbers were derived from the assertion at end of this function. 2337-2269 = 68 - let magic_number = block_circuit.circuit.common.num_public_inputs - (2337 - 2269); - - // while builder.num_public_inputs() < block_circuit.circuit.common.num_public_inputs - (2337 - 2269){} - - dbg!(block_circuit.circuit.verifier_only.constants_sigmas_cap.len()); - dbg!(::HASH_SIZE); - dbg!(NUM_HASH_OUT_ELTS); - dbg!(builder.num_public_inputs()); - dbg!(block_circuit.circuit.common.num_public_inputs ); - dbg!(builder.config.fri_config.cap_height); - dbg!(builder.config.fri_config.num_cap_elements()); - dbg!(1< Date: Wed, 26 Jun 2024 14:54:06 +0800 Subject: [PATCH 35/59] review: remove HasCircuit trait and pass in CircuitData field directly --- .../src/fixed_recursive_verifier.rs | 59 ++++++------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 74541f25e..25e8eac6b 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -155,15 +155,6 @@ where } } -impl HasCircuitData for RootCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - fn circuit_data(&self) -> &CircuitData { - &self.circuit - } -} /// 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. @@ -341,17 +332,6 @@ where cyclic_vk: VerifierCircuitTarget, } -impl HasCircuitData for BlockCircuitData -where - F: RichField + Extendable, - C: GenericConfig, - { - fn circuit_data(&self) -> &CircuitData { - &self.circuit - } - } - - impl TwoToOneBlockCircuitData where F: RichField + Extendable, @@ -770,8 +750,8 @@ where let cyclic_vk = builder.add_verifier_data_public_inputs(); #[cfg(debug_assertions)] let count_public_inputs = builder.num_public_inputs(); - let lhs = Self::add_agg_child(&mut builder, root); - let rhs = Self::add_agg_child(&mut builder, root); + let lhs = Self::add_agg_child(&mut builder, &root.circuit); + let rhs = Self::add_agg_child(&mut builder, &root.circuit); let lhs_public_values = lhs.public_values(&mut builder); let rhs_public_values = rhs.public_values(&mut builder); @@ -882,30 +862,33 @@ where /// # Arguments /// /// - `builder`: The circuit builder object. - /// - `block_circuit_data`: Circuit data describing the blocks that can be - /// aggregated. + /// - `base_circuit`: Circuit data describing the circuit of the base proof. /// /// # Outputs /// /// Returns a [`TwoToOneBlockChildTarget`] object. - fn add_agg_child>( + fn add_agg_child( builder: &mut CircuitBuilder, - root: &T, + base_circuit: &CircuitData, ) -> AggregationChildTarget { - let common = &root.circuit_data().common; - let root_vk = builder.constant_verifier_data(&root.circuit_data().verifier_only); + let common = &base_circuit.common; + let base_vk = builder.constant_verifier_data(&base_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 base_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, + &base_proof, + &base_vk, + common, ) .expect("Failed to build cyclic recursion circuit"); AggregationChildTarget { is_agg, agg_proof, - base_proof: evm_proof, + base_proof, } } @@ -1036,8 +1019,8 @@ where #[cfg(debug_assertions)] let count_public_inputs = builder.num_public_inputs(); - let lhs = Self::add_agg_child(&mut builder, block_circuit); - let rhs = Self::add_agg_child(&mut builder, block_circuit); + let lhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); + let rhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); let lhs_public_values = lhs.public_values_vec(&mut builder); let rhs_public_values = rhs.public_values_vec(&mut builder); @@ -2036,12 +2019,4 @@ fn shrinking_config() -> CircuitConfig { num_routed_wires: 40, ..CircuitConfig::standard_recursion_config() } -} - -trait HasCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - fn circuit_data(&self) -> &CircuitData; -} +} \ No newline at end of file From a5de30471c656372e435b375ddca52a2efc265d4 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 15:45:40 +0800 Subject: [PATCH 36/59] review: rework `set_dummy_if_necessary` --- .../src/fixed_recursive_verifier.rs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 25e8eac6b..5e63ea013 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1700,49 +1700,48 @@ where check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } - - /// 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. + /// Creates dummy public inputs with correct verifier key at the end. Used + /// by [`set_dummy_if_necessary`]. It 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, + agg_proof_with_pis: &ProofWithPublicInputsTarget, + base_proof_with_pis: &ProofWithPublicInputs, ) { let ProofWithPublicInputs { - proof, - public_inputs, - } = proof; + proof: base_proof, + public_inputs: base_public_inputs, + } = base_proof_with_pis; let ProofWithPublicInputsTarget { - proof: proof_targets, - public_inputs: pi_targets, - } = agg_proof; + proof: agg_proof_targets, + public_inputs: agg_pi_targets, + } = agg_proof_with_pis; // The proof remains the same. - witness.set_proof_target(proof_targets, proof); + witness.set_proof_target(agg_proof_targets, base_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]; - } + let mut dummy_pis = vec![F::ZERO; circuit_agg.common.num_public_inputs - cyclic_vk.len()]; + dummy_pis.append(&mut cyclic_vk); // Set dummy public inputs. - for (&pi_t, pi) in pi_targets.iter().zip_eq(dummy_pis) { + for (&pi_t, pi) in agg_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. + /// If the [`AggregationChild`] is a base proof and not an aggregation + /// proof, we need to manually set the public inputs vector of the otherwise + /// inert `agg_proof`, so that they correspond to the `cyclic_vk` of the + /// aggregation circuit. The cyclic prover expects to find the `cyclic_vk` + /// targets in the very end of the public inputs vector, and so it does not + /// matter what the preceding values are. fn set_dummy_if_necessary( agg_child: &AggregationChildTarget, is_agg: bool, From 7c872acbbb9c1dc8374644ccb8226996539e9e07 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 19:46:43 +0800 Subject: [PATCH 37/59] review: remove logging in test --- evm_arithmetization/tests/two_to_one_block.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index a648c4d7e..1c85025b9 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -176,8 +176,6 @@ fn get_test_block_proof( all_stark: &AllStark, config: &StarkConfig, ) -> anyhow::Result> { - log::info!("Stage 0"); - log::info!("Generating proof of block {}", timestamp); let inputs0 = empty_transfer(timestamp)?; let inputs = inputs0.clone(); let dummy0 = GenerationInputs { @@ -202,9 +200,6 @@ fn get_test_block_proof( block_metadata: inputs.block_metadata.clone(), block_hashes: inputs.block_hashes.clone(), }; - log::info!("{:#?}", inputs0); - log::info!("{:#?}", dummy0); - log::info!("Stage 1"); let (root_proof0, pv0) = all_circuits.prove_root(all_stark, config, inputs0, timing, None)?; all_circuits.verify_root(root_proof0.clone())?; @@ -212,7 +207,6 @@ fn get_test_block_proof( all_circuits.prove_root(all_stark, config, dummy0, timing, None)?; all_circuits.verify_root(dummy_proof0.clone())?; - log::info!("Stage 2"); let (agg_proof0, pv0) = all_circuits.prove_aggregation( false, &root_proof0, @@ -222,10 +216,8 @@ fn get_test_block_proof( dummy_pv0, )?; - log::info!("Stage 3: Verify aggregation"); all_circuits.verify_aggregation(&agg_proof0)?; - log::info!("Stage 4: Check public values"); // 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); @@ -234,7 +226,6 @@ fn get_test_block_proof( pv0.extra_block_data.checkpoint_state_trie_root ); - log::info!("Stage 5: Prove Block"); let (block_proof0, block_public_values) = all_circuits.prove_block( None, // We don't specify a previous proof, considering block 1 as the new checkpoint. &agg_proof0, From 6c78514c02453eb21f7f7ac570b81a8ee2aaa312 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 19:49:43 +0800 Subject: [PATCH 38/59] review: remove legacy test --- evm_arithmetization/tests/two_to_one_agg.rs | 485 -------------------- 1 file changed, 485 deletions(-) delete mode 100644 evm_arithmetization/tests/two_to_one_agg.rs diff --git a/evm_arithmetization/tests/two_to_one_agg.rs b/evm_arithmetization/tests/two_to_one_agg.rs deleted file mode 100644 index 5b0e9e941..000000000 --- a/evm_arithmetization/tests/two_to_one_agg.rs +++ /dev/null @@ -1,485 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::{env, fs}; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; -use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; -use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; -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::PoseidonGoldilocksConfig; -use plonky2::plonk::proof::ProofWithPublicInputs; -use plonky2::util::timing::TimingTree; - -type F = GoldilocksField; -const D: usize = 2; -type C = PoseidonGoldilocksConfig; - -/// Set this to true to cache blocks in `/tmp``. This is intended mainly for -/// developer experience and not for CI testing. -const CACHE_TEST_BLOCKS: bool = true; - -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} - -/// Get `GenerationInputs` for a simple token transfer txn, where the block has -/// the given timestamp. -fn empty_transfer(timestamp: u64) -> anyhow::Result { - init_logger(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - let to_account_before = AccountRlp::default(); - - let state_trie_before: HashedPartialTrie = Node::Leaf { - nibbles: sender_nibbles, - value: rlp::encode(&sender_account_before).to_vec(), - } - .into(); - let checkpoint_state_trie_root = state_trie_before.hash(); - assert_eq!( - checkpoint_state_trie_root, - hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into() - ); - - let tries_before = TrieInputs { - state_trie: HashedPartialTrie::from(Node::Empty), - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], - }; - - // Generated using a little py-evm script. - let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); - let value = U256::from(100u32); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: timestamp.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: 0.into(), - block_bloom: [0.into(); 8], - }; - - let contract_code = HashMap::new(); - - let expected_state_trie_after: HashedPartialTrie = { - let txdata_gas = 2 * 16; - let gas_used = 21_000 + txdata_gas; - - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value - gas_used * 10, - nonce: sender_account_before.nonce + 1, - ..sender_account_before - }; - let to_account_after = AccountRlp { - balance: value, - ..to_account_before - }; - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_after).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_after).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - .into() - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 21032.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - )?; - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let _trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let tries_after = TrieRoots { - state_root: tries_before.state_trie.hash(), - transactions_root: tries_before.transactions_trie.hash(), - receipts_root: tries_before.receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: None, - withdrawals: vec![], - tries: tries_before.clone(), - trie_roots_after: tries_after, - contract_code, - checkpoint_state_trie_root: tries_before.state_trie.hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 0.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - Ok(inputs) -} - -fn get_test_block_proof( - timestamp: u64, - timing: &mut TimingTree, - all_circuits: &AllRecursiveCircuits, - all_stark: &AllStark, - config: &StarkConfig, -) -> anyhow::Result> { - log::info!("Stage 0"); - log::info!("Generating proof of block {}", timestamp); - let inputs0 = empty_transfer(timestamp)?; - let inputs = inputs0.clone(); - let dummy0 = GenerationInputs { - txn_number_before: inputs.txn_number_before, - gas_used_before: inputs.gas_used_after, - gas_used_after: inputs.gas_used_after, - signed_txn: None, - withdrawals: vec![], - tries: TrieInputs { - state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), - transactions_trie: HashedPartialTrie::from(Node::Hash( - inputs.trie_roots_after.transactions_root, - )), - receipts_trie: HashedPartialTrie::from(Node::Hash( - inputs.trie_roots_after.receipts_root, - )), - storage_tries: vec![], - }, - trie_roots_after: inputs.trie_roots_after, - checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, - contract_code: Default::default(), - block_metadata: inputs.block_metadata.clone(), - block_hashes: inputs.block_hashes.clone(), - }; - log::info!("{:#?}", inputs0); - log::info!("{:#?}", dummy0); - log::info!("Stage 1"); - - let (root_proof0, pv0) = all_circuits.prove_root(all_stark, config, inputs0, timing, None)?; - all_circuits.verify_root(root_proof0.clone())?; - let (dummy_proof0, dummy_pv0) = - all_circuits.prove_root(all_stark, config, dummy0, timing, None)?; - all_circuits.verify_root(dummy_proof0.clone())?; - - log::info!("Stage 2"); - let (agg_proof0, pv0) = all_circuits.prove_aggregation( - false, - &root_proof0, - pv0, - false, - &dummy_proof0, - dummy_pv0, - )?; - - log::info!("Stage 3: Verify aggregation"); - all_circuits.verify_aggregation(&agg_proof0)?; - - log::info!("Stage 4: Check public values"); - // 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); - assert_eq!( - pv0.trie_roots_before.state_root, - pv0.extra_block_data.checkpoint_state_trie_root - ); - - log::info!("Stage 5: Prove Block"); - let (block_proof0, 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(), - )?; - - let pv_block = PublicValues::from_public_inputs(&block_proof0.public_inputs); - assert_eq!(block_public_values, pv_block); - - Ok(block_proof0) -} - -/// Caches proofs in `/tmp/zk_evm_test_blocks/`. -fn get_test_block_proof_cached( - timestamp: u64, - timing: &mut TimingTree, - all_circuits: &AllRecursiveCircuits, - all_stark: &AllStark, - config: &StarkConfig, -) -> anyhow::Result> { - log::info!("Getting proof of block {}", timestamp); - - // 1. Setup path - let mut path = env::temp_dir(); - path.push("zk_evm_test"); - path.push(format!("test_block_{timestamp}.bpf")); - log::info!("{:#?}", path); - - // 2. Read cached block from disc and return early. - if CACHE_TEST_BLOCKS - && path.try_exists()? - && fs::File::open(path.clone())?.metadata()?.len() > 0 - { - let raw_block = fs::read(path)?; - return ProofWithPublicInputs::from_bytes(raw_block, &all_circuits.block.circuit.common); - } - - // 3. Compute new block proof. - let block_proof = get_test_block_proof(timestamp, timing, all_circuits, all_stark, config)?; - all_circuits.verify_block(&block_proof)?; - - // 4. Write block to disc cache and validate. - if CACHE_TEST_BLOCKS { - // write to tmp - let raw_block = ProofWithPublicInputs::to_bytes(&block_proof); - - if let Some(p) = path.parent() { - fs::create_dir_all(p)? - }; - fs::write(path.clone(), &raw_block)?; - log::info!("Succesfully wrote blockproof to {:#?}", path); - - let written_block = fs::read(path.clone())?; - assert_eq!(&raw_block, &written_block); - let restored_block = - ProofWithPublicInputs::from_bytes(written_block, &all_circuits.block.circuit.common)?; - assert_eq!(block_proof, restored_block); - log::info!("Succesfully validated blockproof from {:#?}", path); - } - - Ok(block_proof) -} - -/// Aggregate a sequential /// proof containing three proofs with the structure -/// `((A,B),(C,D))`. -/// -/// A B C D Blockproofs (base case) -/// \ / \ / -/// (A, B) (C, D) Two-to-one block aggregation proofs -/// \ / -/// ((A,B), (C,D)) Two-to-one block aggregation proofs -#[test] -fn test_block_aggregation_binop_4_blocks() -> anyhow::Result<()> { - init_logger(); - log::info!("Meta Stage 0: Setup"); - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - - log::info!("Meta Stage 1: Compute block proofs"); - let some_timestamps = [127, 42, 65, 43]; - let unrelated_block_proofs = some_timestamps - .iter() - .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) - .collect::>>>()?; - - log::info!("Meta Stage 2: Verify block proofs"); - unrelated_block_proofs - .iter() - .try_for_each(|bp| all_circuits.verify_block(bp))?; - - log::info!("Meta Stage 3: Aggregate block proofs"); - let bp = unrelated_block_proofs; - - let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; - all_circuits.verify_two_to_one_block(&aggproof01)?; - - let aggproof23 = all_circuits.prove_two_to_one_block(&bp[2], false, &bp[3], false)?; - all_circuits.verify_two_to_one_block(&aggproof23)?; - - let aggproof0123 = all_circuits.prove_two_to_one_block(&aggproof01, true, &aggproof23, true)?; - all_circuits.verify_two_to_one_block(&aggproof0123)?; - - Ok(()) -} - -#[test] -fn test_block_aggregation_binop_same_block_twice() -> anyhow::Result<()> { - init_logger(); - log::info!("Meta Stage 0: Setup"); - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - - log::info!("Meta Stage 1: Compute block proofs"); - let some_timestamps = [42, 42]; - let unrelated_block_proofs = some_timestamps - .iter() - .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) - .collect::>>>()?; - - log::info!("Meta Stage 2: Verify block proofs"); - unrelated_block_proofs - .iter() - .try_for_each(|bp| all_circuits.verify_block(bp))?; - - log::info!("Meta Stage 3: Aggregate block proofs"); - let bp = unrelated_block_proofs; - - let aggproof_42_42 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; - all_circuits.verify_two_to_one_block(&aggproof_42_42)?; - - Ok(()) -} - -/// Aggregate a sequential /// proof containing three proofs with the structure -/// `((A,B),(C,D))`. -/// -/// A B C Blockproofs (base case) -/// \ / / -/// (A, B) / Two-to-one block aggregation proofs -/// \ / -/// ((A,B), C) Two-to-one block aggregation proofs -#[test] -fn test_block_aggregation_binop_foldleft() -> anyhow::Result<()> { - init_logger(); - log::info!("Meta Stage 0: Setup"); - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - - log::info!("Meta Stage 1: Compute block proofs"); - let some_timestamps = [65, 127, 42]; - let unrelated_block_proofs = some_timestamps - .iter() - .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) - .collect::>>>()?; - - log::info!("Meta Stage 2: Verify block proofs"); - unrelated_block_proofs - .iter() - .try_for_each(|bp| all_circuits.verify_block(bp))?; - - log::info!("Meta Stage 3: Aggregate block proofs"); - let bp = unrelated_block_proofs; - - let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; - all_circuits.verify_two_to_one_block(&aggproof01)?; - - let aggproof012 = all_circuits.prove_two_to_one_block(&aggproof01, true, &bp[2], false)?; - all_circuits.verify_two_to_one_block(&aggproof012)?; - - Ok(()) -} - -/// A B C Blockproofs (base case) -/// \ \ / -/// \ (B,C) Two-to-one block aggregation proofs -/// \ / -/// (A,(B, C)) Two-to-one block aggregation proofs -#[test] -fn test_block_aggregation_binop_foldright() -> anyhow::Result<()> { - init_logger(); - log::info!("Meta Stage 0: Setup"); - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - - log::info!("Meta Stage 1: Compute block proofs"); - let some_timestamps = [65, 127, 42]; - let unrelated_block_proofs = some_timestamps - .iter() - .map(|&ts| get_test_block_proof_cached(ts, &mut timing, &all_circuits, &all_stark, &config)) - .collect::>>>()?; - - log::info!("Meta Stage 2: Verify block proofs"); - unrelated_block_proofs - .iter() - .try_for_each(|bp| all_circuits.verify_block(bp))?; - - log::info!("Meta Stage 3: Aggregate block proofs"); - let bp = unrelated_block_proofs; - - let aggproof12 = all_circuits.prove_two_to_one_block(&bp[1], false, &bp[2], false)?; - all_circuits.verify_two_to_one_block(&aggproof12)?; - - let aggproof012 = all_circuits.prove_two_to_one_block(&bp[0], false, &aggproof12, true)?; - all_circuits.verify_two_to_one_block(&aggproof012)?; - - Ok(()) -} From 02485b2f1a1b8c9522a822ffe81f2a1d464811b4 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 19:59:55 +0800 Subject: [PATCH 39/59] review: remove all `debug_assert!` --- .../src/fixed_recursive_verifier.rs | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 5e63ea013..62b550b3f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -577,7 +577,6 @@ where let aggregation = Self::create_aggregation_circuit(&root); let block = Self::create_block_circuit(&aggregation); let two_to_one_block = Self::create_two_to_one_block_circuit(&block); - debug_assert_eq!(&block.circuit.common, &two_to_one_block.circuit.common); Self { root, @@ -748,8 +747,7 @@ where let mut builder = CircuitBuilder::::new(root.circuit.common.config.clone()); let public_values = add_virtual_public_values(&mut builder); let cyclic_vk = builder.add_verifier_data_public_inputs(); - #[cfg(debug_assertions)] - let count_public_inputs = builder.num_public_inputs(); + let lhs = Self::add_agg_child(&mut builder, &root.circuit); let rhs = Self::add_agg_child(&mut builder, &root.circuit); @@ -1014,10 +1012,6 @@ where } let cyclic_vk = builder.add_verifier_data_public_inputs(); - // Avoid accidentally adding public inputs after calling - // [`add_verifier_data_public_inputs`]. - #[cfg(debug_assertions)] - let count_public_inputs = builder.num_public_inputs(); let lhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); let rhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); @@ -1032,18 +1026,6 @@ where mix_vec.extend(&rhs_pv_hash.elements); let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); - debug_assert_eq!( - count_public_inputs, - builder.num_public_inputs(), - "Public inputs were registered after calling `add_verifier_data_public_inputs`." - ); - - debug_assert_eq!( - block_circuit.circuit.common.num_public_inputs, - builder.num_public_inputs(), - "The block aggregation circuit and the block circuit must agree on the number of public inputs." - ); - let circuit = builder.build::(); TwoToOneBlockCircuitData { circuit, From ad2c7937f6a2c4266835e6a5873f412fea1b32dd Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 26 Jun 2024 20:02:59 +0800 Subject: [PATCH 40/59] review: obey Clippy and fmt --- evm_arithmetization/src/fixed_recursive_verifier.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 62b550b3f..5fefeca15 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -374,9 +374,6 @@ where } } - - - impl AllRecursiveCircuits where F: RichField + Extendable, @@ -2000,4 +1997,4 @@ fn shrinking_config() -> CircuitConfig { num_routed_wires: 40, ..CircuitConfig::standard_recursion_config() } -} \ No newline at end of file +} From 60a0d125c21077054e82e449ee2cecd0c5050cfc Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Fri, 28 Jun 2024 20:16:13 +0800 Subject: [PATCH 41/59] review: impl Merklet tree test --- .../src/fixed_recursive_verifier.rs | 11 +++++++++++ evm_arithmetization/tests/two_to_one_block.rs | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 5fefeca15..d83078d82 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -9,6 +9,7 @@ use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; use mpt_trie::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use plonky2::field::extension::Extendable; +use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::fri::FriParams; use plonky2::gates::constant::ConstantGate; use plonky2::gates::noop::NoopGate; @@ -1998,3 +1999,13 @@ fn shrinking_config() -> CircuitConfig { ..CircuitConfig::standard_recursion_config() } } + +pub fn extract_aggregation_hash(proof: &ProofWithPublicInputs) -> HashOut +where + F: RichField + Extendable, + C: GenericConfig, + C::Hasher: AlgebraicHasher, +{ + let elements: [F;NUM_HASH_OUT_ELTS] = proof.public_inputs[0..NUM_HASH_OUT_ELTS].try_into().expect("Malformed proof"); + HashOut::::try_from(elements).expect("Malformed proof") +} \ No newline at end of file diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 1c85025b9..f0cbb5d6e 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; +use evm_arithmetization::fixed_recursive_verifier::extract_aggregation_hash; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; @@ -12,7 +13,7 @@ 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::PoseidonGoldilocksConfig; +use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, Hasher}; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; @@ -288,6 +289,19 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { let aggproof0123 = all_circuits.prove_two_to_one_block(&aggproof01, true, &aggproof23, true)?; all_circuits.verify_two_to_one_block(&aggproof0123)?; + + let hash_no_pad = >::InnerHasher::hash_no_pad; + let two_to_one= >::InnerHasher::two_to_one; + + let mut hashes : Vec<_> = bp.iter().map(|bp| hash_no_pad(&bp.public_inputs)).collect(); + hashes.extend_from_within(0..hashes.len()); + (hashes.len()); + for i in hashes.len()/2..hashes.len()-1 { + hashes[i] = two_to_one(hashes[i/2], hashes[i/2+1]); + } + assert_eq!(extract_aggregation_hash(&aggproof0123), hashes[hashes.len()-2]); + return Ok(()) // TODO REMOVE THIS + } { From 70d961e81e35e13aef620350ebf9b4682f58161b Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Fri, 28 Jun 2024 22:15:44 +0800 Subject: [PATCH 42/59] WIP review: fix hashing mechanism and make hash public input --- .../src/fixed_recursive_verifier.rs | 50 ++++++++++++++++--- evm_arithmetization/tests/two_to_one_block.rs | 4 +- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index d83078d82..d44f6717b 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -987,6 +987,9 @@ where { let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); + let mix_pv_hash_output = builder.add_virtual_hash(); + builder.register_public_inputs(&mix_pv_hash_output.elements); + // The number of PIS that will be added after padding by // `builder.add_verifier_data_public_inputs()`. let verification_key_len = block_circuit @@ -1002,6 +1005,7 @@ where let mut padding = block_circuit.circuit.common.num_public_inputs; padding -= verification_key_len; padding -= builder.num_public_inputs(); + //padding -= mix_pv_hash_output.elements.len(); let mut dummy_pis = vec![]; for _ in 0..padding { @@ -1017,13 +1021,34 @@ where let lhs_public_values = lhs.public_values_vec(&mut builder); let rhs_public_values = rhs.public_values_vec(&mut builder); - let lhs_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values); - let rhs_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values); + let lhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values.to_owned()).elements; + let rhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values.to_owned()).elements; + + let lhs_agg_pv_hash = &lhs_public_values[..NUM_HASH_OUT_ELTS]; + let rhs_agg_pv_hash = &rhs_public_values[..NUM_HASH_OUT_ELTS]; + + let lhs_hash : Vec = zip_eq( + lhs_agg_pv_hash, + lhs_base_pv_hash, + ) + .map(|(&agg_target, base_target)| builder.select(lhs.is_agg, agg_target, base_target)) + .collect(); + + let rhs_hash : Vec = zip_eq( + rhs_agg_pv_hash, + rhs_base_pv_hash, + ) + .map(|(&agg_target, base_target)| builder.select(rhs.is_agg, agg_target, base_target)) + .collect(); + let mut mix_vec = vec![]; - mix_vec.extend(&lhs_pv_hash.elements); - mix_vec.extend(&rhs_pv_hash.elements); + mix_vec.extend(&lhs_hash); + mix_vec.extend(&rhs_hash); + let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); + builder.connect_hashes(mix_pv_hash_output, mix_pv_hash); + let circuit = builder.build::(); TwoToOneBlockCircuitData { circuit, @@ -1653,8 +1678,21 @@ where &self.two_to_one_block.circuit.verifier_only, ); - let lhs_pv_hash = C::InnerHasher::hash_no_pad(&lhs.public_inputs); - let rhs_pv_hash = C::InnerHasher::hash_no_pad(&rhs.public_inputs); + let lhs_pv_hash = if lhs_is_agg { + let elements = lhs.public_inputs[..NUM_HASH_OUT_ELTS].try_into().expect("wrong length"); + HashOut { elements } + } else { + C::InnerHasher::hash_no_pad(&lhs.public_inputs) + }; + + let rhs_pv_hash = if rhs_is_agg { + //extract_aggregation_hash(&rhs.public_inputs) + let elements = rhs.public_inputs[..NUM_HASH_OUT_ELTS].try_into().expect("wrong length"); + HashOut { elements } + } else { + C::InnerHasher::hash_no_pad(&rhs.public_inputs) + }; + let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); witness.set_hash_target(self.two_to_one_block.mix_pv_hash, mix_pv_hash); diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index f0cbb5d6e..50261ecde 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; -use evm_arithmetization::fixed_recursive_verifier::extract_aggregation_hash; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; @@ -13,6 +12,7 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, Hasher}; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; @@ -299,7 +299,7 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { for i in hashes.len()/2..hashes.len()-1 { hashes[i] = two_to_one(hashes[i/2], hashes[i/2+1]); } - assert_eq!(extract_aggregation_hash(&aggproof0123), hashes[hashes.len()-2]); + assert_eq!(&aggproof0123.public_inputs[..NUM_HASH_OUT_ELTS], hashes[hashes.len()-2].elements, "Merkle root of verification tree did not match."); return Ok(()) // TODO REMOVE THIS } From d7518f66e600a27f0ea7b2f8c4ee7cb7342449a1 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Fri, 28 Jun 2024 22:48:06 +0800 Subject: [PATCH 43/59] review: remove VK from inputs --- .../src/fixed_recursive_verifier.rs | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index d44f6717b..81ecfda7f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -330,6 +330,7 @@ where rhs: AggregationChildTarget, mix_pv_hash: HashOutTarget, dummy_pis: Vec, + vk_len: usize, cyclic_vk: VerifierCircuitTarget, } @@ -371,6 +372,7 @@ where mix_pv_hash, dummy_pis, cyclic_vk, + vk_len: todo!(), }) } } @@ -1013,6 +1015,8 @@ where dummy_pis.push(target); } + let user_pis_len = builder.num_public_inputs(); + let cyclic_vk = builder.add_verifier_data_public_inputs(); let lhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); @@ -1021,12 +1025,12 @@ where let lhs_public_values = lhs.public_values_vec(&mut builder); let rhs_public_values = rhs.public_values_vec(&mut builder); - let lhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values.to_owned()).elements; - let rhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values.to_owned()).elements; - let lhs_agg_pv_hash = &lhs_public_values[..NUM_HASH_OUT_ELTS]; let rhs_agg_pv_hash = &rhs_public_values[..NUM_HASH_OUT_ELTS]; + let lhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values[..user_pis_len].to_owned()).elements; + let rhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values[..user_pis_len].to_owned()).elements; + let lhs_hash : Vec = zip_eq( lhs_agg_pv_hash, lhs_base_pv_hash, @@ -1054,9 +1058,10 @@ where circuit, lhs, rhs, - mix_pv_hash, + mix_pv_hash: mix_pv_hash_output, dummy_pis, cyclic_vk, + vk_len: verification_key_len, } } @@ -1678,11 +1683,27 @@ where &self.two_to_one_block.circuit.verifier_only, ); + + let verification_key_len = self.two_to_one_block.vk_len; + + // // The number of PIS that will be added after padding by + // // `builder.add_verifier_data_public_inputs()`. + // let verification_key_len = block_circuit + // .circuit + // .verifier_only + // .circuit_digest + // .elements + // .len() + // + (1 << block_circuit.circuit.common.config.fri_config.cap_height) + // * (NUM_HASH_OUT_ELTS); + + let user_pis_len = lhs.public_inputs.len() - verification_key_len; + let lhs_pv_hash = if lhs_is_agg { let elements = lhs.public_inputs[..NUM_HASH_OUT_ELTS].try_into().expect("wrong length"); HashOut { elements } } else { - C::InnerHasher::hash_no_pad(&lhs.public_inputs) + C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]) }; let rhs_pv_hash = if rhs_is_agg { @@ -1690,7 +1711,7 @@ where let elements = rhs.public_inputs[..NUM_HASH_OUT_ELTS].try_into().expect("wrong length"); HashOut { elements } } else { - C::InnerHasher::hash_no_pad(&rhs.public_inputs) + C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]) }; let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); From a0d2a47da485a4c9b6a7fc33928cf12822bbef9e Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Sat, 29 Jun 2024 19:21:27 +0800 Subject: [PATCH 44/59] review: test Merkle tree working --- .../src/fixed_recursive_verifier.rs | 80 ++++++++++--------- evm_arithmetization/tests/two_to_one_block.rs | 42 +++++++--- 2 files changed, 77 insertions(+), 45 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 81ecfda7f..6277aa86e 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1028,22 +1028,20 @@ where let lhs_agg_pv_hash = &lhs_public_values[..NUM_HASH_OUT_ELTS]; let rhs_agg_pv_hash = &rhs_public_values[..NUM_HASH_OUT_ELTS]; - let lhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(lhs_public_values[..user_pis_len].to_owned()).elements; - let rhs_base_pv_hash = builder.hash_n_to_hash_no_pad::(rhs_public_values[..user_pis_len].to_owned()).elements; - - let lhs_hash : Vec = zip_eq( - lhs_agg_pv_hash, - lhs_base_pv_hash, - ) - .map(|(&agg_target, base_target)| builder.select(lhs.is_agg, agg_target, base_target)) - .collect(); + let lhs_base_pv_hash = builder + .hash_n_to_hash_no_pad::(lhs_public_values[..user_pis_len].to_owned()) + .elements; + let rhs_base_pv_hash = builder + .hash_n_to_hash_no_pad::(rhs_public_values[..user_pis_len].to_owned()) + .elements; + + let lhs_hash: Vec = zip_eq(lhs_agg_pv_hash, lhs_base_pv_hash) + .map(|(&agg_target, base_target)| builder.select(lhs.is_agg, agg_target, base_target)) + .collect(); - let rhs_hash : Vec = zip_eq( - rhs_agg_pv_hash, - rhs_base_pv_hash, - ) - .map(|(&agg_target, base_target)| builder.select(rhs.is_agg, agg_target, base_target)) - .collect(); + let rhs_hash: Vec = zip_eq(rhs_agg_pv_hash, rhs_base_pv_hash) + .map(|(&agg_target, base_target)| builder.select(rhs.is_agg, agg_target, base_target)) + .collect(); let mut mix_vec = vec![]; mix_vec.extend(&lhs_hash); @@ -1683,7 +1681,6 @@ where &self.two_to_one_block.circuit.verifier_only, ); - let verification_key_len = self.two_to_one_block.vk_len; // // The number of PIS that will be added after padding by @@ -1698,23 +1695,34 @@ where // * (NUM_HASH_OUT_ELTS); let user_pis_len = lhs.public_inputs.len() - verification_key_len; + log::info!("user_pis_len: {user_pis_len}"); - let lhs_pv_hash = if lhs_is_agg { - let elements = lhs.public_inputs[..NUM_HASH_OUT_ELTS].try_into().expect("wrong length"); - HashOut { elements } - } else { - C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]) + let lelements = lhs.public_inputs[..NUM_HASH_OUT_ELTS] + .try_into() + .expect("wrong length"); + let lhsagg = HashOut { + elements: lelements, }; - - let rhs_pv_hash = if rhs_is_agg { - //extract_aggregation_hash(&rhs.public_inputs) - let elements = rhs.public_inputs[..NUM_HASH_OUT_ELTS].try_into().expect("wrong length"); - HashOut { elements } - } else { - C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]) + let lhsbase = C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]); + let lhs_pv_hash = if lhs_is_agg { lhsagg } else { lhsbase }; + + let relements = rhs.public_inputs[..NUM_HASH_OUT_ELTS] + .try_into() + .expect("wrong length"); + let rhsagg = HashOut { + elements: relements, }; + let rhsbase = C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]); + let rhs_pv_hash = if rhs_is_agg { rhsagg } else { rhsbase }; + log::info!("lhsPIS: {:?}", lhs.public_inputs); + log::info!("rhsPIS: {:?}", rhs.public_inputs); let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); + log::info!("lhsagg: {lhsagg:?}"); + log::info!("lhsbase: {lhsbase:?}"); + log::info!("rhsagg: {rhsagg:?}"); + log::info!("rhsbase: {rhsbase:?}"); + log::info!("mix: {mix_pv_hash:?}"); witness.set_hash_target(self.two_to_one_block.mix_pv_hash, mix_pv_hash); let proof = self.two_to_one_block.circuit.prove(witness)?; @@ -2059,12 +2067,12 @@ fn shrinking_config() -> CircuitConfig { } } -pub fn extract_aggregation_hash(proof: &ProofWithPublicInputs) -> HashOut -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, +pub fn extract_aggregation_hash(public_inputs: &Vec) -> &[F; NUM_HASH_OUT_ELTS] +// where +// F: Sized + Default + Copy, { - let elements: [F;NUM_HASH_OUT_ELTS] = proof.public_inputs[0..NUM_HASH_OUT_ELTS].try_into().expect("Malformed proof"); - HashOut::::try_from(elements).expect("Malformed proof") -} \ No newline at end of file + // let mut res = [Default::default(); NUM_HASH_OUT_ELTS]; + // res.clone_from_slice(&public_inputs[0..NUM_HASH_OUT_ELTS]); + // res + public_inputs[0..NUM_HASH_OUT_ELTS].try_into().expect("Public Inputs were malformed") +} diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 50261ecde..3072cc968 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -13,7 +13,7 @@ use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; -use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, Hasher}; +use plonky2::plonk::config::{GenericConfig, Hasher, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; @@ -291,17 +291,41 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { all_circuits.verify_two_to_one_block(&aggproof0123)?; let hash_no_pad = >::InnerHasher::hash_no_pad; - let two_to_one= >::InnerHasher::two_to_one; + let two_to_one = >::InnerHasher::two_to_one; + + log::info!("Leaf hashes"); + let mut hashes: Vec<_> = bp + .iter() + .map(|bp| { + log::info!( + "bppis: {:?} + vk: {:?} total_len: {}, vk_len: {}", + &bp.public_inputs, + &bp.public_inputs[2201..], + &bp.public_inputs.len(), + &bp.public_inputs.len() - 2201 + ); + hash_no_pad(&bp.public_inputs[..2201]) + }) + .collect(); + for (i, h) in hashes.iter().enumerate() { + log::info!("{}:\n{:?}", i, h); + } - let mut hashes : Vec<_> = bp.iter().map(|bp| hash_no_pad(&bp.public_inputs)).collect(); + log::info!("Inner hashes"); hashes.extend_from_within(0..hashes.len()); - (hashes.len()); - for i in hashes.len()/2..hashes.len()-1 { - hashes[i] = two_to_one(hashes[i/2], hashes[i/2+1]); + assert_eq!(hashes.len(), 8); + let half = hashes.len() / 2; + for i in 0..half - 1 { + hashes[i + half] = two_to_one(hashes[2 * i], hashes[2 * i + 1]); + log::info!("{i} lhs: {:?}", hashes[2 * i]); + log::info!("{i} rhs: {:?}", hashes[2 * i + 1]); + log::info!("{i} mix: {:?}", hashes[half + i]); } - assert_eq!(&aggproof0123.public_inputs[..NUM_HASH_OUT_ELTS], hashes[hashes.len()-2].elements, "Merkle root of verification tree did not match."); - return Ok(()) // TODO REMOVE THIS - + assert_eq!( + &aggproof0123.public_inputs[..NUM_HASH_OUT_ELTS], + hashes[hashes.len() - 2].elements, + "Merkle root of verification tree did not match." + ); } { From c7cad0054357b6247127cc0a99d72c9625009d8a Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Sat, 29 Jun 2024 20:14:49 +0800 Subject: [PATCH 45/59] review: refactor: remove scaffolding --- .../src/fixed_recursive_verifier.rs | 51 +++++------- evm_arithmetization/tests/two_to_one_block.rs | 81 ++++++++++--------- 2 files changed, 65 insertions(+), 67 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 6277aa86e..0120305a2 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1025,8 +1025,8 @@ where let lhs_public_values = lhs.public_values_vec(&mut builder); let rhs_public_values = rhs.public_values_vec(&mut builder); - let lhs_agg_pv_hash = &lhs_public_values[..NUM_HASH_OUT_ELTS]; - let rhs_agg_pv_hash = &rhs_public_values[..NUM_HASH_OUT_ELTS]; + let lhs_agg_pv_hash = extract_two_to_one_block_hash(&lhs_public_values); + let rhs_agg_pv_hash = extract_two_to_one_block_hash(&rhs_public_values); let lhs_base_pv_hash = builder .hash_n_to_hash_no_pad::(lhs_public_values[..user_pis_len].to_owned()) @@ -1681,6 +1681,7 @@ where &self.two_to_one_block.circuit.verifier_only, ); + // TODO let verification_key_len = self.two_to_one_block.vk_len; // // The number of PIS that will be added after padding by @@ -1697,32 +1698,23 @@ where let user_pis_len = lhs.public_inputs.len() - verification_key_len; log::info!("user_pis_len: {user_pis_len}"); - let lelements = lhs.public_inputs[..NUM_HASH_OUT_ELTS] - .try_into() - .expect("wrong length"); - let lhsagg = HashOut { - elements: lelements, + let lhs_pv_hash = if lhs_is_agg { + HashOut { + elements: *extract_two_to_one_block_hash(&lhs.public_inputs), + } + } else { + C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]) }; - let lhsbase = C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]); - let lhs_pv_hash = if lhs_is_agg { lhsagg } else { lhsbase }; - - let relements = rhs.public_inputs[..NUM_HASH_OUT_ELTS] - .try_into() - .expect("wrong length"); - let rhsagg = HashOut { - elements: relements, + + let rhs_pv_hash = if rhs_is_agg { + HashOut { + elements: *extract_two_to_one_block_hash(&rhs.public_inputs), + } + } else { + C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]) }; - let rhsbase = C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]); - let rhs_pv_hash = if rhs_is_agg { rhsagg } else { rhsbase }; - log::info!("lhsPIS: {:?}", lhs.public_inputs); - log::info!("rhsPIS: {:?}", rhs.public_inputs); let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); - log::info!("lhsagg: {lhsagg:?}"); - log::info!("lhsbase: {lhsbase:?}"); - log::info!("rhsagg: {rhsagg:?}"); - log::info!("rhsbase: {rhsbase:?}"); - log::info!("mix: {mix_pv_hash:?}"); witness.set_hash_target(self.two_to_one_block.mix_pv_hash, mix_pv_hash); let proof = self.two_to_one_block.circuit.prove(witness)?; @@ -2067,12 +2059,9 @@ fn shrinking_config() -> CircuitConfig { } } -pub fn extract_aggregation_hash(public_inputs: &Vec) -> &[F; NUM_HASH_OUT_ELTS] -// where -// F: Sized + Default + Copy, +pub fn extract_two_to_one_block_hash(public_inputs: &Vec) -> &[F; NUM_HASH_OUT_ELTS] { - // let mut res = [Default::default(); NUM_HASH_OUT_ELTS]; - // res.clone_from_slice(&public_inputs[0..NUM_HASH_OUT_ELTS]); - // res - public_inputs[0..NUM_HASH_OUT_ELTS].try_into().expect("Public Inputs were malformed") + public_inputs[0..NUM_HASH_OUT_ELTS] + .try_into() + .expect("Public Inputs were malformed") } diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 3072cc968..bf30ffe45 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; +use evm_arithmetization::fixed_recursive_verifier::extract_two_to_one_block_hash; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; @@ -12,7 +13,6 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::{GenericConfig, Hasher, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; @@ -290,42 +290,51 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { all_circuits.prove_two_to_one_block(&aggproof01, true, &aggproof23, true)?; all_circuits.verify_two_to_one_block(&aggproof0123)?; - let hash_no_pad = >::InnerHasher::hash_no_pad; - let two_to_one = >::InnerHasher::two_to_one; - - log::info!("Leaf hashes"); - let mut hashes: Vec<_> = bp - .iter() - .map(|bp| { - log::info!( - "bppis: {:?} + vk: {:?} total_len: {}, vk_len: {}", - &bp.public_inputs, - &bp.public_inputs[2201..], - &bp.public_inputs.len(), - &bp.public_inputs.len() - 2201 - ); - hash_no_pad(&bp.public_inputs[..2201]) - }) - .collect(); - for (i, h) in hashes.iter().enumerate() { - log::info!("{}:\n{:?}", i, h); + { + // Compute Merkle root from public inputs of block proofs. + let hash_no_pad = + >::InnerHasher::hash_no_pad; + let two_to_one = + >::InnerHasher::two_to_one; + // TODO: compute this + let user_pis_len = 2201; + + log::info!("Leaf hashes"); + let mut hashes: Vec<_> = bp + .iter() + .map(|bp| { + log::info!( + "bppis: {:?} + vk: {:?} total_len: {}, vk_len: {}", + &bp.public_inputs, + &bp.public_inputs[user_pis_len..], + &bp.public_inputs.len(), + &bp.public_inputs.len() - user_pis_len + ); + hash_no_pad(&bp.public_inputs[..user_pis_len]) + }) + .collect(); + for (i, h) in hashes.iter().enumerate() { + log::info!("{}:\n{:?}", i, h); + } + + log::info!("Inner hashes"); + hashes.extend_from_within(0..hashes.len()); + assert_eq!(hashes.len(), 8); + let half = hashes.len() / 2; + for i in 0..half - 1 { + hashes[half + i] = two_to_one(hashes[2 * i], hashes[2 * i + 1]); + log::info!("{i} lhs: {:?}", hashes[2 * i]); + log::info!("{i} rhs: {:?}", hashes[2 * i + 1]); + log::info!("{i} mix: {:?}", hashes[half + i]); + } + let merkle_root = hashes[hashes.len() - 2].elements; + + assert_eq!( + extract_two_to_one_block_hash(&aggproof0123.public_inputs), + &merkle_root, + "Merkle root of verifier's verification tree did not match merkle root in public inputs." + ); } - - log::info!("Inner hashes"); - hashes.extend_from_within(0..hashes.len()); - assert_eq!(hashes.len(), 8); - let half = hashes.len() / 2; - for i in 0..half - 1 { - hashes[i + half] = two_to_one(hashes[2 * i], hashes[2 * i + 1]); - log::info!("{i} lhs: {:?}", hashes[2 * i]); - log::info!("{i} rhs: {:?}", hashes[2 * i + 1]); - log::info!("{i} mix: {:?}", hashes[half + i]); - } - assert_eq!( - &aggproof0123.public_inputs[..NUM_HASH_OUT_ELTS], - hashes[hashes.len() - 2].elements, - "Merkle root of verification tree did not match." - ); } { From 3c56dcaf82c9bc9bd7112ee04617448f23314ea2 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Sat, 29 Jun 2024 20:58:51 +0800 Subject: [PATCH 46/59] review: fix ascii art --- evm_arithmetization/tests/two_to_one_block.rs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index bf30ffe45..c2eeee28a 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -272,14 +272,14 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { } { - // Aggregate a sequential proof containing three proofs with the structure - // `((A,B),(C,D))`. + // Binary tree reduction // // A B C D Blockproofs (base case) // \ / \ / // (A, B) (C, D) Two-to-one block aggregation proofs // \ / // ((A,B), (C,D)) Two-to-one block aggregation proofs + let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; all_circuits.verify_two_to_one_block(&aggproof01)?; @@ -302,15 +302,15 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { log::info!("Leaf hashes"); let mut hashes: Vec<_> = bp .iter() - .map(|bp| { + .map(|block_proof| { log::info!( "bppis: {:?} + vk: {:?} total_len: {}, vk_len: {}", - &bp.public_inputs, - &bp.public_inputs[user_pis_len..], - &bp.public_inputs.len(), - &bp.public_inputs.len() - user_pis_len + &block_proof.public_inputs, + &block_proof.public_inputs[user_pis_len..], + &block_proof.public_inputs.len(), + &block_proof.public_inputs.len() - user_pis_len ); - hash_no_pad(&bp.public_inputs[..user_pis_len]) + hash_no_pad(&block_proof.public_inputs[..user_pis_len]) }) .collect(); for (i, h) in hashes.iter().enumerate() { @@ -339,14 +339,14 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { { // Foldleft - // Aggregate a sequential /// proof containing three proofs with the structure - // `((A,B),(C,D))`. // - // A B C Blockproofs (base case) - // \ / / - // (A, B) / Two-to-one block aggregation proofs - // \ / - // ((A,B), C) Two-to-one block aggregation proofs + // A B C D Blockproofs (base case) + // \ / / / + // (A, B) / / Two-to-one block aggregation proofs + // \ / / + // ((A,B), C) / Two-to-one block aggregation proofs + // \ / + // (((A,B),C),D) Two-to-one block aggregation proofs let aggproof01 = all_circuits.prove_two_to_one_block(&bp[0], false, &bp[1], false)?; all_circuits.verify_two_to_one_block(&aggproof01)?; @@ -361,11 +361,14 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { { // Foldright - // A B C Blockproofs (base case) - // \ \ / - // \ (B,C) Two-to-one block aggregation proofs - // \ / - // (A,(B, C)) Two-to-one block aggregation proofs + // + // A B C D Blockproofs (base case) + // \ \ \ / + // \ \ (C,D) Two-to-one block aggregation proofs + // \ \ / + // \ (B,(C, D)) Two-to-one block aggregation proofs + // \ / + // (A,(B,(C,D))) Two-to-one block aggregation proofs let aggproof23 = all_circuits.prove_two_to_one_block(&bp[2], false, &bp[3], false)?; all_circuits.verify_two_to_one_block(&aggproof23)?; From 7283beeabe4973163b40d397d63be4a66821ba03 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Sun, 30 Jun 2024 00:57:25 +0800 Subject: [PATCH 47/59] review: clippy --- .../src/fixed_recursive_verifier.rs | 60 ++++++++----------- evm_arithmetization/tests/two_to_one_block.rs | 16 +++-- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 0120305a2..4e3d57467 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -330,7 +330,6 @@ where rhs: AggregationChildTarget, mix_pv_hash: HashOutTarget, dummy_pis: Vec, - vk_len: usize, cyclic_vk: VerifierCircuitTarget, } @@ -372,7 +371,6 @@ where mix_pv_hash, dummy_pis, cyclic_vk, - vk_len: todo!(), }) } } @@ -989,25 +987,17 @@ where { let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); - let mix_pv_hash_output = builder.add_virtual_hash(); - builder.register_public_inputs(&mix_pv_hash_output.elements); + let mix_pv_hash = builder.add_virtual_hash(); + builder.register_public_inputs(&mix_pv_hash.elements); // The number of PIS that will be added after padding by // `builder.add_verifier_data_public_inputs()`. - let verification_key_len = block_circuit - .circuit - .verifier_only - .circuit_digest - .elements - .len() - + (1 << block_circuit.circuit.common.config.fri_config.cap_height) - * (NUM_HASH_OUT_ELTS); + let verification_key_len = verification_key_len(&block_circuit.circuit); // 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; padding -= verification_key_len; padding -= builder.num_public_inputs(); - //padding -= mix_pv_hash_output.elements.len(); let mut dummy_pis = vec![]; for _ in 0..padding { @@ -1047,19 +1037,18 @@ where mix_vec.extend(&lhs_hash); mix_vec.extend(&rhs_hash); - let mix_pv_hash = builder.hash_n_to_hash_no_pad::(mix_vec); + let mix_pv_hash_intermediate = builder.hash_n_to_hash_no_pad::(mix_vec); - builder.connect_hashes(mix_pv_hash_output, mix_pv_hash); + builder.connect_hashes(mix_pv_hash, mix_pv_hash_intermediate); let circuit = builder.build::(); TwoToOneBlockCircuitData { circuit, lhs, rhs, - mix_pv_hash: mix_pv_hash_output, + mix_pv_hash, dummy_pis, cyclic_vk, - vk_len: verification_key_len, } } @@ -1681,20 +1670,11 @@ where &self.two_to_one_block.circuit.verifier_only, ); - // TODO - let verification_key_len = self.two_to_one_block.vk_len; - - // // The number of PIS that will be added after padding by - // // `builder.add_verifier_data_public_inputs()`. - // let verification_key_len = block_circuit - // .circuit - // .verifier_only - // .circuit_digest - // .elements - // .len() - // + (1 << block_circuit.circuit.common.config.fri_config.cap_height) - // * (NUM_HASH_OUT_ELTS); - + // The number of PIS that will be added after padding by + // [`builder.add_verifier_data_public_inputs`]. + let verification_key_len = verification_key_len(&self.block.circuit); + assert_eq!(verification_key_len, 68); + debug_assert_eq!(lhs.public_inputs.len(), rhs.public_inputs.len()); let user_pis_len = lhs.public_inputs.len() - verification_key_len; log::info!("user_pis_len: {user_pis_len}"); @@ -2059,9 +2039,21 @@ fn shrinking_config() -> CircuitConfig { } } -pub fn extract_two_to_one_block_hash(public_inputs: &Vec) -> &[F; NUM_HASH_OUT_ELTS] -{ +/// Extracts the two-to-one block aggregation hash from a predefined location. +pub fn extract_two_to_one_block_hash(public_inputs: &[F]) -> &[F; NUM_HASH_OUT_ELTS] { public_inputs[0..NUM_HASH_OUT_ELTS] .try_into() - .expect("Public Inputs were malformed") + .expect("Public inputs vector was malformed.") +} + +/// Computes the length added to the public inputs vector by +/// [`CircuitBuilder::add_verifier_data_public_inputs`]. +pub fn verification_key_len(circuit: &CircuitData) -> usize +where + F: RichField + Extendable, + C: GenericConfig, + C::Hasher: AlgebraicHasher, +{ + circuit.verifier_only.circuit_digest.elements.len() + + (1 << circuit.common.config.fri_config.cap_height) * (NUM_HASH_OUT_ELTS) } diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index c2eeee28a..6fbd6cd66 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; -use evm_arithmetization::fixed_recursive_verifier::extract_two_to_one_block_hash; +use evm_arithmetization::fixed_recursive_verifier::{ + extract_two_to_one_block_hash, verification_key_len, +}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; @@ -297,18 +299,24 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { let two_to_one = >::InnerHasher::two_to_one; // TODO: compute this - let user_pis_len = 2201; + + let verification_key_len = verification_key_len(&all_circuits.block.circuit); log::info!("Leaf hashes"); let mut hashes: Vec<_> = bp .iter() .map(|block_proof| { + let user_pis_len = block_proof.public_inputs.len() - verification_key_len; + log::info!("{:#?}", user_pis_len); + log::info!("{:#?}", verification_key_len); + log::info!("{:#?}", block_proof.public_inputs.len()); + assert_eq!(user_pis_len, 2201); log::info!( "bppis: {:?} + vk: {:?} total_len: {}, vk_len: {}", &block_proof.public_inputs, &block_proof.public_inputs[user_pis_len..], - &block_proof.public_inputs.len(), - &block_proof.public_inputs.len() - user_pis_len + block_proof.public_inputs.len(), + block_proof.public_inputs.len() - user_pis_len ); hash_no_pad(&block_proof.public_inputs[..user_pis_len]) }) From 91bdf83422d66f955b15035b6d6fb5ea1ba80d25 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Sun, 30 Jun 2024 01:28:35 +0800 Subject: [PATCH 48/59] review: remove logging --- .../src/fixed_recursive_verifier.rs | 42 +++++++++---------- evm_arithmetization/tests/two_to_one_block.rs | 24 ++--------- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 4e3d57467..21dd74082 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -328,7 +328,7 @@ where pub circuit: CircuitData, lhs: AggregationChildTarget, rhs: AggregationChildTarget, - mix_pv_hash: HashOutTarget, + mix_hash: HashOutTarget, dummy_pis: Vec, cyclic_vk: VerifierCircuitTarget, } @@ -348,7 +348,7 @@ where self.lhs.to_buffer(buffer)?; self.rhs.to_buffer(buffer)?; buffer.write_target_vec(&self.dummy_pis)?; - buffer.write_target_hash(&self.mix_pv_hash)?; + buffer.write_target_hash(&self.mix_hash)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; Ok(()) } @@ -361,14 +361,14 @@ where let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; let lhs = AggregationChildTarget::from_buffer(buffer)?; let rhs = AggregationChildTarget::from_buffer(buffer)?; - let mix_pv_hash = buffer.read_target_hash()?; + let mix_hash = buffer.read_target_hash()?; let dummy_pis = buffer.read_target_vec()?; let cyclic_vk = buffer.read_target_verifier_circuit()?; Ok(Self { circuit, lhs, rhs, - mix_pv_hash, + mix_hash, dummy_pis, cyclic_vk, }) @@ -987,16 +987,14 @@ where { let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); - let mix_pv_hash = builder.add_virtual_hash(); - builder.register_public_inputs(&mix_pv_hash.elements); - - // The number of PIS that will be added after padding by - // `builder.add_verifier_data_public_inputs()`. - let verification_key_len = verification_key_len(&block_circuit.circuit); + let mix_hash = builder.add_virtual_hash(); + builder.register_public_inputs(&mix_hash.elements); // 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; - padding -= verification_key_len; + // The number of PIS that will be added after padding by `builder.add_verifier_data_public_inputs()`. + padding -= verification_key_len(&block_circuit.circuit); + // Account for `mix_pv_hash`. padding -= builder.num_public_inputs(); let mut dummy_pis = vec![]; @@ -1006,7 +1004,6 @@ where } let user_pis_len = builder.num_public_inputs(); - let cyclic_vk = builder.add_verifier_data_public_inputs(); let lhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); @@ -1037,16 +1034,16 @@ where mix_vec.extend(&lhs_hash); mix_vec.extend(&rhs_hash); - let mix_pv_hash_intermediate = builder.hash_n_to_hash_no_pad::(mix_vec); + let mix_hash_intermediate = builder.hash_n_to_hash_no_pad::(mix_vec); - builder.connect_hashes(mix_pv_hash, mix_pv_hash_intermediate); + builder.connect_hashes(mix_hash, mix_hash_intermediate); let circuit = builder.build::(); TwoToOneBlockCircuitData { circuit, lhs, rhs, - mix_pv_hash, + mix_hash, dummy_pis, cyclic_vk, } @@ -1670,15 +1667,14 @@ where &self.two_to_one_block.circuit.verifier_only, ); - // The number of PIS that will be added after padding by - // [`builder.add_verifier_data_public_inputs`]. let verification_key_len = verification_key_len(&self.block.circuit); - assert_eq!(verification_key_len, 68); debug_assert_eq!(lhs.public_inputs.len(), rhs.public_inputs.len()); + // The number of PIS that we want to hash in case of a block proof. let user_pis_len = lhs.public_inputs.len() - verification_key_len; - log::info!("user_pis_len: {user_pis_len}"); - let lhs_pv_hash = if lhs_is_agg { + // If `lhs_is_agg` we read the hash verbatim from the public inputs. + // Otherwise we hash the block proof's Public Values from PIS. + let lhs_hash = if lhs_is_agg { HashOut { elements: *extract_two_to_one_block_hash(&lhs.public_inputs), } @@ -1686,7 +1682,7 @@ where C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]) }; - let rhs_pv_hash = if rhs_is_agg { + let rhs_hash = if rhs_is_agg { HashOut { elements: *extract_two_to_one_block_hash(&rhs.public_inputs), } @@ -1694,8 +1690,8 @@ where C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]) }; - let mix_pv_hash = C::InnerHasher::two_to_one(lhs_pv_hash, rhs_pv_hash); - witness.set_hash_target(self.two_to_one_block.mix_pv_hash, mix_pv_hash); + let mix_hash = C::InnerHasher::two_to_one(lhs_hash, rhs_hash); + witness.set_hash_target(self.two_to_one_block.mix_hash, mix_hash); let proof = self.two_to_one_block.circuit.prove(witness)?; Ok(proof) diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 6fbd6cd66..46775e91a 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -298,42 +298,24 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { >::InnerHasher::hash_no_pad; let two_to_one = >::InnerHasher::two_to_one; - // TODO: compute this + // We do not want to include the verification key in the hash. let verification_key_len = verification_key_len(&all_circuits.block.circuit); - log::info!("Leaf hashes"); + // Leaves let mut hashes: Vec<_> = bp .iter() .map(|block_proof| { let user_pis_len = block_proof.public_inputs.len() - verification_key_len; - log::info!("{:#?}", user_pis_len); - log::info!("{:#?}", verification_key_len); - log::info!("{:#?}", block_proof.public_inputs.len()); - assert_eq!(user_pis_len, 2201); - log::info!( - "bppis: {:?} + vk: {:?} total_len: {}, vk_len: {}", - &block_proof.public_inputs, - &block_proof.public_inputs[user_pis_len..], - block_proof.public_inputs.len(), - block_proof.public_inputs.len() - user_pis_len - ); hash_no_pad(&block_proof.public_inputs[..user_pis_len]) }) .collect(); - for (i, h) in hashes.iter().enumerate() { - log::info!("{}:\n{:?}", i, h); - } - log::info!("Inner hashes"); + // Inner nodes hashes.extend_from_within(0..hashes.len()); - assert_eq!(hashes.len(), 8); let half = hashes.len() / 2; for i in 0..half - 1 { hashes[half + i] = two_to_one(hashes[2 * i], hashes[2 * i + 1]); - log::info!("{i} lhs: {:?}", hashes[2 * i]); - log::info!("{i} rhs: {:?}", hashes[2 * i + 1]); - log::info!("{i} mix: {:?}", hashes[half + i]); } let merkle_root = hashes[hashes.len() - 2].elements; From 2d0162f599980b855b20378492a712c42a8dc501 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Sun, 30 Jun 2024 01:30:08 +0800 Subject: [PATCH 49/59] review: fmt --- evm_arithmetization/src/fixed_recursive_verifier.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 21dd74082..7d1076270 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -992,7 +992,8 @@ where // 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; - // The number of PIS that will be added after padding by `builder.add_verifier_data_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); // Account for `mix_pv_hash`. padding -= builder.num_public_inputs(); From ba8372f4f5f0990fa3d300583e10977c39092271 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 1 Jul 2024 22:55:03 +0800 Subject: [PATCH 50/59] review: remove redundant computations --- .../src/fixed_recursive_verifier.rs | 44 +------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 7d1076270..4a38ab8eb 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -328,8 +328,6 @@ where pub circuit: CircuitData, lhs: AggregationChildTarget, rhs: AggregationChildTarget, - mix_hash: HashOutTarget, - dummy_pis: Vec, cyclic_vk: VerifierCircuitTarget, } @@ -347,8 +345,6 @@ where buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; self.lhs.to_buffer(buffer)?; self.rhs.to_buffer(buffer)?; - buffer.write_target_vec(&self.dummy_pis)?; - buffer.write_target_hash(&self.mix_hash)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; Ok(()) } @@ -361,15 +357,11 @@ where let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; let lhs = AggregationChildTarget::from_buffer(buffer)?; let rhs = AggregationChildTarget::from_buffer(buffer)?; - let mix_hash = buffer.read_target_hash()?; - let dummy_pis = buffer.read_target_vec()?; let cyclic_vk = buffer.read_target_verifier_circuit()?; Ok(Self { circuit, lhs, rhs, - mix_hash, - dummy_pis, cyclic_vk, }) } @@ -998,10 +990,9 @@ where // Account for `mix_pv_hash`. padding -= builder.num_public_inputs(); - let mut dummy_pis = vec![]; for _ in 0..padding { - let target = builder.add_virtual_public_input(); - dummy_pis.push(target); + let target = builder.zero(); + builder.register_public_input(target); } let user_pis_len = builder.num_public_inputs(); @@ -1044,8 +1035,6 @@ where circuit, lhs, rhs, - mix_hash, - dummy_pis, cyclic_vk, } } @@ -1644,9 +1633,6 @@ where ) -> anyhow::Result> { let mut witness = PartialWitness::new(); - let dummy_pis = &self.two_to_one_block.dummy_pis; - witness.set_target_arr(dummy_pis, &vec![F::ZERO; dummy_pis.len()]); - Self::set_dummy_if_necessary( &self.two_to_one_block.lhs, lhs_is_agg, @@ -1668,32 +1654,6 @@ where &self.two_to_one_block.circuit.verifier_only, ); - let verification_key_len = verification_key_len(&self.block.circuit); - debug_assert_eq!(lhs.public_inputs.len(), rhs.public_inputs.len()); - // The number of PIS that we want to hash in case of a block proof. - let user_pis_len = lhs.public_inputs.len() - verification_key_len; - - // If `lhs_is_agg` we read the hash verbatim from the public inputs. - // Otherwise we hash the block proof's Public Values from PIS. - let lhs_hash = if lhs_is_agg { - HashOut { - elements: *extract_two_to_one_block_hash(&lhs.public_inputs), - } - } else { - C::InnerHasher::hash_no_pad(&lhs.public_inputs[..user_pis_len]) - }; - - let rhs_hash = if rhs_is_agg { - HashOut { - elements: *extract_two_to_one_block_hash(&rhs.public_inputs), - } - } else { - C::InnerHasher::hash_no_pad(&rhs.public_inputs[..user_pis_len]) - }; - - let mix_hash = C::InnerHasher::two_to_one(lhs_hash, rhs_hash); - witness.set_hash_target(self.two_to_one_block.mix_hash, mix_hash); - let proof = self.two_to_one_block.circuit.prove(witness)?; Ok(proof) } From 6546c25031deb998bd7b8073ac3fbeab2e100534 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Tue, 2 Jul 2024 20:28:51 +0800 Subject: [PATCH 51/59] fmt --- .../src/fixed_recursive_verifier.rs | 24 +++++++++---------- evm_arithmetization/tests/two_to_one_block.rs | 21 +++++----------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 4a38ab8eb..a275e3767 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -9,11 +9,10 @@ use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; use mpt_trie::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use plonky2::field::extension::Extendable; -use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::fri::FriParams; use plonky2::gates::constant::ConstantGate; use plonky2::gates::noop::NoopGate; -use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField, NUM_HASH_OUT_ELTS}; +use plonky2::hash::hash_types::{RichField, NUM_HASH_OUT_ELTS}; use plonky2::iop::challenger::RecursiveChallenger; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; @@ -21,7 +20,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{ CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget, }; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut, Hasher}; +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; @@ -246,7 +245,7 @@ impl AggregationChildTarget { PublicValuesTarget::select(builder, self.is_agg, agg_pv, evm_pv) } - fn public_values_vec>( + fn public_inputs>( &self, builder: &mut CircuitBuilder, ) -> Vec { @@ -399,6 +398,8 @@ where .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; self.block .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + self.two_to_one_block + .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; if !skip_tables { for table in &self.by_table { table.to_buffer(&mut buffer, gate_serializer, generator_serializer)?; @@ -979,8 +980,7 @@ where { let mut builder = CircuitBuilder::::new(block_circuit.circuit.common.config.clone()); - let mix_hash = builder.add_virtual_hash(); - builder.register_public_inputs(&mix_hash.elements); + 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; @@ -1001,8 +1001,8 @@ where let lhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); let rhs = Self::add_agg_child(&mut builder, &block_circuit.circuit); - let lhs_public_values = lhs.public_values_vec(&mut builder); - let rhs_public_values = rhs.public_values_vec(&mut builder); + let lhs_public_values = lhs.public_inputs(&mut builder); + let rhs_public_values = rhs.public_inputs(&mut builder); let lhs_agg_pv_hash = extract_two_to_one_block_hash(&lhs_public_values); let rhs_agg_pv_hash = extract_two_to_one_block_hash(&rhs_public_values); @@ -1026,9 +1026,9 @@ where mix_vec.extend(&lhs_hash); mix_vec.extend(&rhs_hash); - let mix_hash_intermediate = builder.hash_n_to_hash_no_pad::(mix_vec); + let mix_hash_virtual = builder.hash_n_to_hash_no_pad::(mix_vec); - builder.connect_hashes(mix_hash, mix_hash_intermediate); + builder.connect_hashes(mix_hash, mix_hash_virtual); let circuit = builder.build::(); TwoToOneBlockCircuitData { @@ -1671,7 +1671,7 @@ where &self, proof: &ProofWithPublicInputs, ) -> anyhow::Result<()> { - self.two_to_one_block.circuit.verify(proof.clone()); + self.two_to_one_block.circuit.verify(proof.clone())?; let verifier_data = &self.two_to_one_block.circuit.verifier_data(); check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } @@ -1689,7 +1689,7 @@ where ) { let ProofWithPublicInputs { proof: base_proof, - public_inputs: base_public_inputs, + public_inputs: _, } = base_proof_with_pis; let ProofWithPublicInputsTarget { proof: agg_proof_targets, diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 46775e91a..3ac319270 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -8,7 +8,7 @@ use evm_arithmetization::fixed_recursive_verifier::{ }; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; -use evm_arithmetization::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; +use evm_arithmetization::proof::{BlockMetadata, PublicValues, TrieRoots}; use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; use hex_literal::hex; use keccak_hash::keccak; @@ -147,26 +147,18 @@ fn empty_transfer(timestamp: u64) -> anyhow::Result { receipts_root: receipts_trie.hash(), }; - let tries_after = TrieRoots { + let trie_roots_after = TrieRoots { state_root: tries_before.state_trie.hash(), transactions_root: tries_before.transactions_trie.hash(), receipts_root: tries_before.receipts_trie.hash(), }; let inputs = GenerationInputs { - signed_txn: None, - withdrawals: vec![], tries: tries_before.clone(), - trie_roots_after: tries_after, + trie_roots_after, contract_code, checkpoint_state_trie_root: tries_before.state_trie.hash(), block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 0.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, + ..Default::default() }; Ok(inputs) @@ -174,7 +166,6 @@ fn empty_transfer(timestamp: u64) -> anyhow::Result { fn get_test_block_proof( timestamp: u64, - timing: &mut TimingTree, all_circuits: &AllRecursiveCircuits, all_stark: &AllStark, config: &StarkConfig, @@ -204,6 +195,7 @@ fn get_test_block_proof( block_hashes: inputs.block_hashes.clone(), }; + let timing = &mut TimingTree::new(&format!("Blockproof {timestamp}"), log::Level::Info); let (root_proof0, pv0) = all_circuits.prove_root(all_stark, config, inputs0, timing, None)?; all_circuits.verify_root(root_proof0.clone())?; let (dummy_proof0, dummy_pv0) = @@ -254,11 +246,10 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], &config, ); - let mut timing = TimingTree::new("prove root first", log::Level::Info); let unrelated_block_proofs = some_timestamps .iter() - .map(|&ts| get_test_block_proof(ts, &mut timing, &all_circuits, &all_stark, &config)) + .map(|&ts| get_test_block_proof(ts, &all_circuits, &all_stark, &config)) .collect::>>>()?; unrelated_block_proofs From 88cb6e52b9a72ea6e1c9cd21727c41b901914640 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 3 Jul 2024 15:58:46 +0800 Subject: [PATCH 52/59] review: implement feedback from Hamy --- .../src/fixed_recursive_verifier.rs | 44 +++++++++++-------- evm_arithmetization/src/proof.rs | 15 +++---- evm_arithmetization/tests/two_to_one_block.rs | 11 ++--- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index a275e3767..d4690332a 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -228,11 +228,11 @@ impl AggregationChildTarget { 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 base_proof = buffer.read_target_proof_with_public_inputs()?; Ok(Self { is_agg, agg_proof, - base_proof: evm_proof, + base_proof, }) } @@ -241,8 +241,8 @@ impl AggregationChildTarget { builder: &mut CircuitBuilder, ) -> PublicValuesTarget { let agg_pv = PublicValuesTarget::from_public_inputs(&self.agg_proof.public_inputs); - let evm_pv = PublicValuesTarget::from_public_inputs(&self.base_proof.public_inputs); - PublicValuesTarget::select(builder, self.is_agg, agg_pv, evm_pv) + let base_pv = PublicValuesTarget::from_public_inputs(&self.base_proof.public_inputs); + PublicValuesTarget::select(builder, self.is_agg, agg_pv, base_pv) } fn public_inputs>( @@ -253,7 +253,7 @@ impl AggregationChildTarget { &self.agg_proof.public_inputs, &self.base_proof.public_inputs, ) - .map(|(&agg_pv, &block_pv)| builder.select(self.is_agg, agg_pv, block_pv)) + .map(|(&agg_pv, &base_pv)| builder.select(self.is_agg, agg_pv, base_pv)) .collect() } } @@ -845,8 +845,7 @@ where builder.connect(lhs.gas_used_after, rhs.gas_used_before); } - /// Setup a new part of a circuit to handle a new unrelated proof. - /// Helper method for [`create_two_to_one_block_circuit`]. + /// Extend a circuit to verify one of two proofs. /// /// # Arguments /// @@ -964,7 +963,7 @@ where /// /// # Arguments /// - /// - `block_circuit`: circuit data for the block circuit, that constitues + /// - `block_circuit`: circuit data for the block circuit, that constitutes /// the base case for aggregation. /// /// # Outputs @@ -990,28 +989,30 @@ where // Account for `mix_pv_hash`. padding -= builder.num_public_inputs(); + let zero = builder.zero(); for _ in 0..padding { - let target = builder.zero(); - builder.register_public_input(target); + builder.register_public_input(zero); } - let user_pis_len = builder.num_public_inputs(); 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_public_values = lhs.public_inputs(&mut builder); - let rhs_public_values = rhs.public_inputs(&mut builder); + 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_agg_pv_hash = extract_two_to_one_block_hash(&lhs_public_values); - let rhs_agg_pv_hash = extract_two_to_one_block_hash(&rhs_public_values); + 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); let lhs_base_pv_hash = builder - .hash_n_to_hash_no_pad::(lhs_public_values[..user_pis_len].to_owned()) + .hash_n_to_hash_no_pad::(lhs_public_values.to_vec()) .elements; let rhs_base_pv_hash = builder - .hash_n_to_hash_no_pad::(rhs_public_values[..user_pis_len].to_owned()) + .hash_n_to_hash_no_pad::(rhs_public_values.to_vec()) .elements; let lhs_hash: Vec = zip_eq(lhs_agg_pv_hash, lhs_base_pv_hash) @@ -1997,12 +1998,19 @@ fn shrinking_config() -> CircuitConfig { } /// Extracts the two-to-one block aggregation hash from a predefined location. -pub fn extract_two_to_one_block_hash(public_inputs: &[F]) -> &[F; NUM_HASH_OUT_ELTS] { +pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OUT_ELTS] { public_inputs[0..NUM_HASH_OUT_ELTS] .try_into() .expect("Public inputs vector was malformed.") } +/// Extracts the two-to-one block aggregation hash from a predefined location. +pub fn extract_block_public_values(public_inputs: &[T]) -> &[T; PublicValuesTarget::SIZE] { + public_inputs[0..PublicValuesTarget::SIZE] + .try_into() + .expect("Public inputs vector was malformed.") +} + /// Computes the length added to the public inputs vector by /// [`CircuitBuilder::add_verifier_data_public_inputs`]. pub fn verification_key_len(circuit: &CircuitData) -> usize diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 338eda2fa..380bd318a 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -59,14 +59,7 @@ impl PublicValues { /// 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!( - pis.len() - > TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + ExtraBlockDataTarget::SIZE - - 1 - ); + assert!(PublicValuesTarget::SIZE <= pis.len()); let trie_roots_before = TrieRoots::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]); let trie_roots_after = @@ -279,6 +272,10 @@ pub struct PublicValuesTarget { } impl PublicValuesTarget { + pub(crate) const SIZE: usize = TrieRootsTarget::SIZE * 2 + + BlockMetadataTarget::SIZE + + BlockHashesTarget::SIZE + + ExtraBlockDataTarget::SIZE; /// Serializes public value targets. pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { let TrieRootsTarget { @@ -758,7 +755,7 @@ pub struct ExtraBlockDataTarget { impl ExtraBlockDataTarget { /// Number of `Target`s required for the extra block data. - const SIZE: usize = 12; + pub(crate) const SIZE: usize = 12; /// Extracts the extra block data `Target`s from the public input `Target`s. /// The provided `pis` should start with the extra vblock data. diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 3ac319270..63e0047b8 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -3,9 +3,7 @@ use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; -use evm_arithmetization::fixed_recursive_verifier::{ - extract_two_to_one_block_hash, verification_key_len, -}; +use evm_arithmetization::fixed_recursive_verifier::{extract_block_public_values, extract_two_to_one_block_hash}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockMetadata, PublicValues, TrieRoots}; @@ -290,15 +288,12 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { let two_to_one = >::InnerHasher::two_to_one; - // We do not want to include the verification key in the hash. - let verification_key_len = verification_key_len(&all_circuits.block.circuit); - // Leaves let mut hashes: Vec<_> = bp .iter() .map(|block_proof| { - let user_pis_len = block_proof.public_inputs.len() - verification_key_len; - hash_no_pad(&block_proof.public_inputs[..user_pis_len]) + let public_values = extract_block_public_values(&block_proof.public_inputs); + hash_no_pad(public_values) }) .collect(); From 0d6e2ac7da3ae85339d581480d50ce25260e7730 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 3 Jul 2024 16:19:36 +0800 Subject: [PATCH 53/59] fmt --- evm_arithmetization/tests/two_to_one_block.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 63e0047b8..4410f7d07 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; use ethereum_types::{Address, BigEndianHash, H256, U256}; -use evm_arithmetization::fixed_recursive_verifier::{extract_block_public_values, extract_two_to_one_block_hash}; +use evm_arithmetization::fixed_recursive_verifier::{ + extract_block_public_values, extract_two_to_one_block_hash, +}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockMetadata, PublicValues, TrieRoots}; From 308f71fb50876e4a37a72e0cfc0d73838428c50c Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 8 Jul 2024 18:37:17 +0800 Subject: [PATCH 54/59] review: const verifier_key_len --- evm_arithmetization/src/fixed_recursive_verifier.rs | 4 ++-- evm_arithmetization/tests/two_to_one_block.rs | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index d4690332a..fd18a103d 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -2013,12 +2013,12 @@ pub fn extract_block_public_values(public_inputs: &[T]) -> &[T; PublicValuesT /// Computes the length added to the public inputs vector by /// [`CircuitBuilder::add_verifier_data_public_inputs`]. -pub fn verification_key_len(circuit: &CircuitData) -> usize +pub const fn verification_key_len(circuit: &CircuitData) -> usize where F: RichField + Extendable, C: GenericConfig, C::Hasher: AlgebraicHasher, { circuit.verifier_only.circuit_digest.elements.len() - + (1 << circuit.common.config.fri_config.cap_height) * (NUM_HASH_OUT_ELTS) + + (1 << circuit.common.config.fri_config.cap_height) * NUM_HASH_OUT_ELTS } diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 4410f7d07..fe387cee2 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -15,7 +15,8 @@ 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::{GenericConfig, Hasher, PoseidonGoldilocksConfig}; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::plonk::config::{Hasher, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; @@ -285,17 +286,12 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { { // Compute Merkle root from public inputs of block proofs. - let hash_no_pad = - >::InnerHasher::hash_no_pad; - let two_to_one = - >::InnerHasher::two_to_one; - // Leaves let mut hashes: Vec<_> = bp .iter() .map(|block_proof| { let public_values = extract_block_public_values(&block_proof.public_inputs); - hash_no_pad(public_values) + PoseidonHash::hash_no_pad(public_values) }) .collect(); @@ -303,7 +299,7 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { hashes.extend_from_within(0..hashes.len()); let half = hashes.len() / 2; for i in 0..half - 1 { - hashes[half + i] = two_to_one(hashes[2 * i], hashes[2 * i + 1]); + hashes[half + i] = PoseidonHash::two_to_one(hashes[2 * i], hashes[2 * i + 1]); } let merkle_root = hashes[hashes.len() - 2].elements; From 27b6b22d3ec8f184a44249c1e4f608ee1cfa42f5 Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Mon, 8 Jul 2024 18:37:39 +0800 Subject: [PATCH 55/59] review: make offsets explicit --- evm_arithmetization/src/fixed_recursive_verifier.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index fd18a103d..60862d4d3 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1999,14 +1999,18 @@ fn shrinking_config() -> CircuitConfig { /// Extracts the two-to-one block aggregation hash from a predefined location. pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OUT_ELTS] { - public_inputs[0..NUM_HASH_OUT_ELTS] + const PV_HASH_INDEX_START: usize = 0; + const PV_HASH_INDEX_END: usize = PV_HASH_INDEX_START + NUM_HASH_OUT_ELTS; + public_inputs[PV_HASH_INDEX_START..PV_HASH_INDEX_END] .try_into() .expect("Public inputs vector was malformed.") } /// Extracts the two-to-one block aggregation hash from a predefined location. pub fn extract_block_public_values(public_inputs: &[T]) -> &[T; PublicValuesTarget::SIZE] { - public_inputs[0..PublicValuesTarget::SIZE] + const PV_INDEX_START: usize = 0; + const PV_INDEX_END: usize = PV_INDEX_START + PublicValuesTarget::SIZE; + public_inputs[PV_INDEX_START..PV_INDEX_END] .try_into() .expect("Public inputs vector was malformed.") } From 1c78102488321adf0f361ad12e4a68c0480ace1a Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 10 Jul 2024 13:17:05 +0800 Subject: [PATCH 56/59] review(docs): rewrite rustdoc for helper functions --- .../src/fixed_recursive_verifier.rs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 60862d4d3..e7db1f8e4 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1997,7 +1997,19 @@ fn shrinking_config() -> CircuitConfig { } } -/// Extracts the two-to-one block aggregation hash from a predefined location. +/// Extracts the two-to-one block aggregation hash from a public inputs slice. +/// +/// # Arguments +/// +/// - `public_inputs`: A slice of public inputs originating from the aggregation +/// case of a two-to-one block proof. This slice must consist of a hash, either +/// of public values, or of two concatenated hashes. The hash must start at +/// offset zero of the slice and is typically followed by padding and then a +/// verifier key. It is an error to call this on a slice for a base proof. +/// +/// # Outputs +/// +/// - A slice containing exactly the hash. pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OUT_ELTS] { const PV_HASH_INDEX_START: usize = 0; const PV_HASH_INDEX_END: usize = PV_HASH_INDEX_START + NUM_HASH_OUT_ELTS; @@ -2006,7 +2018,19 @@ pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OU .expect("Public inputs vector was malformed.") } -/// Extracts the two-to-one block aggregation hash from a predefined location. +/// Extracts the two-to-one block aggregation public values of the block from +/// a public inputs slice. +/// +/// # Arguments +/// +/// - `public_inputs`: A slice of public inputs originating from the base case +/// of a two-to-one block proof. This slice must consist exactly of public +/// values starting at offset zero and is typically followed by a verifier key. +/// It is an error to call this function on a slice for an aggregation proof. +/// +/// # Outputs +/// +/// - A slice containing exactly the public values. pub fn extract_block_public_values(public_inputs: &[T]) -> &[T; PublicValuesTarget::SIZE] { const PV_INDEX_START: usize = 0; const PV_INDEX_END: usize = PV_INDEX_START + PublicValuesTarget::SIZE; From 542249510fd9312ff98c29332aead80328b74b6a Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Wed, 10 Jul 2024 11:41:21 +0800 Subject: [PATCH 57/59] review: clippy --- .../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 e7db1f8e4..0f3e1282f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -2002,10 +2002,10 @@ fn shrinking_config() -> CircuitConfig { /// # Arguments /// /// - `public_inputs`: A slice of public inputs originating from the aggregation -/// case of a two-to-one block proof. This slice must consist of a hash, either -/// of public values, or of two concatenated hashes. The hash must start at -/// offset zero of the slice and is typically followed by padding and then a -/// verifier key. It is an error to call this on a slice for a base proof. +/// case of a two-to-one block proof. This slice must consist of a hash, either +/// of public values, or of two concatenated hashes. The hash must start at +/// offset zero of the slice and is typically followed by padding and then a +/// verifier key. It is an error to call this on a slice for a base proof. /// /// # Outputs /// @@ -2024,9 +2024,9 @@ pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OU /// # Arguments /// /// - `public_inputs`: A slice of public inputs originating from the base case -/// of a two-to-one block proof. This slice must consist exactly of public -/// values starting at offset zero and is typically followed by a verifier key. -/// It is an error to call this function on a slice for an aggregation proof. +/// of a two-to-one block proof. This slice must consist exactly of public +/// values starting at offset zero and is typically followed by a verifier key. +/// It is an error to call this function on a slice for an aggregation proof. /// /// # Outputs /// From 4a44c91cefdc4019d93c587b3a60e54283c125ee Mon Sep 17 00:00:00 2001 From: Einar Rasmussen Date: Fri, 12 Jul 2024 00:44:32 +0800 Subject: [PATCH 58/59] fmt --- .../src/fixed_recursive_verifier.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 0f3e1282f..acddf8fe4 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -2002,10 +2002,11 @@ fn shrinking_config() -> CircuitConfig { /// # Arguments /// /// - `public_inputs`: A slice of public inputs originating from the aggregation -/// case of a two-to-one block proof. This slice must consist of a hash, either -/// of public values, or of two concatenated hashes. The hash must start at -/// offset zero of the slice and is typically followed by padding and then a -/// verifier key. It is an error to call this on a slice for a base proof. +/// case of a two-to-one block proof. This slice must consist of a hash, +/// either of public values, or of two concatenated hashes. The hash must +/// start at offset zero of the slice and is typically followed by padding and +/// then a verifier key. It is an error to call this on a slice for a base +/// proof. /// /// # Outputs /// @@ -2025,8 +2026,9 @@ pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OU /// /// - `public_inputs`: A slice of public inputs originating from the base case /// of a two-to-one block proof. This slice must consist exactly of public -/// values starting at offset zero and is typically followed by a verifier key. -/// It is an error to call this function on a slice for an aggregation proof. +/// values starting at offset zero and is typically followed by a verifier +/// key. It is an error to call this function on a slice for an aggregation +/// proof. /// /// # Outputs /// From 889fa99cd1614667cd9b51dd5a2c45e8ab6561ff Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 11 Jul 2024 13:12:39 -0400 Subject: [PATCH 59/59] Remove double spacing --- .../src/fixed_recursive_verifier.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index acddf8fe4..3fa6e208f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -1677,8 +1677,8 @@ where check_cyclic_proof_verifier_data(proof, &verifier_data.verifier_only, &verifier_data.common) } - /// Creates dummy public inputs with correct verifier key at the end. Used - /// by [`set_dummy_if_necessary`]. It cyclic vk to the aggregation circuit + /// Creates dummy public inputs with correct verifier key at the end. Used + /// by [`set_dummy_if_necessary`]. It 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. @@ -1716,7 +1716,7 @@ where /// If the [`AggregationChild`] is a base proof and not an aggregation /// proof, we need to manually set the public inputs vector of the otherwise /// inert `agg_proof`, so that they correspond to the `cyclic_vk` of the - /// aggregation circuit. The cyclic prover expects to find the `cyclic_vk` + /// aggregation circuit. The cyclic prover expects to find the `cyclic_vk` /// targets in the very end of the public inputs vector, and so it does not /// matter what the preceding values are. fn set_dummy_if_necessary( @@ -2002,10 +2002,10 @@ fn shrinking_config() -> CircuitConfig { /// # Arguments /// /// - `public_inputs`: A slice of public inputs originating from the aggregation -/// case of a two-to-one block proof. This slice must consist of a hash, -/// either of public values, or of two concatenated hashes. The hash must +/// case of a two-to-one block proof. This slice must consist of a hash, +/// either of public values, or of two concatenated hashes. The hash must /// start at offset zero of the slice and is typically followed by padding and -/// then a verifier key. It is an error to call this on a slice for a base +/// then a verifier key. It is an error to call this on a slice for a base /// proof. /// /// # Outputs @@ -2025,9 +2025,9 @@ pub fn extract_two_to_one_block_hash(public_inputs: &[T]) -> &[T; NUM_HASH_OU /// # Arguments /// /// - `public_inputs`: A slice of public inputs originating from the base case -/// of a two-to-one block proof. This slice must consist exactly of public +/// of a two-to-one block proof. This slice must consist exactly of public /// values starting at offset zero and is typically followed by a verifier -/// key. It is an error to call this function on a slice for an aggregation +/// key. It is an error to call this function on a slice for an aggregation /// proof. /// /// # Outputs