diff --git a/mp2-v1/src/api.rs b/mp2-v1/src/api.rs index 22c31accd..395661ae1 100644 --- a/mp2-v1/src/api.rs +++ b/mp2-v1/src/api.rs @@ -20,6 +20,7 @@ use anyhow::Result; use itertools::Itertools; use mp2_common::{ digest::Digest, + group_hashing::map_to_curve_point, poseidon::H, types::HashOutput, utils::{Fieldable, ToFields}, @@ -141,6 +142,9 @@ pub fn generate_proof(params: &PublicParameters, input: CircuitInput) -> Result< length_circuit_set, ) } + final_extraction::CircuitInput::NoProvable(input) => { + params.final_extraction.generate_no_provable_proof(input) + } } } CircuitInput::CellsTree(input) => verifiable_db::api::generate_proof( @@ -243,3 +247,20 @@ pub fn metadata_hash( // compute final hash combine_digest_and_block(contract_digest + value_digest) } + +/// Compute the metadata hash for a table including no provable extraction data. +/// The input is a metadata digest set by the caller, then generate the metadata hash +/// same as the DB computation. +pub fn no_provable_metadata_hash(metadata_digest: &Digest) -> MetadataHash { + // Add the prefix to the metadata digest to ensure the metadata digest + // will keep track of whether we use this dummy circuit or not. + // It's similar logic as the dummy circuit of final extraction. + let prefix = final_extraction::DUMMY_METADATA_DIGEST_PREFIX.to_fields(); + let inputs = prefix + .into_iter() + .chain(metadata_digest.to_fields()) + .collect_vec(); + let digest = map_to_curve_point(&inputs); + + combine_digest_and_block(digest) +} diff --git a/mp2-v1/src/final_extraction/api.rs b/mp2-v1/src/final_extraction/api.rs index ef152d684..335ef4b06 100644 --- a/mp2-v1/src/final_extraction/api.rs +++ b/mp2-v1/src/final_extraction/api.rs @@ -1,25 +1,36 @@ -use mp2_common::{self, default_config, digest::TableDimension, proof::ProofWithVK, C, D, F}; -use plonky2::{iop::target::Target, plonk::circuit_data::VerifierCircuitData}; +use alloy::primitives::U256; +use anyhow::Result; +use itertools::Itertools; +use mp2_common::{ + self, default_config, + digest::{Digest, TableDimension}, + proof::ProofWithVK, + types::HashOutput, + utils::Packer, + C, D, F, +}; +use plonky2::{field::types::Field, iop::target::Target, plonk::circuit_data::VerifierCircuitData}; use recursion_framework::{ circuit_builder::{CircuitWithUniversalVerifier, CircuitWithUniversalVerifierBuilder}, framework::{prepare_recursive_circuit_for_circuit_set, RecursiveCircuits}, }; - use serde::{Deserialize, Serialize}; use super::{ base_circuit::BaseCircuitInput, + dummy_circuit::DummyWires, lengthed_circuit::LengthedRecursiveWires, merge_circuit::{MergeTable, MergeTableRecursiveWires}, simple_circuit::SimpleCircuitRecursiveWires, - BaseCircuitProofInputs, LengthedCircuit, MergeCircuit, PublicInputs, SimpleCircuit, + BaseCircuitProofInputs, DummyCircuit, LengthedCircuit, MergeCircuit, PublicInputs, + SimpleCircuit, }; -use anyhow::Result; pub enum CircuitInput { Simple(SimpleCircuitInput), Lengthed(LengthedCircuitInput), MergeTable(MergeCircuitInput), + NoProvable(DummyCircuit), } #[derive(Clone, Debug)] pub struct FinalExtractionBuilderParams { @@ -51,10 +62,11 @@ pub struct PublicParameters { simple: CircuitWithUniversalVerifier, lengthed: CircuitWithUniversalVerifier, merge: CircuitWithUniversalVerifier, + dummy: CircuitWithUniversalVerifier, circuit_set: RecursiveCircuits, } -const FINAL_EXTRACTION_CIRCUIT_SET_SIZE: usize = 2; +const FINAL_EXTRACTION_CIRCUIT_SET_SIZE: usize = 4; pub(super) const NUM_IO: usize = PublicInputs::::TOTAL_LEN; impl PublicParameters { @@ -76,12 +88,14 @@ impl PublicParameters { ); let simple = builder.build_circuit(builder_params.clone()); let lengthed = builder.build_circuit(builder_params.clone()); - let merge = builder.build_circuit(builder_params); + let merge = builder.build_circuit(builder_params.clone()); + let dummy = builder.build_circuit(builder_params); let circuits = vec![ prepare_recursive_circuit_for_circuit_set(&simple), prepare_recursive_circuit_for_circuit_set(&lengthed), prepare_recursive_circuit_for_circuit_set(&merge), + prepare_recursive_circuit_for_circuit_set(&dummy), ]; let circuit_set = RecursiveCircuits::new(circuits); @@ -90,6 +104,7 @@ impl PublicParameters { simple, lengthed, merge, + dummy, circuit_set, } } @@ -160,6 +175,13 @@ impl PublicParameters { ProofWithVK::serialize(&(proof, self.lengthed.circuit_data().verifier_only.clone()).into()) } + pub(crate) fn generate_no_provable_proof(&self, input: DummyCircuit) -> Result> { + let proof = self + .circuit_set + .generate_proof(&self.dummy, [], [], input)?; + ProofWithVK::serialize(&(proof, self.dummy.circuit_data().verifier_only.clone()).into()) + } + pub(crate) fn get_circuit_set(&self) -> &RecursiveCircuits { &self.circuit_set } @@ -230,6 +252,33 @@ impl CircuitInput { let length_proof = ProofWithVK::deserialize(&length_proof)?; Ok(Self::Lengthed(LengthedCircuitInput { base, length_proof })) } + /// Instantiate inputs for the dummy circuit dealing with no provable extraction case + pub fn new_no_provable_input( + is_merge: bool, + block_number: U256, + block_hash: HashOutput, + prev_block_hash: HashOutput, + metadata_digest: Digest, + row_digest: Digest, + ) -> Self { + let [block_hash, prev_block_hash] = [block_hash, prev_block_hash].map(|h| { + h.pack(mp2_common::utils::Endianness::Little) + .into_iter() + .map(F::from_canonical_u32) + .collect_vec() + .try_into() + .unwrap() + }); + + Self::NoProvable(DummyCircuit::new( + is_merge, + block_number, + block_hash, + prev_block_hash, + metadata_digest, + row_digest, + )) + } } #[cfg(test)] diff --git a/mp2-v1/src/final_extraction/dummy_circuit.rs b/mp2-v1/src/final_extraction/dummy_circuit.rs new file mode 100644 index 000000000..e0655a146 --- /dev/null +++ b/mp2-v1/src/final_extraction/dummy_circuit.rs @@ -0,0 +1,206 @@ +//! The dummy circuit used for generating the proof of no provable indexing data + +use super::{ + api::{FinalExtractionBuilderParams, NUM_IO}, + PublicInputs, DUMMY_METADATA_DIGEST_PREFIX, +}; +use alloy::primitives::U256; +use anyhow::Result; +use derive_more::derive::Constructor; +use itertools::Itertools; +use mp2_common::{ + digest::Digest, + group_hashing::CircuitBuilderGroupHashing, + keccak::PACKED_HASH_LEN, + public_inputs::PublicInputCommon, + serialization::{deserialize, serialize}, + types::CBuilder, + u256::{CircuitBuilderU256, UInt256Target, WitnessWriteU256}, + utils::{ToFields, ToTargets}, + D, F, +}; +use plonky2::{ + iop::{ + target::{BoolTarget, Target}, + witness::{PartialWitness, WitnessWrite}, + }, + plonk::proof::ProofWithPublicInputsTarget, +}; +use plonky2_ecgfp5::gadgets::curve::{CircuitBuilderEcGFp5, CurveTarget, PartialWitnessCurve}; +use recursion_framework::circuit_builder::CircuitLogicWires; +use serde::{Deserialize, Serialize}; +use std::array; + +#[derive(Clone, Debug, Constructor)] +pub struct DummyCircuit { + /// Merge flag + is_merge: bool, + /// Block number + block_number: U256, + /// Packed block hash + block_hash: [F; PACKED_HASH_LEN], + /// Packed block hash of the previous block + prev_block_hash: [F; PACKED_HASH_LEN], + /// Metadata digest for the rows extracted + /// This value can be computed outside of the circuit depending on the data source, + /// the circuits don’t care how it is computed given that we are not proving the + /// provenance of the data. + metadata_digest: Digest, + /// Row values digest of all the rows extracted + /// This must corresponds to the value digest that will be re-computed when + /// constructing the rows tree for the current block. + row_digest: Digest, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DummyWires { + #[serde(serialize_with = "serialize", deserialize_with = "deserialize")] + is_merge: BoolTarget, + block_number: UInt256Target, + block_hash: [Target; PACKED_HASH_LEN], + prev_block_hash: [Target; PACKED_HASH_LEN], + #[serde(serialize_with = "serialize", deserialize_with = "deserialize")] + metadata_digest: CurveTarget, + #[serde(serialize_with = "serialize", deserialize_with = "deserialize")] + row_digest: CurveTarget, +} + +impl DummyCircuit { + fn build(b: &mut CBuilder) -> DummyWires { + let is_merge = b.add_virtual_bool_target_unsafe(); + let block_number = b.add_virtual_u256_unsafe(); + let [block_hash, prev_block_hash] = array::from_fn(|_| b.add_virtual_target_arr()); + let [metadata_digest, row_digest] = array::from_fn(|_| b.add_virtual_curve_target()); + + // Add the prefix to the metadata digest to ensure the metadata digest + // will keep track of whether we use this dummy circuit or not. + let prefix = b.constants(&DUMMY_METADATA_DIGEST_PREFIX.to_fields()); + let inputs = prefix + .into_iter() + .chain(metadata_digest.to_targets()) + .collect_vec(); + let encoded_metadata_digest = b.map_to_curve_point(&inputs); + + PublicInputs::new( + &block_hash, + &prev_block_hash, + &row_digest.to_targets(), + &encoded_metadata_digest.to_targets(), + &block_number.to_targets(), + &[is_merge.target], + ) + .register_args(b); + + DummyWires { + is_merge, + block_number, + block_hash, + prev_block_hash, + metadata_digest, + row_digest, + } + } + + fn assign(&self, pw: &mut PartialWitness, wires: &DummyWires) { + pw.set_bool_target(wires.is_merge, self.is_merge); + pw.set_u256_target(&wires.block_number, self.block_number); + [ + (wires.block_hash, self.block_hash), + (wires.prev_block_hash, self.prev_block_hash), + ] + .iter() + .for_each(|(t, v)| { + pw.set_target_arr(t, v); + }); + [ + (wires.metadata_digest, self.metadata_digest), + (wires.row_digest, self.row_digest), + ] + .iter() + .for_each(|(t, v)| { + pw.set_curve_target(*t, v.to_weierstrass()); + }); + } +} + +impl CircuitLogicWires for DummyWires { + type CircuitBuilderParams = FinalExtractionBuilderParams; + type Inputs = DummyCircuit; + + const NUM_PUBLIC_INPUTS: usize = NUM_IO; + + fn circuit_logic( + builder: &mut CBuilder, + _verified_proofs: [&ProofWithPublicInputsTarget; 0], + _builder_parameters: Self::CircuitBuilderParams, + ) -> Self { + DummyCircuit::build(builder) + } + + fn assign_input(&self, inputs: Self::Inputs, pw: &mut PartialWitness) -> Result<()> { + inputs.assign(pw, self); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use mp2_common::{group_hashing::map_to_curve_point, C}; + use mp2_test::circuit::{run_circuit, UserCircuit}; + use plonky2::field::types::Sample; + use rand::{thread_rng, Rng}; + + impl UserCircuit for DummyCircuit { + type Wires = DummyWires; + + fn build(cb: &mut CBuilder) -> Self::Wires { + DummyCircuit::build(cb) + } + + fn prove(&self, pw: &mut PartialWitness, wires: &Self::Wires) { + self.assign(pw, wires); + } + } + + #[test] + fn test_final_extraction_dummy_circuit() { + let rng = &mut thread_rng(); + + let is_merge = rng.gen(); + let block_number = U256::from(rng.gen::()); + let [block_hash, prev_block_hash] = array::from_fn(|_| F::rand_array()); + let [metadata_digest, row_digest] = array::from_fn(|_| Digest::sample(rng)); + + let test_circuit = DummyCircuit::new( + is_merge, + block_number, + block_hash, + prev_block_hash, + metadata_digest, + row_digest, + ); + + let proof = run_circuit::(test_circuit); + let pi = PublicInputs::from_slice(&proof.public_inputs); + + // Check the public inputs. + assert_eq!(pi.is_merge_case(), is_merge); + assert_eq!(U256::from(pi.block_number()), block_number); + assert_eq!(pi.block_hash_raw(), block_hash); + assert_eq!(pi.prev_block_hash_raw(), prev_block_hash); + assert_eq!(pi.value_point(), row_digest.to_weierstrass()); + { + let prefix = DUMMY_METADATA_DIGEST_PREFIX.to_fields(); + let inputs = prefix + .into_iter() + .chain(metadata_digest.to_fields()) + .collect_vec(); + let expected_metadata_digest = map_to_curve_point(&inputs); + assert_eq!( + pi.metadata_point(), + expected_metadata_digest.to_weierstrass() + ); + } + } +} diff --git a/mp2-v1/src/final_extraction/mod.rs b/mp2-v1/src/final_extraction/mod.rs index cb6e1c6a4..577983aa6 100644 --- a/mp2-v1/src/final_extraction/mod.rs +++ b/mp2-v1/src/final_extraction/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod api; mod base_circuit; +mod dummy_circuit; mod lengthed_circuit; mod merge_circuit; mod public_inputs; @@ -9,6 +10,11 @@ pub use api::{CircuitInput, PublicParameters}; pub use public_inputs::PublicInputs; pub(crate) use base_circuit::BaseCircuitProofInputs; +pub(crate) use dummy_circuit::DummyCircuit; pub(crate) use lengthed_circuit::LengthedCircuitInput as LengthedCircuit; pub(crate) use merge_circuit::MergeCircuitInput as MergeCircuit; pub(crate) use simple_circuit::SimpleCircuitInput as SimpleCircuit; + +/// The prefix to ensure the metadata digest will keep track of whether +/// we use this dummy circuit or not +pub(crate) const DUMMY_METADATA_DIGEST_PREFIX: &[u8] = b"DUMMY_EXTRACTION"; diff --git a/mp2-v1/src/final_extraction/public_inputs.rs b/mp2-v1/src/final_extraction/public_inputs.rs index e0bb67c21..2c8d860b7 100644 --- a/mp2-v1/src/final_extraction/public_inputs.rs +++ b/mp2-v1/src/final_extraction/public_inputs.rs @@ -6,7 +6,7 @@ use mp2_common::{ public_inputs::{PublicInputCommon, PublicInputRange}, types::{CBuilder, CURVE_TARGET_LEN}, u256::{self, UInt256Target}, - utils::{FromFields, FromTargets, ToTargets}, + utils::{FromFields, FromTargets, ToTargets, TryIntoBool}, F, }; use plonky2::iop::target::{BoolTarget, Target}; @@ -110,6 +110,9 @@ impl PublicInputs<'_, F> { pub fn block_number(&self) -> u64 { U256::from_fields(self.bn).to() } + pub fn is_merge_case(&self) -> bool { + self.merge[0].try_into_bool().unwrap() + } } impl<'a, T> PublicInputs<'a, T> { diff --git a/mp2-v1/src/values_extraction/mod.rs b/mp2-v1/src/values_extraction/mod.rs index e40681f5c..626dd9ccf 100644 --- a/mp2-v1/src/values_extraction/mod.rs +++ b/mp2-v1/src/values_extraction/mod.rs @@ -1,7 +1,8 @@ use alloy::primitives::Address; use mp2_common::{ + digest::TableDimension, eth::left_pad32, - group_hashing::map_to_curve_point, + group_hashing::{add_curve_point, field_hashed_scalar_mul, map_to_curve_point}, poseidon::H, types::{GFp, MAPPING_KEY_LEN, MAPPING_LEAF_VALUE_LEN}, utils::{Endianness, Packer, ToFields}, @@ -158,3 +159,23 @@ pub fn compute_leaf_mapping_metadata_digest(key_id: u64, value_id: u64, slot: u8 GFp::from_canonical_u8(slot), ]) } + +/// Compute the row value digest of one table. +/// The parameter `digests` are computed by `compute_leaf_single_values_digest` or +/// `compute_leaf_mapping_values_digest`. +pub fn compute_table_row_digest(dimension: TableDimension, digests: &[Digest]) -> Digest { + let acc_digest = add_curve_point(digests); + + dimension.conditional_row_digest(acc_digest) +} + +/// Merge the row value digests of multiple tables. +pub fn merge_table_row_digests( + individual_digests: &[Digest], + multiplier_digests: &[Digest], +) -> Digest { + let [acc_individual_digest, acc_multiplier_digest] = + [individual_digests, multiplier_digests].map(add_curve_point); + + field_hashed_scalar_mul(acc_multiplier_digest.to_fields(), acc_individual_digest) +} diff --git a/mp2-v1/tests/common/cases/contract.rs b/mp2-v1/tests/common/cases/contract.rs index c25b3aa44..5105a2ba0 100644 --- a/mp2-v1/tests/common/cases/contract.rs +++ b/mp2-v1/tests/common/cases/contract.rs @@ -1,11 +1,11 @@ +use super::indexing::{SimpleSingleValue, UpdateSimpleStorage}; +use crate::common::{bindings::simple::Simple, TestContext}; use alloy::{primitives::Address, providers::ProviderBuilder}; use anyhow::Result; use log::info; +use serde::{Deserialize, Serialize}; -use crate::common::{bindings::simple::Simple, TestContext}; - -use super::indexing::{SimpleSingleValue, UpdateSimpleStorage}; - +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Contract { pub address: Address, pub chain_id: u64, diff --git a/mp2-v1/tests/common/cases/indexing.rs b/mp2-v1/tests/common/cases/indexing.rs index 41bdd24db..f98e3c81c 100644 --- a/mp2-v1/tests/common/cases/indexing.rs +++ b/mp2-v1/tests/common/cases/indexing.rs @@ -4,7 +4,6 @@ use anyhow::Result; use log::{debug, info}; use mp2_v1::{ - contract_extraction, indexing::{ block::BlockPrimaryIndex, cell::Cell, @@ -23,7 +22,7 @@ use crate::common::{ identifier_single_var_column, table_source::{ LengthExtractionArgs, MappingIndex, MappingValuesExtractionArgs, MergeSource, - SingleValuesExtractionArgs, DEFAULT_ADDRESS, + NoProvableExtractionArgs, SingleValuesExtractionArgs, DEFAULT_ADDRESS, }, }, proof_storage::{ProofKey, ProofStorage}, @@ -44,7 +43,7 @@ use alloy::{ primitives::{Address, U256}, providers::ProviderBuilder, }; -use mp2_common::{eth::StorageSlot, proof::ProofWithVK, types::HashOutput}; +use mp2_common::{eth::StorageSlot, types::HashOutput}; /// Test slots for single values extraction const SINGLE_SLOTS: [u8; 4] = [0, 1, 2, 3]; @@ -69,6 +68,15 @@ pub(crate) const MAPPING_VALUE_COLUMN: &str = "map_value"; pub(crate) const MAPPING_KEY_COLUMN: &str = "map_key"; impl TableIndexing { + /// Create a wrapping test case from the original one for testing no provable extraction. + pub(crate) fn no_provable_test_case(mut original: Self) -> Self { + // Wrap the original table source to a source of no provable extraction type. + original.source = + TableSource::NoProvable(NoProvableExtractionArgs::new(Box::new(original.source))); + + original + } + pub(crate) async fn merge_table_test_case( ctx: &mut TestContext, ) -> Result<(Self, Vec>)> { @@ -561,88 +569,25 @@ impl TableIndexing { ctx: &mut TestContext, bn: BlockPrimaryIndex, ) -> Result { - let contract_proof_key = ProofKey::ContractExtraction((self.contract.address, bn)); - let contract_proof = match ctx.storage.get_proof_exact(&contract_proof_key) { - Ok(proof) => { - info!( - "Loaded Contract Extraction (C.3) proof for block number {}", - bn - ); - proof - } - Err(_) => { - let contract_proof = ctx - .prove_contract_extraction( - &self.contract.address, - self.contract_extraction.slot.clone(), - bn, - ) - .await; - ctx.storage - .store_proof(contract_proof_key, contract_proof.clone())?; - info!( - "Generated Contract Extraction (C.3) proof for block number {}", - bn - ); - { - let pvk = ProofWithVK::deserialize(&contract_proof)?; - let pis = - contract_extraction::PublicInputs::from_slice(&pvk.proof().public_inputs); - debug!( - " CONTRACT storage root pis.storage_root() {:?}", - hex::encode( - pis.root_hash_field() - .into_iter() - .flat_map(|u| u.to_be_bytes()) - .collect::>() - ) - ); - } - contract_proof - } - }; - - // We look if block proof has already been generated for this block - // since it is the same between proofs - let block_proof_key = ProofKey::BlockExtraction(bn as BlockPrimaryIndex); - let block_proof = match ctx.storage.get_proof_exact(&block_proof_key) { - Ok(proof) => { - info!( - "Loaded Block Extraction (C.4) proof for block number {}", - bn - ); - proof - } - Err(_) => { - let proof = ctx - .prove_block_extraction(bn as BlockPrimaryIndex) - .await - .unwrap(); - ctx.storage.store_proof(block_proof_key, proof.clone())?; - info!( - "Generated Block Extraction (C.4) proof for block number {}", - bn - ); - proof - } - }; - let table_id = &self.table.public_name.clone(); // we construct the proof key for both mappings and single variable in the same way since // it is derived from the table id which should be different for any tables we create. let value_key = ProofKey::ValueExtraction((table_id.clone(), bn as BlockPrimaryIndex)); // final extraction for single variables combining the different proofs generated before let final_key = ProofKey::FinalExtraction((table_id.clone(), bn as BlockPrimaryIndex)); + let (extraction, metadata_hash) = self .source - .generate_extraction_proof_inputs(ctx, &self.contract, value_key) + .generate_extraction_proof_inputs( + ctx, + &self.contract, + &self.contract_extraction, + value_key, + ) .await?; // no need to generate it if it's already present if ctx.storage.get_proof_exact(&final_key).is_err() { - let proof = ctx - .prove_final_extraction(contract_proof, block_proof, extraction) - .await - .unwrap(); + let proof = ctx.prove_final_extraction(extraction).await.unwrap(); ctx.storage .store_proof(final_key, proof.clone()) .expect("unable to save in storage?"); @@ -941,10 +886,12 @@ impl TableIndexing { TableInfo { public_name: self.table.public_name.clone(), value_column: self.value_column.clone(), - chain_id: self.contract.chain_id, columns: self.table.columns.clone(), - contract_address: self.contract.address, source: self.source.clone(), + contract: Contract { + chain_id: self.contract.chain_id, + address: self.contract.address, + }, } } } diff --git a/mp2-v1/tests/common/cases/table_source.rs b/mp2-v1/tests/common/cases/table_source.rs index 01f0497d6..59d622e7b 100644 --- a/mp2-v1/tests/common/cases/table_source.rs +++ b/mp2-v1/tests/common/cases/table_source.rs @@ -10,24 +10,31 @@ use alloy::{ providers::Provider, }; use anyhow::{bail, Result}; +use derive_more::derive::Constructor; use futures::{future::BoxFuture, FutureExt}; use log::{debug, info}; use mp2_common::{ - digest::TableDimension, + digest::{Digest, TableDimension}, eth::{ProofQuery, StorageSlot}, + group_hashing::map_to_curve_point, proof::ProofWithVK, types::HashOutput, + utils::ToFields, }; use mp2_v1::{ - api::{merge_metadata_hash, metadata_hash, SlotInputs}, + api::{ + merge_metadata_hash, metadata_hash, no_provable_metadata_hash, MetadataHash, SlotInputs, + }, + contract_extraction, indexing::{ block::BlockPrimaryIndex, cell::Cell, row::{RowTreeKey, ToNonce}, }, values_extraction::{ - identifier_for_mapping_key_column, identifier_for_mapping_value_column, - identifier_single_var_column, + compute_leaf_mapping_values_digest, compute_leaf_single_values_digest, + compute_table_row_digest, identifier_for_mapping_key_column, + identifier_for_mapping_value_column, identifier_single_var_column, merge_table_row_digests, }, }; use rand::{Rng, SeedableRng}; @@ -35,7 +42,10 @@ use serde::{Deserialize, Serialize}; use crate::common::{ cases::indexing::{MappingUpdate, SimpleSingleValue, TableRowValues}, - final_extraction::{ExtractionProofInput, ExtractionTableProof, MergeExtractionProof}, + final_extraction::{ + ExtractionProofInput, ExtractionTableProof, MergeExtractionProof, + NoProvableExtractionProof, NormalExtractionProof, + }, proof_storage::{ProofKey, ProofStorage}, rowtree::SecondaryIndexCell, table::CellsUpdate, @@ -190,14 +200,58 @@ pub(crate) enum TableSource { /// We can test with and without the length Mapping((MappingValuesExtractionArgs, Option)), Merge(MergeSource), + /// Test arguments for indexing data with no provable extraction + NoProvable(NoProvableExtractionArgs), } impl TableSource { + pub fn is_merge_case(&self) -> bool { + match self { + TableSource::SingleValues(_) | TableSource::Mapping(_) => false, + TableSource::Merge(_) => true, + TableSource::NoProvable(NoProvableExtractionArgs { inner }) => inner.is_merge_case(), + } + } + + pub async fn compute_row_digest(&self, ctx: &TestContext, contract: &Contract) -> Digest { + match self { + TableSource::SingleValues(args) => args.compute_row_digest(ctx, contract).await, + TableSource::Mapping((args, _)) => args.compute_row_digest(ctx, contract).await, + TableSource::Merge(args) => args.compute_row_digest(ctx, contract).await, + TableSource::NoProvable(_) => { + panic!("Should not call row digest computation for no provable table") + } + } + } + + pub fn metadata_hash(&self, contract: &Contract) -> MetadataHash { + match self { + TableSource::Mapping((mapping, _)) => { + let slot = SlotInputs::Mapping(mapping.slot); + metadata_hash(slot, &contract.address, contract.chain_id, vec![]) + } + // mapping with length not tested right now + TableSource::SingleValues(args) => { + let slot = SlotInputs::Simple(args.slots.clone()); + metadata_hash(slot, &contract.address, contract.chain_id, vec![]) + } + TableSource::Merge(merge) => { + let single = SlotInputs::Simple(merge.single.slots.clone()); + let mapping = SlotInputs::Mapping(merge.mapping.slot); + merge_metadata_hash(contract.address, contract.chain_id, vec![], single, mapping) + } + TableSource::NoProvable(NoProvableExtractionArgs { inner }) => { + inner.metadata_hash(contract) + } + } + } + pub fn slot_input(&self) -> SlotInputs { match self { TableSource::SingleValues(single) => SlotInputs::Simple(single.slots.clone()), TableSource::Mapping((m, _)) => SlotInputs::Mapping(m.slot), TableSource::Merge(_) => panic!("can't call slot inputs on merge table"), + TableSource::NoProvable(NoProvableExtractionArgs { inner }) => inner.slot_input(), } } @@ -212,6 +266,9 @@ impl TableSource { TableSource::SingleValues(ref mut s) => s.init_contract_data(ctx, contract).await, TableSource::Mapping((ref mut m, _)) => m.init_contract_data(ctx, contract).await, TableSource::Merge(ref mut merge) => merge.init_contract_data(ctx, contract).await, + TableSource::NoProvable(NoProvableExtractionArgs { inner }) => { + inner.init_contract_data(ctx, contract).await + } } } .boxed() @@ -221,22 +278,42 @@ impl TableSource { &self, ctx: &mut TestContext, contract: &Contract, + contract_extraction_args: &ContractExtractionArgs, value_key: ProofKey, ) -> Result<(ExtractionProofInput, HashOutput)> { match self { // first lets do without length TableSource::Mapping((ref mapping, _)) => { mapping - .generate_extraction_proof_inputs(ctx, contract, value_key) + .generate_extraction_proof_inputs( + ctx, + contract, + contract_extraction_args, + value_key, + ) .await } TableSource::SingleValues(ref args) => { - args.generate_extraction_proof_inputs(ctx, contract, value_key) - .await + args.generate_extraction_proof_inputs( + ctx, + contract, + contract_extraction_args, + value_key, + ) + .await } TableSource::Merge(ref merge) => { merge - .generate_extraction_proof_inputs(ctx, contract, value_key) + .generate_extraction_proof_inputs( + ctx, + contract, + contract_extraction_args, + value_key, + ) + .await + } + TableSource::NoProvable(args) => { + args.generate_extraction_proof_inputs(ctx, contract, value_key) .await } } @@ -260,6 +337,9 @@ impl TableSource { TableSource::Merge(ref mut merge) => { merge.random_contract_update(ctx, contract, c).await } + TableSource::NoProvable(NoProvableExtractionArgs { inner }) => { + inner.random_contract_update(ctx, contract, c).await + } } } .boxed() @@ -276,6 +356,30 @@ pub(crate) struct SingleValuesExtractionArgs { } impl SingleValuesExtractionArgs { + async fn compute_row_digest(&self, ctx: &TestContext, contract: &Contract) -> Digest { + let mut digests = Vec::with_capacity(self.slots.len()); + for slot in self.slots.iter() { + // Compute the slot identifier. + let id = + identifier_single_var_column(*slot, &contract.address, contract.chain_id, vec![]); + + // Query the slot value. + let query = ProofQuery::new_simple_slot(contract.address, *slot as usize); + let value = ctx + .query_mpt_proof(&query, BlockNumberOrTag::Number(ctx.block_number().await)) + .await + .storage_proof[0] + .value; + + // Compute the digest. + let digest = compute_leaf_single_values_digest(id, &value.to_be_bytes_vec()); + + digests.push(digest); + } + + compute_table_row_digest(TableDimension::Single, &digests) + } + async fn init_contract_data( &mut self, ctx: &mut TestContext, @@ -398,12 +502,16 @@ impl SingleValuesExtractionArgs { &self, ctx: &mut TestContext, contract: &Contract, + contract_extraction_args: &ContractExtractionArgs, proof_key: ProofKey, ) -> Result<(ExtractionProofInput, HashOutput)> { let chain_id = ctx.rpc.get_chain_id().await?; let ProofKey::ValueExtraction((_id, bn)) = proof_key.clone() else { bail!("invalid proof key"); }; + let contract_proof = + generate_contract_extraction_proof(ctx, bn, contract, contract_extraction_args).await; + let block_proof = generate_block_extraction_proof(ctx, bn).await; let single_value_proof = match ctx.storage.get_proof_exact(&proof_key) { Ok(p) => p, Err(_) => { @@ -441,10 +549,14 @@ impl SingleValuesExtractionArgs { let slot_input = SlotInputs::Simple(self.slots.clone()); let metadata_hash = metadata_hash(slot_input, &contract.address, chain_id, vec![]); // we're just proving a single set of a value - let input = ExtractionProofInput::Single(ExtractionTableProof { - dimension: TableDimension::Single, - value_proof: single_value_proof, - length_proof: None, + let input = ExtractionProofInput::Normal(NormalExtractionProof { + inner: ExtractionTableProof { + dimension: TableDimension::Single, + value_proof: single_value_proof, + length_proof: None, + }, + block_proof, + contract_proof, }); Ok((input, metadata_hash)) } @@ -465,6 +577,48 @@ pub(crate) struct MappingValuesExtractionArgs { } impl MappingValuesExtractionArgs { + async fn compute_row_digest(&self, ctx: &TestContext, contract: &Contract) -> Digest { + let mut digests = Vec::with_capacity(self.mapping_keys.len()); + for mapping_key in &self.mapping_keys { + // Compute the mapping key and value identifiers. + let key_id = identifier_for_mapping_key_column( + self.slot, + &contract.address, + contract.chain_id, + vec![], + ); + let value_id = identifier_for_mapping_value_column( + self.slot, + &contract.address, + contract.chain_id, + vec![], + ); + + // Query the mapping value. + let query = ProofQuery::new_mapping_slot( + contract.address, + self.slot as usize, + mapping_key.clone(), + ); + let response = ctx + .query_mpt_proof(&query, BlockNumberOrTag::Number(ctx.block_number().await)) + .await; + let mapping_value = response.storage_proof[0].value; + + // Compute the digest. + let digest = compute_leaf_mapping_values_digest( + key_id, + value_id, + mapping_key, + &mapping_value.to_be_bytes_vec(), + ); + + digests.push(digest); + } + + compute_table_row_digest(TableDimension::Compound, &digests) + } + pub async fn init_contract_data( &mut self, ctx: &mut TestContext, @@ -642,9 +796,16 @@ impl MappingValuesExtractionArgs { &self, ctx: &mut TestContext, contract: &Contract, + contract_extraction_args: &ContractExtractionArgs, proof_key: ProofKey, ) -> Result<(ExtractionProofInput, HashOutput)> { let chain_id = ctx.rpc.get_chain_id().await?; + let ProofKey::ValueExtraction((_id, bn)) = proof_key.clone() else { + bail!("invalid proof key"); + }; + let contract_proof = + generate_contract_extraction_proof(ctx, bn, contract, contract_extraction_args).await; + let block_proof = generate_block_extraction_proof(ctx, bn).await; let mapping_root_proof = match ctx.storage.get_proof_exact(&proof_key) { Ok(p) => p, Err(_) => { @@ -683,10 +844,14 @@ impl MappingValuesExtractionArgs { let slot_input = SlotInputs::Mapping(self.slot); let metadata_hash = metadata_hash(slot_input, &contract.address, chain_id, vec![]); // it's a compoound value type of proof since we're not using the length - let input = ExtractionProofInput::Single(ExtractionTableProof { - dimension: TableDimension::Compound, - value_proof: mapping_root_proof, - length_proof: None, + let input = ExtractionProofInput::Normal(NormalExtractionProof { + inner: ExtractionTableProof { + dimension: TableDimension::Compound, + value_proof: mapping_root_proof, + length_proof: None, + }, + block_proof, + contract_proof, }); Ok((input, metadata_hash)) } @@ -801,6 +966,13 @@ impl MergeSource { Self { single, mapping } } + async fn compute_row_digest(&self, ctx: &TestContext, contract: &Contract) -> Digest { + let single_digest = self.single.compute_row_digest(ctx, contract).await; + let mapping_digest = self.mapping.compute_row_digest(ctx, contract).await; + + merge_table_row_digests(&[mapping_digest], &[single_digest]) + } + pub async fn init_contract_data( &mut self, ctx: &mut TestContext, @@ -944,6 +1116,7 @@ impl MergeSource { &'a self, ctx: &'a mut TestContext, contract: &'a Contract, + contract_extraction_args: &'a ContractExtractionArgs, proof_key: ProofKey, ) -> BoxFuture> { async move { @@ -958,10 +1131,11 @@ impl MergeSource { .generate_extraction_proof_inputs( ctx, contract, + contract_extraction_args, ProofKey::ValueExtraction((id_a, bn)), ) .await?; - let ExtractionProofInput::Single(extract_a) = extract_single else { + let ExtractionProofInput::Normal(extract_a) = extract_single else { bail!("can't merge non single tables") }; let (extract_mappping, _) = self @@ -969,10 +1143,11 @@ impl MergeSource { .generate_extraction_proof_inputs( ctx, contract, + contract_extraction_args, ProofKey::ValueExtraction((id_b, bn)), ) .await?; - let ExtractionProofInput::Single(extract_b) = extract_mappping else { + let ExtractionProofInput::Normal(extract_b) = extract_mappping else { bail!("can't merge non single tables") }; @@ -987,8 +1162,10 @@ impl MergeSource { assert!(extract_a != extract_b); Ok(( ExtractionProofInput::Merge(MergeExtractionProof { - single: extract_a, - mapping: extract_b, + single: extract_a.inner, + mapping: extract_b.inner, + block_proof: extract_b.block_proof, + contract_proof: extract_b.contract_proof, }), md, )) @@ -996,6 +1173,7 @@ impl MergeSource { .boxed() } } + /// Length extraction arguments (C.2) #[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)] pub(crate) struct LengthExtractionArgs { @@ -1040,3 +1218,138 @@ pub fn next_value() -> U256 { let bv: U256 = *BASE_VALUE; bv + U256::from(shift) } + +/// Generate the contract extraction proof. +async fn generate_contract_extraction_proof( + ctx: &mut TestContext, + bn: BlockPrimaryIndex, + contract: &Contract, + contract_extraction_args: &ContractExtractionArgs, +) -> Vec { + let contract_proof_key = ProofKey::ContractExtraction((contract.address, bn)); + match ctx.storage.get_proof_exact(&contract_proof_key) { + Ok(proof) => { + info!( + "Loaded Contract Extraction (C.3) proof for block number {}", + bn + ); + proof + } + Err(_) => { + let contract_proof = ctx + .prove_contract_extraction( + &contract.address, + contract_extraction_args.slot.clone(), + bn, + ) + .await; + ctx.storage + .store_proof(contract_proof_key, contract_proof.clone()) + .unwrap(); + info!( + "Generated Contract Extraction (C.3) proof for block number {}", + bn + ); + { + let pvk = ProofWithVK::deserialize(&contract_proof).unwrap(); + let pis = contract_extraction::PublicInputs::from_slice(&pvk.proof().public_inputs); + debug!( + " CONTRACT storage root pis.storage_root() {:?}", + hex::encode( + pis.root_hash_field() + .into_iter() + .flat_map(|u| u.to_be_bytes()) + .collect::>() + ) + ); + } + contract_proof + } + } +} + +/// Generate the block extraction proof. +async fn generate_block_extraction_proof(ctx: &mut TestContext, bn: BlockPrimaryIndex) -> Vec { + // We look if block proof has already been generated for this block + // since it is the same between proofs + let block_proof_key = ProofKey::BlockExtraction(bn as BlockPrimaryIndex); + match ctx.storage.get_proof_exact(&block_proof_key) { + Ok(proof) => { + info!( + "Loaded Block Extraction (C.4) proof for block number {}", + bn + ); + proof + } + Err(_) => { + let proof = ctx + .prove_block_extraction(bn as BlockPrimaryIndex) + .await + .unwrap(); + ctx.storage + .store_proof(block_proof_key, proof.clone()) + .unwrap(); + info!( + "Generated Block Extraction (C.4) proof for block number {}", + bn + ); + proof + } + } +} + +/// No provable extraction arguments +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Constructor)] +pub(crate) struct NoProvableExtractionArgs { + inner: Box, +} + +impl NoProvableExtractionArgs { + /// Generate the extraction inputs without provable. + pub async fn generate_extraction_proof_inputs( + &self, + ctx: &mut TestContext, + contract: &Contract, + proof_key: ProofKey, + ) -> Result<(ExtractionProofInput, HashOutput)> { + let ProofKey::ValueExtraction((_, block_number)) = proof_key else { + bail!("invalid proof key"); + }; + let block = ctx + .query_block_at(BlockNumberOrTag::Number(block_number as u64)) + .await; + + let block_number = U256::from(block_number); + let [block_hash, prev_block_hash] = + [block.header.hash, block.header.parent_hash].map(|b| HashOutput(b.0)); + + let is_merge = self.inner.is_merge_case(); + + // TRICKY: The metadata digest could be set to any digest which is used to identify this + // table of no provable extraction. We convert the metadata hash of the original extraction + // for convenience, actually it could be set to any digest by the caller. In the dummy + // circuit of final extraction, we add a prefix `DUMMY_EXTRACTION` to generate a new digest + // and don't prove this metadata digest. + let inputs = self.inner.metadata_hash(contract).to_fields(); + let metadata_digest = map_to_curve_point(&inputs); + + // The expected metadata hash is computed by the metadata digest and will check in the IVC + // circuits. + let metadata_hash = no_provable_metadata_hash(&metadata_digest); + + // Compute the row digest. + let row_digest = self.inner.compute_row_digest(ctx, contract).await; + + Ok(( + ExtractionProofInput::NoProvable(NoProvableExtractionProof { + is_merge, + block_hash, + prev_block_hash, + block_number, + metadata_digest, + row_digest, + }), + metadata_hash, + )) + } +} diff --git a/mp2-v1/tests/common/final_extraction.rs b/mp2-v1/tests/common/final_extraction.rs index 0ae8db58a..e480f886d 100644 --- a/mp2-v1/tests/common/final_extraction.rs +++ b/mp2-v1/tests/common/final_extraction.rs @@ -1,13 +1,18 @@ +use super::TestContext; +use alloy::primitives::U256; +use anyhow::Result; use log::debug; -use mp2_common::{digest::TableDimension, proof::ProofWithVK, types::HashOutput, utils::ToFields}; +use mp2_common::{ + digest::{Digest, TableDimension}, + proof::ProofWithVK, + types::HashOutput, + utils::ToFields, +}; use mp2_v1::{ api, final_extraction::{CircuitInput, PublicInputs}, }; -use super::TestContext; -use anyhow::Result; - #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExtractionTableProof { pub value_proof: Vec, @@ -15,48 +20,80 @@ pub struct ExtractionTableProof { pub length_proof: Option>, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct NormalExtractionProof { + pub inner: ExtractionTableProof, + pub block_proof: Vec, + pub contract_proof: Vec, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct MergeExtractionProof { // NOTE: Right now hardcoding for single and mapping but that can be generalized later easily. pub single: ExtractionTableProof, pub mapping: ExtractionTableProof, + pub block_proof: Vec, + pub contract_proof: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct NoProvableExtractionProof { + pub is_merge: bool, + pub block_hash: HashOutput, + pub prev_block_hash: HashOutput, + pub block_number: U256, + pub metadata_digest: Digest, + pub row_digest: Digest, } +#[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExtractionProofInput { - Single(ExtractionTableProof), + Normal(NormalExtractionProof), Merge(MergeExtractionProof), + NoProvable(NoProvableExtractionProof), } impl TestContext { pub(crate) async fn prove_final_extraction( &self, - contract_proof: Vec, - block_proof: Vec, value_proofs: ExtractionProofInput, ) -> Result> { + let block = self.query_current_block().await; + let circuit_input = match value_proofs { - ExtractionProofInput::Single(inputs) if inputs.length_proof.is_some() => { - CircuitInput::new_lengthed_input( - block_proof, - contract_proof, - inputs.value_proof, - inputs.length_proof.unwrap(), - ) + ExtractionProofInput::Normal(inputs) => { + if inputs.inner.length_proof.is_some() { + CircuitInput::new_lengthed_input( + inputs.block_proof, + inputs.contract_proof, + inputs.inner.value_proof, + inputs.inner.length_proof.unwrap(), + ) + } else { + CircuitInput::new_simple_input( + inputs.block_proof, + inputs.contract_proof, + inputs.inner.value_proof, + inputs.inner.dimension, + ) + } } - ExtractionProofInput::Single(inputs) => CircuitInput::new_simple_input( - block_proof, - contract_proof, - inputs.value_proof, - inputs.dimension, - ), // NOTE hardcoded for single and mapping right now ExtractionProofInput::Merge(inputs) => CircuitInput::new_merge_single_and_mapping( - block_proof, - contract_proof, + inputs.block_proof, + inputs.contract_proof, inputs.single.value_proof, inputs.mapping.value_proof, ), + ExtractionProofInput::NoProvable(inputs) => Ok(CircuitInput::new_no_provable_input( + inputs.is_merge, + inputs.block_number, + inputs.block_hash, + inputs.prev_block_hash, + inputs.metadata_digest, + inputs.row_digest, + )), }?; let params = self.params(); let proof = self @@ -67,7 +104,6 @@ impl TestContext { .expect("unable to generate final extraction proof"); let pproof = ProofWithVK::deserialize(&proof)?; - let block = self.query_current_block().await; let block_hash = HashOutput::from(block.header.hash.0); let prev_block_hash = HashOutput::from(block.header.parent_hash.0); diff --git a/mp2-v1/tests/common/index_tree.rs b/mp2-v1/tests/common/index_tree.rs index 481d6aeb7..dd4fd65cd 100644 --- a/mp2-v1/tests/common/index_tree.rs +++ b/mp2-v1/tests/common/index_tree.rs @@ -83,6 +83,11 @@ impl TestContext { let ext_pi = mp2_v1::final_extraction::PublicInputs::from_slice( &ext_proof.proof().public_inputs, ); + assert_eq!( + row_pi.is_merge_flag(), + ext_pi.is_merge_case(), + "Merge flags of values extraction and rows in DB don't match", + ); assert_eq!( row_pi.rows_digest_field(), ext_pi.value_point(), diff --git a/mp2-v1/tests/common/ivc.rs b/mp2-v1/tests/common/ivc.rs index 09b6d4ceb..8bffa15eb 100644 --- a/mp2-v1/tests/common/ivc.rs +++ b/mp2-v1/tests/common/ivc.rs @@ -8,7 +8,7 @@ use anyhow::Result; use mp2_common::{proof::ProofWithVK, types::HashOutput, F}; use mp2_v1::{api, indexing::block::BlockPrimaryIndex}; use plonky2::{hash::hash_types::HashOut, plonk::config::GenericHashOut}; -use verifiable_db::ivc::PublicInputs; +use verifiable_db::{block_tree, ivc::PublicInputs}; impl TestContext { pub async fn prove_ivc( @@ -34,6 +34,24 @@ impl TestContext { let previous_ivc_key = ProofKey::IVC(bn - 1); let input = match self.storage.get_proof_exact(&previous_ivc_key) { Ok(previous_proof) => { + // Check the input previous proof and block proof. + { + let [prev_proof, block_proof] = [&previous_proof, &root_proof] + .map(|proof| ProofWithVK::deserialize(proof).unwrap()); + let prev_pi = PublicInputs::from_slice(&prev_proof.proof.public_inputs); + let block_pi = + block_tree::PublicInputs::from_slice(&block_proof.proof.public_inputs); + assert_eq!( + prev_pi.block_hash_fields(), + block_pi.prev_block_hash_fields(), + ); + assert_eq!( + prev_pi.merkle_root_hash_fields(), + block_pi.old_merkle_hash_field(), + ); + assert_eq!(prev_pi.z0_u256(), block_pi.min_block_number().unwrap()); + assert_eq!(prev_pi.metadata_hash(), block_pi.metadata_hash()); + } verifiable_db::ivc::CircuitInput::new_subsequent_input(root_proof, previous_proof) } Err(_) => verifiable_db::ivc::CircuitInput::new_first_input(root_proof), diff --git a/mp2-v1/tests/common/mod.rs b/mp2-v1/tests/common/mod.rs index c604a1424..1f381c224 100644 --- a/mp2-v1/tests/common/mod.rs +++ b/mp2-v1/tests/common/mod.rs @@ -1,8 +1,7 @@ //! Utility structs and functions used for integration tests -use alloy::primitives::Address; use anyhow::Result; -use cases::table_source::TableSource; -use mp2_v1::api::{merge_metadata_hash, metadata_hash, MetadataHash, SlotInputs}; +use cases::{contract::Contract, table_source::TableSource}; +use mp2_v1::api::MetadataHash; use serde::{Deserialize, Serialize}; use table::TableColumns; pub mod benchmarker; @@ -70,34 +69,12 @@ pub struct TableInfo { // column to do queries over for numerical values, NOT secondary index pub value_column: String, pub public_name: String, - pub contract_address: Address, - pub chain_id: u64, + pub contract: Contract, pub source: TableSource, } impl TableInfo { pub fn metadata_hash(&self) -> MetadataHash { - match &self.source { - TableSource::Mapping((mapping, _)) => { - let slot = SlotInputs::Mapping(mapping.slot); - metadata_hash(slot, &self.contract_address, self.chain_id, vec![]) - } - // mapping with length not tested right now - TableSource::SingleValues(args) => { - let slot = SlotInputs::Simple(args.slots.clone()); - metadata_hash(slot, &self.contract_address, self.chain_id, vec![]) - } - TableSource::Merge(merge) => { - let single = SlotInputs::Simple(merge.single.slots.clone()); - let mapping = SlotInputs::Mapping(merge.mapping.slot); - merge_metadata_hash( - self.contract_address, - self.chain_id, - vec![], - single, - mapping, - ) - } - } + self.source.metadata_hash(&self.contract) } } diff --git a/mp2-v1/tests/integrated_tests.rs b/mp2-v1/tests/integrated_tests.rs index 8ce01bcb4..22eaf1140 100644 --- a/mp2-v1/tests/integrated_tests.rs +++ b/mp2-v1/tests/integrated_tests.rs @@ -29,7 +29,7 @@ use common::{ context::{self, ParamsType, TestContextConfig}, proof_storage::{ProofKV, DEFAULT_PROOF_STORE_FOLDER}, table::Table, - TableInfo, + TableInfo, TestContext, }; use envconfig::Envconfig; use log::info; @@ -113,6 +113,8 @@ async fn integrated_indexing() -> Result<()> { ]; merged.run(&mut ctx, genesis, changes).await?; + run_no_provable_test_cases(&mut ctx).await?; + // save columns information and table information in JSON so querying test can pick up write_table_info(MAPPING_TABLE_INFO_FILE, mapping.table_info())?; write_table_info(MERGE_TABLE_INFO_FILE, merged.table_info())?; @@ -247,3 +249,51 @@ async fn test_andrus_query() -> Result<()> { info!("all good"); Ok(()) } + +async fn run_no_provable_test_cases(ctx: &mut TestContext) -> Result<()> { + // Run a test case for no provable extraction of single value slots. + let (mut no_provable, genesis) = { + let (single, genesis) = TableIndexing::single_value_test_case(ctx).await?; + let no_provable = TableIndexing::no_provable_test_case(single); + + (no_provable, genesis) + }; + let changes = vec![ + ChangeType::Update(UpdateType::Rest), + ChangeType::Silent, + ChangeType::Update(UpdateType::SecondaryIndex), + ]; + no_provable.run(ctx, genesis, changes).await?; + + // Run a test case for no provable extraction of mapping slot. + let (mut no_provable, genesis) = { + let (mapping, genesis) = TableIndexing::mapping_test_case(ctx).await?; + let no_provable = TableIndexing::no_provable_test_case(mapping); + + (no_provable, genesis) + }; + let changes = vec![ + ChangeType::Insertion, + ChangeType::Update(UpdateType::Rest), + ChangeType::Silent, + ChangeType::Update(UpdateType::SecondaryIndex), + ChangeType::Deletion, + ]; + no_provable.run(ctx, genesis, changes).await?; + + // Run a test case for no provable extraction of merge table. + let (mut no_provable, genesis) = { + let (merge, genesis) = TableIndexing::merge_table_test_case(ctx).await?; + let no_provable = TableIndexing::no_provable_test_case(merge); + + (no_provable, genesis) + }; + let changes = vec![ + ChangeType::Insertion, + ChangeType::Update(UpdateType::Rest), + ChangeType::Update(UpdateType::Rest), + ChangeType::Silent, + ChangeType::Deletion, + ]; + no_provable.run(ctx, genesis, changes).await +} diff --git a/verifiable-db/src/ivc/circuit.rs b/verifiable-db/src/ivc/circuit.rs index 133759216..5c9234433 100644 --- a/verifiable-db/src/ivc/circuit.rs +++ b/verifiable-db/src/ivc/circuit.rs @@ -18,7 +18,6 @@ use plonky2::{ }, plonk::circuit_builder::CircuitBuilder, }; -use plonky2_crypto::u32::arithmetic_u32::CircuitBuilderU32; use plonky2_ecgfp5::gadgets::curve::CircuitBuilderEcGFp5; use recursion_framework::{ circuit_builder::CircuitLogicWires, @@ -44,13 +43,6 @@ impl IVCCircuit { // assert prev_proof.H_i == new_block_proof.H_old c.connect_targets(prev_pi.merkle_hash(), block_pi.old_merkle_hash()); - // assert prev_proof.z_i + 1 == new_block_proof.block_number - let big_one = c.one_u256(); - let (expected_zi, carry) = c.add_u256(&prev_pi.zi(), &big_one); - // make sure there is no carry - let small_zero = c.zero_u32(); - c.connect_u32(small_zero, carry); - c.enforce_equal_u256(&expected_zi, &block_pi.index_value()); // assert prev_proof.z_0 == new_block_proof.min c.enforce_equal_u256(&prev_pi.z0(), &block_pi.min_value()); //assert prev_proof.M == new_block_proof.M