-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] feat: implement record circuit
- Loading branch information
Showing
2 changed files
with
183 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
pub(crate) mod public_inputs; | ||
pub(crate) mod record; | ||
|
||
use public_inputs::PublicInputs; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<const MAX_NUM_RESULTS: usize> { | ||
#[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<const MAX_NUM_RESULTS: usize> { | ||
// 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<F>, | ||
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<const MAX_NUM_RESULTS: usize> RecordCircuit<MAX_NUM_RESULTS> { | ||
pub fn build(b: &mut CBuilder) -> RecordWires<MAX_NUM_RESULTS> { | ||
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::<H>(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<F>, wires: &RecordWires<MAX_NUM_RESULTS>) { | ||
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<const MAX_NUM_RESULTS: usize> CircuitLogicWires<F, D, NUM_VERIFIED_PROOFS> | ||
for RecordWires<MAX_NUM_RESULTS> | ||
{ | ||
type CircuitBuilderParams = (); | ||
type Inputs = RecordCircuit<MAX_NUM_RESULTS>; | ||
|
||
const NUM_PUBLIC_INPUTS: usize = PublicInputs::<F, MAX_NUM_RESULTS>::total_len(); | ||
|
||
fn circuit_logic( | ||
builder: &mut CBuilder, | ||
verified_proofs: [&ProofWithPublicInputsTarget<D>; NUM_VERIFIED_PROOFS], | ||
builder_parameters: Self::CircuitBuilderParams, | ||
) -> Self { | ||
Self::Inputs::build(builder) | ||
} | ||
|
||
fn assign_input(&self, inputs: Self::Inputs, pw: &mut PartialWitness<F>) -> anyhow::Result<()> { | ||
inputs.assign(pw, self); | ||
Ok(()) | ||
} | ||
} | ||
|
||
// TODO(Insun35): Add test |