mirror of https://github.com/zcash/orchard.git
Combine constants fixed columns using assign_advice_from_constant.
Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
parent
a10aefc8c1
commit
d80333799d
|
@ -1,3 +1,4 @@
|
|||
|
||||
[package]
|
||||
name = "orchard"
|
||||
version = "0.0.0"
|
||||
|
@ -62,5 +63,5 @@ name = "small"
|
|||
harness = false
|
||||
|
||||
[patch.crates-io]
|
||||
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "dda1be47316c32585c0d974c0b6401108714875d" }
|
||||
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "62f088e2f249dfce2c3bcab7321cba0d99697af9" }
|
||||
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "cc533a9da4f6a7209a7be05f82b12a03969152c9" }
|
||||
|
|
166
src/circuit.rs
166
src/circuit.rs
|
@ -5,7 +5,7 @@ use std::mem;
|
|||
use group::{Curve, GroupEncoding};
|
||||
use halo2::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
plonk::{self, Advice, Column, Expression, Instance as InstanceColumn, Selector},
|
||||
plonk::{self, Advice, Column, Expression, Fixed, Instance as InstanceColumn, Selector},
|
||||
poly::Rotation,
|
||||
transcript::{Blake2bRead, Blake2bWrite},
|
||||
};
|
||||
|
@ -69,21 +69,21 @@ pub(crate) mod gadget;
|
|||
const K: u32 = 12;
|
||||
|
||||
// Absolute offsets for public inputs.
|
||||
const ZERO: usize = 0;
|
||||
const ANCHOR: usize = 1;
|
||||
const NF_OLD: usize = 2;
|
||||
const CV_NET_X: usize = 3;
|
||||
const CV_NET_Y: usize = 4;
|
||||
const RK_X: usize = 5;
|
||||
const RK_Y: usize = 6;
|
||||
const CMX: usize = 7;
|
||||
const ENABLE_SPEND: usize = 8;
|
||||
const ENABLE_OUTPUT: usize = 9;
|
||||
const ANCHOR: usize = 0;
|
||||
const NF_OLD: usize = 1;
|
||||
const CV_NET_X: usize = 2;
|
||||
const CV_NET_Y: usize = 3;
|
||||
const RK_X: usize = 4;
|
||||
const RK_Y: usize = 5;
|
||||
const CMX: usize = 6;
|
||||
const ENABLE_SPEND: usize = 7;
|
||||
const ENABLE_OUTPUT: usize = 8;
|
||||
|
||||
/// Configuration needed to use the Orchard Action circuit.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
primary: Column<InstanceColumn>,
|
||||
constants: Column<Fixed>,
|
||||
q_orchard: Selector,
|
||||
advices: [Column<Advice>; 10],
|
||||
ecc_config: EccConfig,
|
||||
|
@ -190,49 +190,22 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
let table_idx = meta.fixed_column();
|
||||
let lookup = (table_idx, meta.fixed_column(), meta.fixed_column());
|
||||
|
||||
// Shared fixed column used to load constants.
|
||||
// TODO: Replace with public inputs API
|
||||
let ecc_constants = [meta.fixed_column(), meta.fixed_column()];
|
||||
let sinsemilla_1_constants = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
let sinsemilla_2_constants = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
|
||||
// Instance column.
|
||||
// Instance column used for public inputs
|
||||
let primary = meta.instance_column();
|
||||
|
||||
meta.enable_equality(primary.into());
|
||||
|
||||
// Permutation over all advice columns and `constants` columns.
|
||||
// TODO: Replace `*_constants` with public inputs API.
|
||||
// Shared fixed column used to load constants
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
// Permutation over all advice columns.
|
||||
for advice in advices.iter() {
|
||||
meta.enable_equality((*advice).into());
|
||||
}
|
||||
for fixed in ecc_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
for fixed in sinsemilla_1_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
for fixed in sinsemilla_2_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
|
||||
// Configuration for curve point operations.
|
||||
// This uses 10 advice columns and spans the whole circuit.
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx, ecc_constants);
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx);
|
||||
|
||||
// Configuration for the Poseidon hash.
|
||||
let poseidon_config = PoseidonChip::configure(
|
||||
|
@ -250,12 +223,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
// Since the Sinsemilla config uses only 5 advice columns,
|
||||
// we can fit two instances side-by-side.
|
||||
let (sinsemilla_config_1, merkle_config_1) = {
|
||||
let sinsemilla_config_1 = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
lookup,
|
||||
sinsemilla_1_constants,
|
||||
);
|
||||
let sinsemilla_config_1 =
|
||||
SinsemillaChip::configure(meta, advices[..5].try_into().unwrap(), lookup);
|
||||
let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
|
||||
|
||||
(sinsemilla_config_1, merkle_config_1)
|
||||
|
@ -266,12 +235,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
// Since the Sinsemilla config uses only 5 advice columns,
|
||||
// we can fit two instances side-by-side.
|
||||
let (sinsemilla_config_2, merkle_config_2) = {
|
||||
let sinsemilla_config_2 = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[5..].try_into().unwrap(),
|
||||
lookup,
|
||||
sinsemilla_2_constants,
|
||||
);
|
||||
let sinsemilla_config_2 =
|
||||
SinsemillaChip::configure(meta, advices[5..].try_into().unwrap(), lookup);
|
||||
let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
|
||||
|
||||
(sinsemilla_config_2, merkle_config_2)
|
||||
|
@ -294,6 +259,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
|
||||
Config {
|
||||
primary,
|
||||
constants,
|
||||
q_orchard,
|
||||
advices,
|
||||
ecc_config,
|
||||
|
@ -401,27 +367,21 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
let v_net = {
|
||||
// v_net = v_old - v_new
|
||||
let v_net = {
|
||||
let v_net_val = self.v_old.zip(self.v_new).map(|(v_old, v_new)| {
|
||||
// Do the subtraction in the scalar field.
|
||||
let v_old = pallas::Scalar::from_u64(v_old.inner());
|
||||
let v_new = pallas::Scalar::from_u64(v_new.inner());
|
||||
v_old - v_new
|
||||
});
|
||||
// If v_net_val > (p - 1)/2, its sign is negative.
|
||||
let is_negative =
|
||||
v_net_val.map(|val| val > (-pallas::Scalar::one()) * pallas::Scalar::TWO_INV);
|
||||
let magnitude_sign =
|
||||
v_net_val
|
||||
.zip(is_negative)
|
||||
.map(|(signed_value, is_negative)| {
|
||||
let magnitude = {
|
||||
// v_old, v_new are guaranteed to be 64-bit values. Therefore, we can
|
||||
// move them into the base field.
|
||||
let v_old = self
|
||||
.v_old
|
||||
.map(|v_old| pallas::Base::from_u64(v_old.inner()));
|
||||
let v_new = self
|
||||
.v_new
|
||||
.map(|v_new| pallas::Base::from_u64(v_new.inner()));
|
||||
|
||||
let magnitude_sign = v_old.zip(v_new).map(|(v_old, v_new)| {
|
||||
let is_negative = v_old < v_new;
|
||||
let magnitude = if is_negative {
|
||||
-signed_value
|
||||
v_new - v_old
|
||||
} else {
|
||||
signed_value
|
||||
};
|
||||
assert!(magnitude < pallas::Scalar::from_u128(1 << 64));
|
||||
pallas::Base::from_bytes(&magnitude.to_bytes()).unwrap()
|
||||
v_old - v_new
|
||||
};
|
||||
let sign = if is_negative {
|
||||
-pallas::Base::one()
|
||||
|
@ -430,6 +390,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
(magnitude, sign)
|
||||
});
|
||||
|
||||
let magnitude = self.load_private(
|
||||
layouter.namespace(|| "v_net magnitude"),
|
||||
config.advices[9],
|
||||
|
@ -451,7 +412,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// blind = [rcv] ValueCommitR
|
||||
let (blind, _) = {
|
||||
let (blind, _rcv) = {
|
||||
let rcv = self.rcv.as_ref().map(|rcv| **rcv);
|
||||
let value_commit_r = OrchardFixedBasesFull::ValueCommitR;
|
||||
let value_commit_r = FixedPoint::from_inner(ecc_chip.clone(), value_commit_r);
|
||||
|
@ -595,7 +556,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// [ivk] g_d_old
|
||||
let (derived_pk_d_old, _) =
|
||||
// The scalar value is passed through and discarded.
|
||||
let (derived_pk_d_old, _ivk) =
|
||||
g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk.inner())?;
|
||||
|
||||
// Constrain derived pk_d_old to equal witnessed pk_d_old
|
||||
|
@ -756,7 +718,7 @@ impl VerifyingKey {
|
|||
/// Builds the verifying key.
|
||||
pub fn build() -> Self {
|
||||
let params = halo2::poly::commitment::Params::new(K);
|
||||
let circuit: Circuit = Default::default(); // TODO
|
||||
let circuit: Circuit = Default::default();
|
||||
|
||||
let vk = plonk::keygen_vk(¶ms, &circuit).unwrap();
|
||||
|
||||
|
@ -775,7 +737,7 @@ impl ProvingKey {
|
|||
/// Builds the proving key.
|
||||
pub fn build() -> Self {
|
||||
let params = halo2::poly::commitment::Params::new(K);
|
||||
let circuit: Circuit = Default::default(); // TODO
|
||||
let circuit: Circuit = Default::default();
|
||||
|
||||
let vk = plonk::keygen_vk(¶ms, &circuit).unwrap();
|
||||
let pk = plonk::keygen_pk(¶ms, vk, &circuit).unwrap();
|
||||
|
@ -797,10 +759,9 @@ pub struct Instance {
|
|||
}
|
||||
|
||||
impl Instance {
|
||||
fn to_halo2_instance(&self) -> [[vesta::Scalar; 10]; 1] {
|
||||
let mut instance = [vesta::Scalar::zero(); 10];
|
||||
fn to_halo2_instance(&self) -> [[vesta::Scalar; 9]; 1] {
|
||||
let mut instance = [vesta::Scalar::zero(); 9];
|
||||
|
||||
// instance[0] is left as 0.
|
||||
instance[ANCHOR] = *self.anchor;
|
||||
instance[CV_NET_X] = self.cv_net.x();
|
||||
instance[CV_NET_Y] = self.cv_net.y();
|
||||
|
@ -984,4 +945,43 @@ mod tests {
|
|||
let proof = Proof::create(&pk, &circuits, &instances).unwrap();
|
||||
assert!(proof.verify(&vk, &instances).is_ok());
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-graph")]
|
||||
#[test]
|
||||
fn print_action_circuit() {
|
||||
use plotters::prelude::*;
|
||||
|
||||
let root = BitMapBackend::new("action-circuit-layout.png", (1024, 768)).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let root = root
|
||||
.titled("Orchard Action Circuit", ("sans-serif", 60))
|
||||
.unwrap();
|
||||
|
||||
let circuit = Circuit {
|
||||
path: None,
|
||||
pos: None,
|
||||
g_d_old: None,
|
||||
pk_d_old: None,
|
||||
v_old: None,
|
||||
rho_old: None,
|
||||
psi_old: None,
|
||||
rcm_old: None,
|
||||
cm_old: None,
|
||||
alpha: None,
|
||||
ak: None,
|
||||
nk: None,
|
||||
rivk: None,
|
||||
g_d_new_star: None,
|
||||
pk_d_new_star: None,
|
||||
v_new: None,
|
||||
psi_new: None,
|
||||
rcm_new: None,
|
||||
rcv: None,
|
||||
};
|
||||
halo2::dev::CircuitLayout::default()
|
||||
.show_labels(false)
|
||||
.view_height(0..(1 << 11))
|
||||
.render(&circuit, &root)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -432,10 +432,12 @@ mod tests {
|
|||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
let constants = [meta.fixed_column(), meta.fixed_column()];
|
||||
let lookup_table = meta.fixed_column();
|
||||
// Shared fixed column for loading constants
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
EccChip::configure(meta, advices, lookup_table, constants)
|
||||
EccChip::configure(meta, advices, lookup_table)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
|
|
|
@ -111,8 +111,6 @@ pub struct EccConfig {
|
|||
/// Witness point
|
||||
pub q_point: Selector,
|
||||
|
||||
/// Shared fixed column used for loading constants.
|
||||
pub constants: Column<Fixed>,
|
||||
/// Lookup range check using 10-bit lookup table
|
||||
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
/// Running sum decomposition.
|
||||
|
@ -155,8 +153,6 @@ impl EccChip {
|
|||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
advices: [Column<Advice>; 10],
|
||||
lookup_table: Column<Fixed>,
|
||||
// TODO: Replace with public inputs API
|
||||
constants: [Column<Fixed>; 2],
|
||||
) -> <Self as Chip<pallas::Base>>::Config {
|
||||
// The following columns need to be equality-enabled for their use in sub-configs:
|
||||
//
|
||||
|
@ -183,18 +179,13 @@ impl EccChip {
|
|||
// mul::complete::Config:
|
||||
// - advices[9]: z_complete
|
||||
//
|
||||
// mul::Config:
|
||||
// - constants[1]: Setting `z_init` to zero.
|
||||
//
|
||||
// TODO: Refactor away from `impl From<EccConfig> for _` so that sub-configs can
|
||||
// equality-enable the columns they need to.
|
||||
for column in &advices {
|
||||
meta.enable_equality((*column).into());
|
||||
}
|
||||
// constants[0] is also equality-enabled here.
|
||||
let lookup_config =
|
||||
LookupRangeCheckConfig::configure(meta, advices[9], constants[0], lookup_table);
|
||||
meta.enable_equality(constants[1].into());
|
||||
|
||||
let lookup_config = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
|
||||
|
||||
let q_mul_fixed_running_sum = meta.selector();
|
||||
let running_sum_config =
|
||||
|
@ -225,7 +216,6 @@ impl EccChip {
|
|||
q_mul_fixed_base_field: meta.selector(),
|
||||
q_mul_fixed_running_sum,
|
||||
q_point: meta.selector(),
|
||||
constants: constants[1],
|
||||
lookup_config,
|
||||
running_sum_config,
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ use ff::PrimeField;
|
|||
use halo2::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Layouter, Region},
|
||||
plonk::{Column, ConstraintSystem, Error, Expression, Fixed, Selector},
|
||||
plonk::{ConstraintSystem, Error, Expression, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
||||
|
@ -40,8 +40,6 @@ const INCOMPLETE_LO_RANGE: Range<usize> = (INCOMPLETE_LEN / 2)..INCOMPLETE_LEN;
|
|||
const COMPLETE_RANGE: Range<usize> = INCOMPLETE_LEN..(INCOMPLETE_LEN + NUM_COMPLETE_BITS);
|
||||
|
||||
pub struct Config {
|
||||
// Fixed column used to constrain the initialization of the running sum to be zero.
|
||||
constants: Column<Fixed>,
|
||||
// Selector used to check switching logic on LSB
|
||||
q_mul_lsb: Selector,
|
||||
// Configuration used in complete addition
|
||||
|
@ -59,7 +57,6 @@ pub struct Config {
|
|||
impl From<&EccConfig> for Config {
|
||||
fn from(ecc_config: &EccConfig) -> Self {
|
||||
let config = Self {
|
||||
constants: ecc_config.constants,
|
||||
q_mul_lsb: ecc_config.q_mul_lsb,
|
||||
add_config: ecc_config.into(),
|
||||
hi_config: ecc_config.into(),
|
||||
|
@ -159,16 +156,14 @@ impl Config {
|
|||
|
||||
// Initialize the running sum for scalar decomposition to zero
|
||||
let z_init = {
|
||||
// Constrain the initialization of `z` to equal zero.
|
||||
let z_init_val = pallas::Base::zero();
|
||||
let z_init_cell = region.assign_fixed(
|
||||
|| "fixed z_init = 0",
|
||||
self.constants,
|
||||
let z_init_cell = region.assign_advice_from_constant(
|
||||
|| "z_init = 0",
|
||||
self.hi_config.z,
|
||||
offset,
|
||||
|| Ok(z_init_val),
|
||||
pallas::Base::zero(),
|
||||
)?;
|
||||
|
||||
Z(CellValue::new(z_init_cell, Some(z_init_val)))
|
||||
Z(CellValue::new(z_init_cell, Some(pallas::Base::zero())))
|
||||
};
|
||||
|
||||
// Double-and-add (incomplete addition) for the `hi` half of the scalar decomposition
|
||||
|
|
|
@ -241,7 +241,7 @@ pub mod tests {
|
|||
use group::Curve;
|
||||
use halo2::{
|
||||
circuit::{Chip, Layouter},
|
||||
plonk::Error,
|
||||
plonk::{Any, Error},
|
||||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
|
||||
|
@ -404,10 +404,13 @@ pub mod tests {
|
|||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
let constants = [meta.fixed_column(), meta.fixed_column()];
|
||||
let lookup_table = meta.fixed_column();
|
||||
|
||||
EccChip::configure(meta, advices, lookup_table, constants)
|
||||
// Shared fixed column for loading constants
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
EccChip::configure(meta, advices, lookup_table)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
|
@ -475,18 +478,22 @@ pub mod tests {
|
|||
assert_eq!(
|
||||
prover.verify(),
|
||||
Err(vec![
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: ((2, "final z = 0").into(), 0, "").into(),
|
||||
row: 24
|
||||
},
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: (
|
||||
(13, "Short fixed-base mul gate").into(),
|
||||
(12, "Short fixed-base mul gate").into(),
|
||||
0,
|
||||
"last_window_check"
|
||||
)
|
||||
.into(),
|
||||
row: 26
|
||||
},
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Fixed, 1).into(),
|
||||
row: 2
|
||||
},
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Advice, 4).into(),
|
||||
row: 24
|
||||
}
|
||||
])
|
||||
);
|
||||
|
@ -505,13 +512,13 @@ pub mod tests {
|
|||
prover.verify(),
|
||||
Err(vec![
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: ((13, "Short fixed-base mul gate").into(), 1, "sign_check")
|
||||
constraint: ((12, "Short fixed-base mul gate").into(), 1, "sign_check")
|
||||
.into(),
|
||||
row: 26
|
||||
},
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: (
|
||||
(13, "Short fixed-base mul gate").into(),
|
||||
(12, "Short fixed-base mul gate").into(),
|
||||
3,
|
||||
"negation_check"
|
||||
)
|
||||
|
|
|
@ -454,43 +454,19 @@ mod tests {
|
|||
meta.advice_column(),
|
||||
];
|
||||
|
||||
// TODO: Replace with public inputs API
|
||||
let constants_1 = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
let constants_2 = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
let ecc_constants = [meta.fixed_column(), meta.fixed_column()];
|
||||
// Shared fixed column for loading constants
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
let table_idx = meta.fixed_column();
|
||||
|
||||
// Fixed columns for the Sinsemilla generator lookup table
|
||||
let lookup = (table_idx, meta.fixed_column(), meta.fixed_column());
|
||||
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx, ecc_constants);
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx);
|
||||
|
||||
let config1 = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
lookup,
|
||||
constants_1,
|
||||
);
|
||||
let config2 = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[5..].try_into().unwrap(),
|
||||
lookup,
|
||||
constants_2,
|
||||
);
|
||||
let config1 = SinsemillaChip::configure(meta, advices[..5].try_into().unwrap(), lookup);
|
||||
let config2 = SinsemillaChip::configure(meta, advices[5..].try_into().unwrap(), lookup);
|
||||
(ecc_config, config1, config2)
|
||||
}
|
||||
|
||||
|
|
|
@ -56,10 +56,6 @@ pub struct SinsemillaConfig {
|
|||
/// The lookup table where $(\mathsf{idx}, x_p, y_p)$ are loaded for the $2^K$
|
||||
/// generators of the Sinsemilla hash.
|
||||
pub(super) generator_table: GeneratorTableConfig,
|
||||
/// Fixed column shared by the whole circuit. This is used to load the
|
||||
/// x-coordinate of the domain $Q$, which is then constrained to equal the
|
||||
/// initial $x_a$.
|
||||
pub(super) constants: Column<Fixed>,
|
||||
/// Configure each advice column to be able to perform lookup range checks.
|
||||
pub(super) lookup_config_0: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
pub(super) lookup_config_1: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
|
@ -115,23 +111,15 @@ impl SinsemillaChip {
|
|||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
advices: [Column<Advice>; 5],
|
||||
lookup: (Column<Fixed>, Column<Fixed>, Column<Fixed>),
|
||||
constants: [Column<Fixed>; 6], // TODO: replace with public inputs API
|
||||
) -> <Self as Chip<pallas::Base>>::Config {
|
||||
// This chip requires all advice columns and the `constants` fixed columns to be
|
||||
// equality-enabled. The advice columns and the first five `constants` columns
|
||||
// are equality-enabled by the calls to LookupRangeCheckConfig::configure.
|
||||
let lookup_config_0 =
|
||||
LookupRangeCheckConfig::configure(meta, advices[0], constants[0], lookup.0);
|
||||
let lookup_config_1 =
|
||||
LookupRangeCheckConfig::configure(meta, advices[1], constants[1], lookup.0);
|
||||
let lookup_config_2 =
|
||||
LookupRangeCheckConfig::configure(meta, advices[2], constants[2], lookup.0);
|
||||
let lookup_config_3 =
|
||||
LookupRangeCheckConfig::configure(meta, advices[3], constants[3], lookup.0);
|
||||
let lookup_config_4 =
|
||||
LookupRangeCheckConfig::configure(meta, advices[4], constants[4], lookup.0);
|
||||
let constants = constants[5];
|
||||
meta.enable_equality(constants.into());
|
||||
let lookup_config_0 = LookupRangeCheckConfig::configure(meta, advices[0], lookup.0);
|
||||
let lookup_config_1 = LookupRangeCheckConfig::configure(meta, advices[1], lookup.0);
|
||||
let lookup_config_2 = LookupRangeCheckConfig::configure(meta, advices[2], lookup.0);
|
||||
let lookup_config_3 = LookupRangeCheckConfig::configure(meta, advices[3], lookup.0);
|
||||
let lookup_config_4 = LookupRangeCheckConfig::configure(meta, advices[4], lookup.0);
|
||||
|
||||
let config = SinsemillaConfig {
|
||||
q_sinsemilla1: meta.selector(),
|
||||
|
@ -147,7 +135,6 @@ impl SinsemillaChip {
|
|||
table_x: lookup.1,
|
||||
table_y: lookup.2,
|
||||
},
|
||||
constants,
|
||||
lookup_config_0,
|
||||
lookup_config_1,
|
||||
lookup_config_2,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use super::super::SinsemillaInstructions;
|
||||
use super::{get_s_by_idx, CellValue, EccPoint, SinsemillaChip, Var};
|
||||
use crate::{
|
||||
circuit::gadget::utilities::copy,
|
||||
primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K},
|
||||
};
|
||||
use crate::primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K};
|
||||
use halo2::{
|
||||
circuit::{Chip, Region},
|
||||
plonk::Error,
|
||||
|
@ -41,14 +38,12 @@ impl SinsemillaChip {
|
|||
// Initialize the accumulator to `Q`.
|
||||
let (mut x_a, mut y_a): (X<pallas::Base>, Y<pallas::Base>) = {
|
||||
// Constrain the initial x_q to equal the x-coordinate of the domain's `Q`.
|
||||
let fixed_x_q = {
|
||||
let x_a = {
|
||||
let cell =
|
||||
region.assign_fixed(|| "fixed x_q", config.constants, offset, || Ok(x_q))?;
|
||||
region.assign_advice_from_constant(|| "public x_q", config.x_a, offset, x_q)?;
|
||||
CellValue::new(cell, Some(x_q))
|
||||
};
|
||||
|
||||
let x_a = copy(region, || "x_q", config.x_a, offset, &fixed_x_q)?;
|
||||
|
||||
// Constrain the initial x_a, lambda_1, lambda_2, x_p using the fixed y_q
|
||||
// initializer. Assign `fixed_y_q` to be zero on every other row.
|
||||
{
|
||||
|
@ -132,7 +127,7 @@ impl SinsemillaChip {
|
|||
let field_elems: Option<Vec<pallas::Base>> =
|
||||
message.iter().map(|piece| piece.field_elem()).collect();
|
||||
|
||||
if field_elems.is_some() {
|
||||
if field_elems.is_some() && x_a.value().is_some() && y_a.value().is_some() {
|
||||
// Get message as a bitstring.
|
||||
let bitstring: Vec<bool> = message
|
||||
.iter()
|
||||
|
|
|
@ -676,41 +676,22 @@ mod tests {
|
|||
meta.advice_column(),
|
||||
];
|
||||
|
||||
// Shared fixed columns for loading constants.
|
||||
// TODO: Replace with public inputs API.
|
||||
let ecc_constants = [meta.fixed_column(), meta.fixed_column()];
|
||||
let sinsemilla_constants = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
for advice in advices.iter() {
|
||||
meta.enable_equality((*advice).into());
|
||||
}
|
||||
for fixed in ecc_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
for fixed in sinsemilla_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
|
||||
let table_idx = meta.fixed_column();
|
||||
let lookup = (table_idx, meta.fixed_column(), meta.fixed_column());
|
||||
|
||||
let sinsemilla_config = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
lookup,
|
||||
sinsemilla_constants,
|
||||
);
|
||||
let sinsemilla_config =
|
||||
SinsemillaChip::configure(meta, advices[..5].try_into().unwrap(), lookup);
|
||||
let commit_ivk_config =
|
||||
CommitIvkConfig::configure(meta, advices, sinsemilla_config);
|
||||
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx, ecc_constants);
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx);
|
||||
|
||||
(commit_ivk_config, ecc_config)
|
||||
}
|
||||
|
|
|
@ -188,23 +188,8 @@ pub mod tests {
|
|||
];
|
||||
|
||||
// Shared fixed column for loading constants
|
||||
// TODO: Replace with public inputs API
|
||||
let constants_1 = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
let constants_2 = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
// Fixed columns for the Sinsemilla generator lookup table
|
||||
let lookup = (
|
||||
|
@ -213,20 +198,12 @@ pub mod tests {
|
|||
meta.fixed_column(),
|
||||
);
|
||||
|
||||
let sinsemilla_config_1 = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[5..].try_into().unwrap(),
|
||||
lookup,
|
||||
constants_1,
|
||||
);
|
||||
let sinsemilla_config_1 =
|
||||
SinsemillaChip::configure(meta, advices[5..].try_into().unwrap(), lookup);
|
||||
let config1 = MerkleChip::configure(meta, sinsemilla_config_1);
|
||||
|
||||
let sinsemilla_config_2 = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
lookup,
|
||||
constants_2,
|
||||
);
|
||||
let sinsemilla_config_2 =
|
||||
SinsemillaChip::configure(meta, advices[..5].try_into().unwrap(), lookup);
|
||||
let config2 = MerkleChip::configure(meta, sinsemilla_config_2);
|
||||
|
||||
(config1, config2)
|
||||
|
|
|
@ -1337,40 +1337,22 @@ mod tests {
|
|||
meta.advice_column(),
|
||||
];
|
||||
|
||||
// Shared fixed columns for loading constants.
|
||||
// TODO: Replace with public inputs API.
|
||||
let ecc_constants = [meta.fixed_column(), meta.fixed_column()];
|
||||
let sinsemilla_constants = [
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
meta.fixed_column(),
|
||||
];
|
||||
// Shared fixed column for loading constants.
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
for advice in advices.iter() {
|
||||
meta.enable_equality((*advice).into());
|
||||
}
|
||||
for fixed in ecc_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
for fixed in sinsemilla_constants.iter() {
|
||||
meta.enable_equality((*fixed).into());
|
||||
}
|
||||
|
||||
let table_idx = meta.fixed_column();
|
||||
let lookup = (table_idx, meta.fixed_column(), meta.fixed_column());
|
||||
let sinsemilla_config = SinsemillaChip::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
lookup,
|
||||
sinsemilla_constants,
|
||||
);
|
||||
let sinsemilla_config =
|
||||
SinsemillaChip::configure(meta, advices[..5].try_into().unwrap(), lookup);
|
||||
let note_commit_config =
|
||||
NoteCommitConfig::configure(meta, advices, sinsemilla_config);
|
||||
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx, ecc_constants);
|
||||
let ecc_config = EccChip::configure(meta, advices, table_idx);
|
||||
|
||||
(note_commit_config, ecc_config)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
|
|||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct RunningSumConfig<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize> {
|
||||
q_range_check: Selector,
|
||||
q_strict: Selector,
|
||||
pub z: Column<Advice>,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
@ -74,7 +73,6 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
|
||||
let config = Self {
|
||||
q_range_check,
|
||||
q_strict: meta.selector(),
|
||||
z,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
@ -90,13 +88,6 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
vec![q_range_check * range_check(word, 1 << WINDOW_NUM_BITS)]
|
||||
});
|
||||
|
||||
meta.create_gate("final z = 0", |meta| {
|
||||
let q_strict = meta.query_selector(config.q_strict);
|
||||
let z_final = meta.query_advice(config.z, Rotation::cur());
|
||||
|
||||
vec![q_strict * z_final]
|
||||
});
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
|
@ -169,17 +160,10 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
assert!(WINDOW_NUM_BITS * num_windows < word_num_bits + WINDOW_NUM_BITS);
|
||||
|
||||
// Enable selectors
|
||||
{
|
||||
for idx in 0..num_windows {
|
||||
self.q_range_check.enable(region, offset + idx)?;
|
||||
}
|
||||
|
||||
if strict {
|
||||
// Constrain the final running sum output to be zero.
|
||||
self.q_strict.enable(region, offset + num_windows)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Decompose base field element into K-bit words.
|
||||
let words: Vec<Option<u8>> = {
|
||||
let words = z_0
|
||||
|
@ -223,6 +207,11 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
zs.push(z);
|
||||
}
|
||||
|
||||
if strict {
|
||||
// Constrain the final running sum output to be zero.
|
||||
region.constrain_constant(zs.last().unwrap().cell(), F::zero())?;
|
||||
}
|
||||
|
||||
Ok((z_0, RunningSum(zs)))
|
||||
}
|
||||
}
|
||||
|
@ -230,13 +219,11 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::constants::{
|
||||
FIXED_BASE_WINDOW_SIZE, L_ORCHARD_BASE, L_VALUE, NUM_WINDOWS, NUM_WINDOWS_SHORT,
|
||||
};
|
||||
use crate::constants::{self, FIXED_BASE_WINDOW_SIZE, L_ORCHARD_BASE, L_VALUE};
|
||||
use halo2::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
dev::{MockProver, VerifyFailure},
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
plonk::{Any, Circuit, ConstraintSystem, Error},
|
||||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
|
||||
|
@ -272,6 +259,8 @@ mod tests {
|
|||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let z = meta.advice_column();
|
||||
let q_range_check = meta.selector();
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
RunningSumConfig::<F, WINDOW_NUM_BITS>::configure(meta, q_range_check, z)
|
||||
}
|
||||
|
@ -320,7 +309,7 @@ mod tests {
|
|||
pallas::Base,
|
||||
L_ORCHARD_BASE,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
NUM_WINDOWS,
|
||||
{ constants::NUM_WINDOWS },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
|
@ -338,7 +327,7 @@ mod tests {
|
|||
pallas::Base,
|
||||
L_VALUE,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
NUM_WINDOWS_SHORT,
|
||||
{ constants::NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
|
@ -354,9 +343,9 @@ mod tests {
|
|||
// Strict partial decomposition should fail.
|
||||
let circuit: MyCircuit<
|
||||
pallas::Base,
|
||||
L_ORCHARD_BASE,
|
||||
L_VALUE,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
NUM_WINDOWS_SHORT,
|
||||
{ constants::NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
|
@ -365,23 +354,31 @@ mod tests {
|
|||
assert_eq!(
|
||||
prover.verify(),
|
||||
Err(vec![
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: ((1, "final z = 0").into(), 0, "").into(),
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Fixed, 1).into(),
|
||||
row: 0
|
||||
},
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Fixed, 1).into(),
|
||||
row: 1
|
||||
},
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Advice, 0).into(),
|
||||
row: 22
|
||||
},
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: ((1, "final z = 0").into(), 0, "").into(),
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Advice, 0).into(),
|
||||
row: 45
|
||||
}
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
// Non-strict partial decomposition should pass.
|
||||
let circuit: MyCircuit<
|
||||
pallas::Base,
|
||||
L_ORCHARD_BASE,
|
||||
{ constants::L_VALUE },
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
NUM_WINDOWS_SHORT,
|
||||
{ constants::NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: false,
|
||||
|
|
|
@ -19,7 +19,6 @@ pub struct LookupRangeCheckConfig<F: FieldExt + PrimeFieldBits, const K: usize>
|
|||
pub q_lookup_short: Selector,
|
||||
pub short_lookup_bitshift: Column<Fixed>,
|
||||
pub running_sum: Column<Advice>,
|
||||
constants: Column<Fixed>,
|
||||
table_idx: Column<Fixed>,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
@ -39,11 +38,9 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
pub fn configure(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
running_sum: Column<Advice>,
|
||||
constants: Column<Fixed>,
|
||||
table_idx: Column<Fixed>,
|
||||
) -> Self {
|
||||
meta.enable_equality(running_sum.into());
|
||||
meta.enable_equality(constants.into());
|
||||
|
||||
let q_lookup = meta.selector();
|
||||
let q_lookup_short = meta.selector();
|
||||
|
@ -53,7 +50,6 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
q_lookup_short,
|
||||
short_lookup_bitshift,
|
||||
running_sum,
|
||||
constants,
|
||||
table_idx,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
@ -135,7 +131,14 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
// Copy `element` and initialize running sum `z_0 = element` to decompose it.
|
||||
let z_0 = copy(&mut region, || "z_0", self.running_sum, 0, &element)?;
|
||||
|
||||
self.range_check(&mut region, z_0, num_words, strict)
|
||||
let zs = self.range_check(&mut region, z_0, num_words)?;
|
||||
|
||||
if strict {
|
||||
// Constrain the final `z` to be zero.
|
||||
region.constrain_constant(zs.last().unwrap().cell(), F::zero())?;
|
||||
}
|
||||
|
||||
Ok(zs)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -148,7 +151,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
num_words: usize,
|
||||
strict: bool,
|
||||
) -> Result<(CellValue<F>, Vec<CellValue<F>>), Error> {
|
||||
layouter.assign_region(
|
||||
let (z_0, zs) = layouter.assign_region(
|
||||
|| "Witness element",
|
||||
|mut region| {
|
||||
let z_0 = {
|
||||
|
@ -161,11 +164,18 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
CellValue::new(cell, value)
|
||||
};
|
||||
|
||||
let zs = self.range_check(&mut region, z_0, num_words, strict)?;
|
||||
let zs = self.range_check(&mut region, z_0, num_words)?;
|
||||
|
||||
if strict {
|
||||
// Constrain the final `z` to be zero.
|
||||
region.constrain_constant(zs.last().unwrap().cell(), F::zero())?;
|
||||
}
|
||||
|
||||
Ok((z_0, zs))
|
||||
},
|
||||
)
|
||||
)?;
|
||||
|
||||
Ok((z_0, zs))
|
||||
}
|
||||
|
||||
/// If `strict` is set to "true", the field element must fit into
|
||||
|
@ -180,7 +190,6 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
region: &mut Region<'_, F>,
|
||||
element: CellValue<F>,
|
||||
num_words: usize,
|
||||
strict: bool,
|
||||
) -> Result<Vec<CellValue<F>>, Error> {
|
||||
// `num_words` must fit into a single field element.
|
||||
assert!(num_words * K <= F::CAPACITY as usize);
|
||||
|
@ -243,13 +252,6 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
zs.push(z);
|
||||
}
|
||||
|
||||
if strict {
|
||||
// Constrain the final `z` to be zero.
|
||||
let cell =
|
||||
region.assign_fixed(|| "zero", self.constants, words.len(), || Ok(F::zero()))?;
|
||||
region.constrain_equal(z.cell(), cell)?;
|
||||
}
|
||||
|
||||
Ok(zs)
|
||||
}
|
||||
|
||||
|
@ -388,10 +390,11 @@ mod tests {
|
|||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let running_sum = meta.advice_column();
|
||||
let constants = meta.fixed_column();
|
||||
let table_idx = meta.fixed_column();
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
LookupRangeCheckConfig::<F, K>::configure(meta, running_sum, constants, table_idx)
|
||||
LookupRangeCheckConfig::<F, K>::configure(meta, running_sum, table_idx)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
|
@ -466,6 +469,7 @@ mod tests {
|
|||
num_words: 6,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
|
@ -493,10 +497,11 @@ mod tests {
|
|||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let running_sum = meta.advice_column();
|
||||
let constants = meta.fixed_column();
|
||||
let table_idx = meta.fixed_column();
|
||||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
LookupRangeCheckConfig::<F, K>::configure(meta, running_sum, constants, table_idx)
|
||||
LookupRangeCheckConfig::<F, K>::configure(meta, running_sum, table_idx)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
|
|
Loading…
Reference in New Issue