diff --git a/verifiable-db/src/results_tree/extraction/mod.rs b/verifiable-db/src/results_tree/extraction/mod.rs index f351f191b..0ea2eb331 100644 --- a/verifiable-db/src/results_tree/extraction/mod.rs +++ b/verifiable-db/src/results_tree/extraction/mod.rs @@ -1 +1,4 @@ pub(crate) mod public_inputs; +pub(crate) mod record; + +use public_inputs::PublicInputs; diff --git a/verifiable-db/src/results_tree/extraction/record.rs b/verifiable-db/src/results_tree/extraction/record.rs new file mode 100644 index 000000000..88eedcd57 --- /dev/null +++ b/verifiable-db/src/results_tree/extraction/record.rs @@ -0,0 +1,180 @@ +use crate::results_tree::extraction::PublicInputs; +use alloy::primitives::U256; +use mp2_common::{ + group_hashing::CircuitBuilderGroupHashing, + poseidon::{empty_poseidon_hash, H}, + public_inputs::PublicInputCommon, + serialization::{deserialize, deserialize_long_array, serialize, serialize_long_array}, + types::CBuilder, + u256::{CircuitBuilderU256, UInt256Target, WitnessWriteU256}, + utils::{SelectHashBuilder, ToTargets}, + D, F, +}; +use plonky2::{ + hash::hash_types::{HashOut, HashOutTarget}, + iop::{ + target::{BoolTarget, Target}, + witness::{PartialWitness, WitnessWrite}, + }, + plonk::proof::ProofWithPublicInputsTarget, +}; +use recursion_framework::circuit_builder::CircuitLogicWires; +use serde::{Deserialize, Serialize}; +use std::iter; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RecordWires { + #[serde( + serialize_with = "serialize_long_array", + deserialize_with = "deserialize_long_array" + )] + indexed_items: [UInt256Target; MAX_NUM_RESULTS], + #[serde( + serialize_with = "serialize_long_array", + deserialize_with = "deserialize_long_array" + )] + index_ids: [Target; MAX_NUM_RESULTS], + #[serde(serialize_with = "serialize", deserialize_with = "deserialize")] + tree_hash: HashOutTarget, + counter: UInt256Target, + #[serde(serialize_with = "serialize", deserialize_with = "deserialize")] + is_stored_in_leaf: BoolTarget, + offset_range_min: UInt256Target, + offset_range_max: UInt256Target, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RecordCircuit { + // TODO(Insun35): add comments + #[serde( + serialize_with = "serialize_long_array", + deserialize_with = "deserialize_long_array" + )] + pub(crate) indexed_items: [U256; MAX_NUM_RESULTS], + #[serde( + serialize_with = "serialize_long_array", + deserialize_with = "deserialize_long_array" + )] + pub(crate) index_ids: [F; MAX_NUM_RESULTS], + pub(crate) tree_hash: HashOut, + pub(crate) counter: U256, + pub(crate) is_stored_in_leaf: bool, + /// Minimum offset range bound + pub(crate) offset_range_min: U256, + /// Maximum offset range bound + pub(crate) offset_range_max: U256, +} + +impl RecordCircuit { + pub fn build(b: &mut CBuilder) -> RecordWires { + let ffalse = b._false(); + let empty_hash = b.constant_hash(*empty_poseidon_hash()); + + let indexed_items: [UInt256Target; MAX_NUM_RESULTS] = b.add_virtual_u256_arr_unsafe(); + let index_ids: [Target; MAX_NUM_RESULTS] = b.add_virtual_target_arr(); + let mut tree_hash = b.add_virtual_hash(); + let counter = b.add_virtual_u256_unsafe(); + let is_stored_in_leaf = b.add_virtual_bool_target_safe(); + let offset_range_min = b.add_virtual_u256_unsafe(); + let offset_range_max = b.add_virtual_u256_unsafe(); + + // H(H("")||H("")||indexed_items[1]||indexed_items[1]||index_ids[1]||indexed_items[1]||tree_hash) + let tree_hash_inputs = empty_hash + .elements + .iter() + .cloned() + .chain(empty_hash.elements) + .chain(indexed_items[1].to_targets()) + .chain(indexed_items[1].to_targets()) + .chain(iter::once(index_ids[1])) + .chain(indexed_items[1].to_targets()) + .chain(tree_hash.elements) + .collect(); + let new_tree_hash = b.hash_n_to_hash_no_pad::(tree_hash_inputs); + tree_hash = b.select_hash(is_stored_in_leaf, &new_tree_hash, &tree_hash); + + // D(index_ids[0]||indexed_items[0]||index_ids[1]||indexed_items[1]||tree_hash) + let accumulator_inputs: Vec<_> = iter::once(index_ids[0]) + .chain(indexed_items[0].to_targets()) + .chain(iter::once(index_ids[1])) + .chain(indexed_items[1].to_targets()) + .chain(tree_hash.to_targets()) + .collect(); + let accumulator = b.map_to_curve_point(&accumulator_inputs); + + // Ensure the counter associated to the current record is in the range + // specified by the query + // offset_range_min <= counter <= offset_range_max + // -> NOT((counter < offset_range_min) OR (counter > offset_range_max) + let is_less_than = b.is_less_than_u256(&counter, &offset_range_min); + let is_greater_than = b.is_greater_than_u256(&counter, &offset_range_max); + let is_out_of_range = b.or(is_less_than, is_greater_than); + b.connect(is_out_of_range.target, ffalse.target); + + // Register the public inputs. + PublicInputs::<_, MAX_NUM_RESULTS>::new( + &tree_hash.to_targets(), + &indexed_items[1].to_targets(), + &indexed_items[1].to_targets(), + &indexed_items[0].to_targets(), + &index_ids, + &counter.to_targets(), + &counter.to_targets(), + &offset_range_min.to_targets(), + &offset_range_max.to_targets(), + &accumulator.to_targets(), + ) + .register(b); + + RecordWires { + indexed_items, + index_ids, + tree_hash, + counter, + is_stored_in_leaf, + offset_range_min, + offset_range_max, + } + } + + fn assign(&self, pw: &mut PartialWitness, wires: &RecordWires) { + wires + .indexed_items + .iter() + .zip(self.indexed_items) + .for_each(|(t, v)| pw.set_u256_target(t, v)); + pw.set_target_arr(&wires.index_ids, &self.index_ids); + pw.set_hash_target(wires.tree_hash, self.tree_hash); + pw.set_u256_target(&wires.counter, self.counter); + pw.set_bool_target(wires.is_stored_in_leaf, self.is_stored_in_leaf); + pw.set_u256_target(&wires.offset_range_min, self.offset_range_min); + pw.set_u256_target(&wires.offset_range_max, self.offset_range_max); + } +} + +/// Verified proof number = 0 +pub(crate) const NUM_VERIFIED_PROOFS: usize = 0; + +impl CircuitLogicWires + for RecordWires +{ + type CircuitBuilderParams = (); + type Inputs = RecordCircuit; + + const NUM_PUBLIC_INPUTS: usize = PublicInputs::::total_len(); + + fn circuit_logic( + builder: &mut CBuilder, + verified_proofs: [&ProofWithPublicInputsTarget; NUM_VERIFIED_PROOFS], + builder_parameters: Self::CircuitBuilderParams, + ) -> Self { + Self::Inputs::build(builder) + } + + fn assign_input(&self, inputs: Self::Inputs, pw: &mut PartialWitness) -> anyhow::Result<()> { + inputs.assign(pw, self); + Ok(()) + } +} + +// TODO(Insun35): Add test