Skip to content

Commit f34c61f

Browse files
committed
[WIP] feat: implement record circuit
1 parent 25d5e4c commit f34c61f

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
pub(crate) mod public_inputs;
2+
pub(crate) mod record;
3+
4+
use public_inputs::PublicInputs;
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
use crate::results_tree::extraction::PublicInputs;
2+
use alloy::primitives::U256;
3+
use mp2_common::{
4+
group_hashing::CircuitBuilderGroupHashing,
5+
poseidon::{empty_poseidon_hash, H},
6+
public_inputs::PublicInputCommon,
7+
serialization::{deserialize, deserialize_long_array, serialize, serialize_long_array},
8+
types::CBuilder,
9+
u256::{CircuitBuilderU256, UInt256Target, WitnessWriteU256},
10+
utils::{SelectHashBuilder, ToTargets},
11+
D, F,
12+
};
13+
use plonky2::{
14+
hash::hash_types::{HashOut, HashOutTarget},
15+
iop::{
16+
target::{BoolTarget, Target},
17+
witness::{PartialWitness, WitnessWrite},
18+
},
19+
plonk::proof::ProofWithPublicInputsTarget,
20+
};
21+
use recursion_framework::circuit_builder::CircuitLogicWires;
22+
use serde::{Deserialize, Serialize};
23+
use std::iter;
24+
25+
#[derive(Clone, Debug, Serialize, Deserialize)]
26+
pub struct RecordWires<const MAX_NUM_RESULTS: usize> {
27+
#[serde(
28+
serialize_with = "serialize_long_array",
29+
deserialize_with = "deserialize_long_array"
30+
)]
31+
indexed_items: [UInt256Target; MAX_NUM_RESULTS],
32+
#[serde(
33+
serialize_with = "serialize_long_array",
34+
deserialize_with = "deserialize_long_array"
35+
)]
36+
index_ids: [Target; MAX_NUM_RESULTS],
37+
#[serde(serialize_with = "serialize", deserialize_with = "deserialize")]
38+
tree_hash: HashOutTarget,
39+
counter: UInt256Target,
40+
#[serde(serialize_with = "serialize", deserialize_with = "deserialize")]
41+
is_stored_in_leaf: BoolTarget,
42+
offset_range_min: UInt256Target,
43+
offset_range_max: UInt256Target,
44+
}
45+
46+
#[derive(Clone, Debug, Serialize, Deserialize)]
47+
pub struct RecordCircuit<const MAX_NUM_RESULTS: usize> {
48+
// TODO(Insun35): add comments
49+
#[serde(
50+
serialize_with = "serialize_long_array",
51+
deserialize_with = "deserialize_long_array"
52+
)]
53+
pub(crate) indexed_items: [U256; MAX_NUM_RESULTS],
54+
#[serde(
55+
serialize_with = "serialize_long_array",
56+
deserialize_with = "deserialize_long_array"
57+
)]
58+
pub(crate) index_ids: [F; MAX_NUM_RESULTS],
59+
pub(crate) tree_hash: HashOut<F>,
60+
pub(crate) counter: U256,
61+
pub(crate) is_stored_in_leaf: bool,
62+
/// Minimum offset range bound
63+
pub(crate) offset_range_min: U256,
64+
/// Maximum offset range bound
65+
pub(crate) offset_range_max: U256,
66+
}
67+
68+
impl<const MAX_NUM_RESULTS: usize> RecordCircuit<MAX_NUM_RESULTS> {
69+
pub fn build(b: &mut CBuilder) -> RecordWires<MAX_NUM_RESULTS> {
70+
let ffalse = b._false();
71+
let empty_hash = b.constant_hash(*empty_poseidon_hash());
72+
73+
let indexed_items: [UInt256Target; MAX_NUM_RESULTS] = b.add_virtual_u256_arr_unsafe();
74+
let index_ids: [Target; MAX_NUM_RESULTS] = b.add_virtual_target_arr();
75+
let mut tree_hash = b.add_virtual_hash();
76+
let counter = b.add_virtual_u256_unsafe();
77+
let is_stored_in_leaf = b.add_virtual_bool_target_safe();
78+
let offset_range_min = b.add_virtual_u256_unsafe();
79+
let offset_range_max = b.add_virtual_u256_unsafe();
80+
81+
// H(H("")||H("")||indexed_items[1]||indexed_items[1]||index_ids[1]||indexed_items[1]||tree_hash)
82+
let tree_hash_inputs = empty_hash
83+
.elements
84+
.iter()
85+
.cloned()
86+
.chain(empty_hash.elements)
87+
.chain(indexed_items[1].to_targets())
88+
.chain(indexed_items[1].to_targets())
89+
.chain(iter::once(index_ids[1]))
90+
.chain(indexed_items[1].to_targets())
91+
.chain(tree_hash.elements)
92+
.collect();
93+
let new_tree_hash = b.hash_n_to_hash_no_pad::<H>(tree_hash_inputs);
94+
tree_hash = b.select_hash(is_stored_in_leaf, &new_tree_hash, &tree_hash);
95+
96+
// D(index_ids[0]||indexed_items[0]||index_ids[1]||indexed_items[1]||tree_hash)
97+
let accumulator_inputs: Vec<_> = iter::once(index_ids[0])
98+
.chain(indexed_items[0].to_targets())
99+
.chain(iter::once(index_ids[1]))
100+
.chain(indexed_items[1].to_targets())
101+
.chain(tree_hash.to_targets())
102+
.collect();
103+
let accumulator = b.map_to_curve_point(&accumulator_inputs);
104+
105+
// Ensure the counter associated to the current record is in the range
106+
// specified by the query
107+
// offset_range_min <= counter <= offset_range_max
108+
// -> NOT((counter < offset_range_min) OR (counter > offset_range_max)
109+
let is_less_than = b.is_less_than_u256(&counter, &offset_range_min);
110+
let is_greater_than = b.is_greater_than_u256(&counter, &offset_range_max);
111+
let is_out_of_range = b.or(is_less_than, is_greater_than);
112+
b.connect(is_out_of_range.target, ffalse.target);
113+
114+
// Register the public inputs.
115+
PublicInputs::<_, MAX_NUM_RESULTS>::new(
116+
&tree_hash.to_targets(),
117+
&indexed_items[1].to_targets(),
118+
&indexed_items[1].to_targets(),
119+
&indexed_items[0].to_targets(),
120+
&index_ids,
121+
&counter.to_targets(),
122+
&counter.to_targets(),
123+
&offset_range_min.to_targets(),
124+
&offset_range_max.to_targets(),
125+
&accumulator.to_targets(),
126+
)
127+
.register(b);
128+
129+
RecordWires {
130+
indexed_items,
131+
index_ids,
132+
tree_hash,
133+
counter,
134+
is_stored_in_leaf,
135+
offset_range_min,
136+
offset_range_max,
137+
}
138+
}
139+
140+
fn assign(&self, pw: &mut PartialWitness<F>, wires: &RecordWires<MAX_NUM_RESULTS>) {
141+
wires
142+
.indexed_items
143+
.iter()
144+
.zip(self.indexed_items)
145+
.for_each(|(t, v)| pw.set_u256_target(t, v));
146+
pw.set_target_arr(&wires.index_ids, &self.index_ids);
147+
pw.set_hash_target(wires.tree_hash, self.tree_hash);
148+
pw.set_u256_target(&wires.counter, self.counter);
149+
pw.set_bool_target(wires.is_stored_in_leaf, self.is_stored_in_leaf);
150+
pw.set_u256_target(&wires.offset_range_min, self.offset_range_min);
151+
pw.set_u256_target(&wires.offset_range_max, self.offset_range_max);
152+
}
153+
}
154+
155+
/// Verified proof number = 0
156+
pub(crate) const NUM_VERIFIED_PROOFS: usize = 0;
157+
158+
impl<const MAX_NUM_RESULTS: usize> CircuitLogicWires<F, D, NUM_VERIFIED_PROOFS>
159+
for RecordWires<MAX_NUM_RESULTS>
160+
{
161+
type CircuitBuilderParams = ();
162+
type Inputs = RecordCircuit<MAX_NUM_RESULTS>;
163+
164+
const NUM_PUBLIC_INPUTS: usize = PublicInputs::<F, MAX_NUM_RESULTS>::total_len();
165+
166+
fn circuit_logic(
167+
builder: &mut CBuilder,
168+
verified_proofs: [&ProofWithPublicInputsTarget<D>; NUM_VERIFIED_PROOFS],
169+
builder_parameters: Self::CircuitBuilderParams,
170+
) -> Self {
171+
Self::Inputs::build(builder)
172+
}
173+
174+
fn assign_input(&self, inputs: Self::Inputs, pw: &mut PartialWitness<F>) -> anyhow::Result<()> {
175+
inputs.assign(pw, self);
176+
Ok(())
177+
}
178+
}
179+
180+
// TODO(Insun35): Add test

0 commit comments

Comments
 (0)