Skip to content

Commit d022777

Browse files
committed
Merge branch 'main' into fe-contains
2 parents 81abd15 + b93187c commit d022777

File tree

22 files changed

+1917
-464
lines changed

22 files changed

+1917
-464
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ strum = "0.26"
1414
strum_macros = "0.26"
1515
anyhow = "1.0.56"
1616
dyn-clone = "1.0.18"
17+
log = "0.4"
18+
env_logger = "0.11"
1719
# enabled by features:
1820
plonky2 = { git = "https://github.com/0xPolygonZero/plonky2", optional = true }
21+
serde = "1.0.219"
22+
serde_json = "1.0.140"
23+
base64 = "0.22.1"
24+
schemars = "1.0.0-alpha.17"
1925

2026
[features]
2127
default = ["backend_plonky2"]

src/backends/plonky2/basetypes.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
//! `backend_plonky2` feature is enabled.
33
//! See src/middleware/basetypes.rs for more details.
44
5+
use crate::middleware::serialization::{
6+
deserialize_hash_tuple, deserialize_value_tuple, serialize_hash_tuple, serialize_value_tuple,
7+
};
8+
use crate::middleware::{Params, ToFields};
59
use anyhow::{anyhow, Error, Result};
610
use hex::{FromHex, FromHexError};
711
use plonky2::field::goldilocks_field::GoldilocksField;
@@ -10,11 +14,11 @@ use plonky2::hash::poseidon::PoseidonHash;
1014
use plonky2::plonk::config::Hasher;
1115
use plonky2::plonk::config::PoseidonGoldilocksConfig;
1216
use plonky2::plonk::proof::Proof as Plonky2Proof;
17+
use schemars::JsonSchema;
18+
use serde::{Deserialize, Serialize};
1319
use std::cmp::{Ord, Ordering};
1420
use std::fmt;
1521

16-
use crate::middleware::{Params, ToFields};
17-
1822
use crate::backends::counter;
1923

2024
/// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
@@ -34,12 +38,22 @@ pub const EMPTY_VALUE: Value = Value([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
3438
pub const SELF_ID_HASH: Hash = Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO]);
3539
pub const EMPTY_HASH: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
3640

37-
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
38-
pub struct Value(pub [F; VALUE_SIZE]);
41+
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
42+
#[schemars(rename = "MiddlewareValue")]
43+
pub struct Value(
44+
#[serde(
45+
serialize_with = "serialize_value_tuple",
46+
deserialize_with = "deserialize_value_tuple"
47+
)]
48+
// We know that Serde will serialize and deserialize this as a string, so we can
49+
// use the JsonSchema to validate the format.
50+
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
51+
pub [F; VALUE_SIZE],
52+
);
3953

4054
impl ToFields for Value {
41-
fn to_fields(&self, _params: &Params) -> (Vec<F>, usize) {
42-
(self.0.to_vec(), VALUE_SIZE)
55+
fn to_fields(&self, _params: &Params) -> Vec<F> {
56+
self.0.to_vec()
4357
}
4458
}
4559

@@ -117,8 +131,15 @@ impl fmt::Display for Value {
117131
}
118132
}
119133

120-
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)]
121-
pub struct Hash(pub [F; HASH_SIZE]);
134+
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
135+
pub struct Hash(
136+
#[serde(
137+
serialize_with = "serialize_hash_tuple",
138+
deserialize_with = "deserialize_hash_tuple"
139+
)]
140+
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
141+
pub [F; HASH_SIZE],
142+
);
122143

123144
pub fn hash_value(input: &Value) -> Hash {
124145
hash_fields(&input.0)
@@ -143,8 +164,8 @@ impl Hash {
143164
}
144165

145166
impl ToFields for Hash {
146-
fn to_fields(&self, _params: &Params) -> (Vec<F>, usize) {
147-
(self.0.to_vec(), VALUE_SIZE)
167+
fn to_fields(&self, _params: &Params) -> Vec<F> {
168+
self.0.to_vec()
148169
}
149170
}
150171

src/backends/plonky2/common.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//! Common functionality to build Pod circuits with plonky2
2+
3+
use crate::backends::plonky2::mock_main::Statement;
4+
use crate::backends::plonky2::mock_main::{Operation, OperationArg};
5+
use crate::middleware::{Params, StatementArg, ToFields, Value, F, HASH_SIZE, VALUE_SIZE};
6+
use crate::middleware::{OPERATION_ARG_F_LEN, STATEMENT_ARG_F_LEN};
7+
use anyhow::Result;
8+
use plonky2::field::extension::Extendable;
9+
use plonky2::field::types::{Field, PrimeField64};
10+
use plonky2::hash::hash_types::RichField;
11+
use plonky2::iop::target::{BoolTarget, Target};
12+
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
13+
use plonky2::plonk::circuit_builder::CircuitBuilder;
14+
use std::iter;
15+
16+
#[derive(Copy, Clone)]
17+
pub struct ValueTarget {
18+
pub elements: [Target; VALUE_SIZE],
19+
}
20+
21+
#[derive(Clone)]
22+
pub struct StatementTarget {
23+
pub predicate: [Target; Params::predicate_size()],
24+
pub args: Vec<[Target; STATEMENT_ARG_F_LEN]>,
25+
}
26+
27+
impl StatementTarget {
28+
pub fn to_flattened(&self) -> Vec<Target> {
29+
self.predicate
30+
.iter()
31+
.chain(self.args.iter().flatten())
32+
.cloned()
33+
.collect()
34+
}
35+
36+
pub fn set_targets(
37+
&self,
38+
pw: &mut PartialWitness<F>,
39+
params: &Params,
40+
st: &Statement,
41+
) -> Result<()> {
42+
pw.set_target_arr(&self.predicate, &st.predicate().to_fields(params))?;
43+
for (i, arg) in st
44+
.args()
45+
.iter()
46+
.chain(iter::repeat(&StatementArg::None))
47+
.take(params.max_statement_args)
48+
.enumerate()
49+
{
50+
pw.set_target_arr(&self.args[i], &arg.to_fields(params))?;
51+
}
52+
Ok(())
53+
}
54+
}
55+
56+
// TODO: Implement Operation::to_field to determine the size of each element
57+
#[derive(Clone)]
58+
pub struct OperationTarget {
59+
pub op_type: [Target; Params::operation_type_size()],
60+
pub args: Vec<[Target; OPERATION_ARG_F_LEN]>,
61+
}
62+
63+
impl OperationTarget {
64+
pub fn set_targets(
65+
&self,
66+
pw: &mut PartialWitness<F>,
67+
params: &Params,
68+
op: &Operation,
69+
) -> Result<()> {
70+
pw.set_target_arr(&self.op_type, &op.op_type().to_fields(params))?;
71+
for (i, arg) in op
72+
.args()
73+
.iter()
74+
.chain(iter::repeat(&OperationArg::None))
75+
.take(params.max_operation_args)
76+
.enumerate()
77+
{
78+
pw.set_target_arr(&self.args[i], &arg.to_fields(params))?;
79+
}
80+
Ok(())
81+
}
82+
}
83+
84+
pub trait CircuitBuilderPod<F: RichField + Extendable<D>, const D: usize> {
85+
fn connect_values(&mut self, x: ValueTarget, y: ValueTarget);
86+
fn connect_slice(&mut self, xs: &[Target], ys: &[Target]);
87+
fn add_virtual_value(&mut self) -> ValueTarget;
88+
fn add_virtual_statement(&mut self, params: &Params) -> StatementTarget;
89+
fn add_virtual_operation(&mut self, params: &Params) -> OperationTarget;
90+
fn select_value(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget) -> ValueTarget;
91+
fn select_bool(&mut self, b: BoolTarget, x: BoolTarget, y: BoolTarget) -> BoolTarget;
92+
fn constant_value(&mut self, v: Value) -> ValueTarget;
93+
fn is_equal_slice(&mut self, xs: &[Target], ys: &[Target]) -> BoolTarget;
94+
}
95+
96+
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderPod<F, D>
97+
for CircuitBuilder<F, D>
98+
{
99+
fn connect_slice(&mut self, xs: &[Target], ys: &[Target]) {
100+
assert_eq!(xs.len(), ys.len());
101+
for (x, y) in xs.iter().zip(ys.iter()) {
102+
self.connect(*x, *y);
103+
}
104+
}
105+
106+
fn connect_values(&mut self, x: ValueTarget, y: ValueTarget) {
107+
self.connect_slice(&x.elements, &y.elements);
108+
}
109+
110+
fn add_virtual_value(&mut self) -> ValueTarget {
111+
ValueTarget {
112+
elements: self.add_virtual_target_arr(),
113+
}
114+
}
115+
116+
fn add_virtual_statement(&mut self, params: &Params) -> StatementTarget {
117+
StatementTarget {
118+
predicate: self.add_virtual_target_arr(),
119+
args: (0..params.max_statement_args)
120+
.map(|_| self.add_virtual_target_arr())
121+
.collect(),
122+
}
123+
}
124+
125+
fn add_virtual_operation(&mut self, params: &Params) -> OperationTarget {
126+
OperationTarget {
127+
op_type: self.add_virtual_target_arr(),
128+
args: (0..params.max_operation_args)
129+
.map(|_| self.add_virtual_target_arr())
130+
.collect(),
131+
}
132+
}
133+
134+
fn select_value(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget) -> ValueTarget {
135+
ValueTarget {
136+
elements: std::array::from_fn(|i| self.select(b, x.elements[i], y.elements[i])),
137+
}
138+
}
139+
140+
fn select_bool(&mut self, b: BoolTarget, x: BoolTarget, y: BoolTarget) -> BoolTarget {
141+
BoolTarget::new_unsafe(self.select(b, x.target, y.target))
142+
}
143+
144+
fn constant_value(&mut self, v: Value) -> ValueTarget {
145+
ValueTarget {
146+
elements: std::array::from_fn(|i| {
147+
self.constant(F::from_noncanonical_u64(v.0[i].to_noncanonical_u64()))
148+
}),
149+
}
150+
}
151+
152+
fn is_equal_slice(&mut self, xs: &[Target], ys: &[Target]) -> BoolTarget {
153+
assert_eq!(xs.len(), ys.len());
154+
let init = self._true();
155+
xs.iter().zip(ys.iter()).fold(init, |ok, (x, y)| {
156+
let is_eq = self.is_equal(*x, *y);
157+
self.and(ok, is_eq)
158+
})
159+
}
160+
}

0 commit comments

Comments
 (0)