diff --git a/Cargo.toml b/Cargo.toml index 51040eed..2662fca4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/src/circuit.rs b/src/circuit.rs index ced02210..da7d1833 100644 --- a/src/circuit.rs +++ b/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, + constants: Column, q_orchard: Selector, advices: [Column; 10], ecc_config: EccConfig, @@ -190,49 +190,22 @@ impl plonk::Circuit 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 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 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 for Circuit { Config { primary, + constants, q_orchard, advices, ecc_config, @@ -401,35 +367,30 @@ impl plonk::Circuit 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 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 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(); + } } diff --git a/src/circuit/gadget/ecc.rs b/src/circuit/gadget/ecc.rs index 99584c41..f1c35946 100644 --- a/src/circuit/gadget/ecc.rs +++ b/src/circuit/gadget/ecc.rs @@ -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( diff --git a/src/circuit/gadget/ecc/chip.rs b/src/circuit/gadget/ecc/chip.rs index 0b0113d8..5bf716b7 100644 --- a/src/circuit/gadget/ecc/chip.rs +++ b/src/circuit/gadget/ecc/chip.rs @@ -111,8 +111,6 @@ pub struct EccConfig { /// Witness point pub q_point: Selector, - /// Shared fixed column used for loading constants. - pub constants: Column, /// Lookup range check using 10-bit lookup table pub lookup_config: LookupRangeCheckConfig, /// Running sum decomposition. @@ -155,8 +153,6 @@ impl EccChip { meta: &mut ConstraintSystem, advices: [Column; 10], lookup_table: Column, - // TODO: Replace with public inputs API - constants: [Column; 2], ) -> >::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 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, }; diff --git a/src/circuit/gadget/ecc/chip/mul.rs b/src/circuit/gadget/ecc/chip/mul.rs index 57d99913..be384f37 100644 --- a/src/circuit/gadget/ecc/chip/mul.rs +++ b/src/circuit/gadget/ecc/chip/mul.rs @@ -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 = (INCOMPLETE_LEN / 2)..INCOMPLETE_LEN; const COMPLETE_RANGE: Range = 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, // 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 diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/short.rs b/src/circuit/gadget/ecc/chip/mul_fixed/short.rs index 924e1815..732cc238 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/short.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/short.rs @@ -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" ) diff --git a/src/circuit/gadget/sinsemilla.rs b/src/circuit/gadget/sinsemilla.rs index 726fae3a..0eafa9bd 100644 --- a/src/circuit/gadget/sinsemilla.rs +++ b/src/circuit/gadget/sinsemilla.rs @@ -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) } diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index 62ace816..749366f5 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -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, /// Configure each advice column to be able to perform lookup range checks. pub(super) lookup_config_0: LookupRangeCheckConfig, pub(super) lookup_config_1: LookupRangeCheckConfig, @@ -115,23 +111,15 @@ impl SinsemillaChip { meta: &mut ConstraintSystem, advices: [Column; 5], lookup: (Column, Column, Column), - constants: [Column; 6], // TODO: replace with public inputs API ) -> >::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, diff --git a/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs b/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs index 26a6bda8..72670ecf 100644 --- a/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs +++ b/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs @@ -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, Y) = { // 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> = 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 = message .iter() diff --git a/src/circuit/gadget/sinsemilla/commit_ivk.rs b/src/circuit/gadget/sinsemilla/commit_ivk.rs index f3d125c5..8f439859 100644 --- a/src/circuit/gadget/sinsemilla/commit_ivk.rs +++ b/src/circuit/gadget/sinsemilla/commit_ivk.rs @@ -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) } diff --git a/src/circuit/gadget/sinsemilla/merkle.rs b/src/circuit/gadget/sinsemilla/merkle.rs index 0eac0d31..4c1341d7 100644 --- a/src/circuit/gadget/sinsemilla/merkle.rs +++ b/src/circuit/gadget/sinsemilla/merkle.rs @@ -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) diff --git a/src/circuit/gadget/sinsemilla/note_commit.rs b/src/circuit/gadget/sinsemilla/note_commit.rs index 8cbf5741..fc487957 100644 --- a/src/circuit/gadget/sinsemilla/note_commit.rs +++ b/src/circuit/gadget/sinsemilla/note_commit.rs @@ -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) } diff --git a/src/circuit/gadget/utilities/decompose_running_sum.rs b/src/circuit/gadget/utilities/decompose_running_sum.rs index 45627dd7..8892bf0d 100644 --- a/src/circuit/gadget/utilities/decompose_running_sum.rs +++ b/src/circuit/gadget/utilities/decompose_running_sum.rs @@ -46,7 +46,6 @@ impl std::ops::Deref for RunningSum { #[derive(Debug, Clone, Eq, PartialEq)] pub struct RunningSumConfig { q_range_check: Selector, - q_strict: Selector, pub z: Column, _marker: PhantomData, } @@ -74,7 +73,6 @@ impl let config = Self { q_range_check, - q_strict: meta.selector(), z, _marker: PhantomData, }; @@ -90,13 +88,6 @@ impl 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 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 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 #[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) -> Self::Config { let z = meta.advice_column(); let q_range_check = meta.selector(); + let constants = meta.fixed_column(); + meta.enable_constant(constants); RunningSumConfig::::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, diff --git a/src/circuit/gadget/utilities/lookup_range_check.rs b/src/circuit/gadget/utilities/lookup_range_check.rs index 8008c529..3546ee78 100644 --- a/src/circuit/gadget/utilities/lookup_range_check.rs +++ b/src/circuit/gadget/utilities/lookup_range_check.rs @@ -19,7 +19,6 @@ pub struct LookupRangeCheckConfig pub q_lookup_short: Selector, pub short_lookup_bitshift: Column, pub running_sum: Column, - constants: Column, table_idx: Column, _marker: PhantomData, } @@ -39,11 +38,9 @@ impl LookupRangeCheckConfig pub fn configure( meta: &mut ConstraintSystem, running_sum: Column, - constants: Column, table_idx: Column, ) -> 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 LookupRangeCheckConfig q_lookup_short, short_lookup_bitshift, running_sum, - constants, table_idx, _marker: PhantomData, }; @@ -135,7 +131,14 @@ impl LookupRangeCheckConfig // 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 LookupRangeCheckConfig num_words: usize, strict: bool, ) -> Result<(CellValue, Vec>), Error> { - layouter.assign_region( + let (z_0, zs) = layouter.assign_region( || "Witness element", |mut region| { let z_0 = { @@ -161,11 +164,18 @@ impl LookupRangeCheckConfig 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 LookupRangeCheckConfig region: &mut Region<'_, F>, element: CellValue, num_words: usize, - strict: bool, ) -> Result>, Error> { // `num_words` must fit into a single field element. assert!(num_words * K <= F::CAPACITY as usize); @@ -243,13 +252,6 @@ impl LookupRangeCheckConfig 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) -> 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::::configure(meta, running_sum, constants, table_idx) + LookupRangeCheckConfig::::configure(meta, running_sum, table_idx) } fn synthesize( @@ -466,6 +469,7 @@ mod tests { num_words: 6, _marker: PhantomData, }; + let prover = MockProver::::run(11, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); } @@ -493,10 +497,11 @@ mod tests { fn configure(meta: &mut ConstraintSystem) -> 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::::configure(meta, running_sum, constants, table_idx) + LookupRangeCheckConfig::::configure(meta, running_sum, table_idx) } fn synthesize(