Combine constants fixed columns using assign_advice_from_constant.

Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
therealyingtong 2021-07-20 16:22:08 +08:00
parent a10aefc8c1
commit d80333799d
14 changed files with 211 additions and 316 deletions

View File

@ -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" }

View File

@ -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,35 +367,30 @@ 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
// 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 {
v_new - v_old
} else {
v_old - v_new
};
let sign = if is_negative {
-pallas::Base::one()
} else {
pallas::Base::one()
};
(magnitude, sign)
});
// 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 = {
let magnitude = if is_negative {
-signed_value
} else {
signed_value
};
assert!(magnitude < pallas::Scalar::from_u128(1 << 64));
pallas::Base::from_bytes(&magnitude.to_bytes()).unwrap()
};
let sign = if is_negative {
-pallas::Base::one()
} else {
pallas::Base::one()
};
(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(&params, &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(&params, &circuit).unwrap();
let pk = plonk::keygen_pk(&params, 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();
}
}

View File

@ -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(

View File

@ -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,
};

View File

@ -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

View File

@ -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"
)

View File

@ -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)
}

View File

@ -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,

View File

@ -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()

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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,15 +160,8 @@ 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)?;
}
for idx in 0..num_windows {
self.q_range_check.enable(region, offset + idx)?;
}
// Decompose base field element into K-bit words.
@ -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,

View File

@ -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(