From 903f9e816010f8114bcebebc910472f83846423e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 4 May 2022 02:55:42 +0000 Subject: [PATCH] Adjust APIs of NoteCommit circuit impl to separate gadget and chip The separation isn't quite complete, as we removed the `GateCells` abstraction, but it makes the outer APIs clearer. --- src/circuit.rs | 16 +++-- src/circuit/gadget.rs | 11 +++- src/circuit/note_commit.rs | 121 +++++++++++++++++++++++++------------ 3 files changed, 98 insertions(+), 50 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index 7aeb77f8..7e1faa65 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -22,7 +22,7 @@ use self::{ add_chip::{AddChip, AddConfig}, assign_free_advice, }, - note_commit::NoteCommitConfig, + note_commit::{NoteCommitChip, NoteCommitConfig}, }; use crate::{ constants::{ @@ -292,12 +292,12 @@ impl plonk::Circuit for Circuit { // Configuration to handle decomposition and canonicity checking // for NoteCommit_old. let old_note_commit_config = - NoteCommitConfig::configure(meta, advices, sinsemilla_config_1.clone()); + NoteCommitChip::configure(meta, advices, sinsemilla_config_1.clone()); // Configuration to handle decomposition and canonicity checking // for NoteCommit_new. let new_note_commit_config = - NoteCommitConfig::configure(meta, advices, sinsemilla_config_2.clone()); + NoteCommitChip::configure(meta, advices, sinsemilla_config_2.clone()); Config { primary, @@ -530,17 +530,16 @@ impl plonk::Circuit for Circuit { // Old note commitment integrity. { - let old_note_commit_config = config.old_note_commit_config.clone(); - let rcm_old = self.rcm_old.as_ref().map(|rcm_old| rcm_old.inner()); // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) - let derived_cm_old = old_note_commit_config.assign_region( + let derived_cm_old = gadget::note_commit( layouter.namespace(|| { "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" }), config.sinsemilla_chip_1(), config.ecc_chip(), + config.note_commit_chip_old(), g_d_old.inner(), pk_d_old.inner(), v_old.clone(), @@ -555,8 +554,6 @@ impl plonk::Circuit for Circuit { // New note commitment integrity. { - let new_note_commit_config = config.new_note_commit_config.clone(); - // Witness g_d_new let g_d_new = { let g_d_new = self.g_d_new.map(|g_d_new| g_d_new.to_affine()); @@ -590,12 +587,13 @@ impl plonk::Circuit for Circuit { let rcm_new = self.rcm_new.as_ref().map(|rcm_new| rcm_new.inner()); // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) - let cm_new = new_note_commit_config.assign_region( + let cm_new = gadget::note_commit( layouter.namespace(|| { "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" }), config.sinsemilla_chip_2(), config.ecc_chip(), + config.note_commit_chip_new(), g_d_new.inner(), pk_d_new.inner(), v_new.clone(), diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index fb471a94..f35e431e 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -3,7 +3,7 @@ use ff::Field; use pasta_curves::pallas; -use super::commit_ivk::CommitIvkChip; +use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip}; use crate::constants::{ NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, ValueCommitV, @@ -64,6 +64,14 @@ impl super::Config { pub(super) fn poseidon_chip(&self) -> PoseidonChip { PoseidonChip::construct(self.poseidon_config.clone()) } + + pub(super) fn note_commit_chip_new(&self) -> NoteCommitChip { + NoteCommitChip::construct(self.new_note_commit_config.clone()) + } + + pub(super) fn note_commit_chip_old(&self) -> NoteCommitChip { + NoteCommitChip::construct(self.old_note_commit_config.clone()) + } } /// An instruction set for adding two circuit words (field elements). @@ -200,3 +208,4 @@ pub(in crate::circuit) fn derive_nullifier< } pub(in crate::circuit) use crate::circuit::commit_ivk::gadgets::commit_ivk; +pub(in crate::circuit) use crate::circuit::note_commit::gadgets::note_commit; diff --git a/src/circuit/note_commit.rs b/src/circuit/note_commit.rs index 14848beb..9fa64df7 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/note_commit.rs @@ -1431,7 +1431,12 @@ pub struct NoteCommitConfig { SinsemillaConfig, } -impl NoteCommitConfig { +#[derive(Clone, Debug)] +pub struct NoteCommitChip { + config: NoteCommitConfig, +} + +impl NoteCommitChip { #[allow(non_snake_case)] #[allow(clippy::many_single_char_names)] pub(in crate::circuit) fn configure( @@ -1442,7 +1447,7 @@ impl NoteCommitConfig { OrchardCommitDomains, OrchardFixedBases, >, - ) -> Self { + ) -> NoteCommitConfig { // Useful constants let two = pallas::Base::from(2); let two_pow_2 = pallas::Base::from(1 << 2); @@ -1536,7 +1541,7 @@ impl NoteCommitConfig { t_p, ); - Self { + NoteCommitConfig { b, d, e, @@ -1553,14 +1558,24 @@ impl NoteCommitConfig { } } + pub(in crate::circuit) fn construct(config: NoteCommitConfig) -> Self { + Self { config } + } +} + +pub(in crate::circuit) mod gadgets { + use halo2_proofs::circuit::Chip; + + use super::*; + #[allow(clippy::many_single_char_names)] #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] - pub(in crate::circuit) fn assign_region( - &self, + pub(in crate::circuit) fn note_commit( mut layouter: impl Layouter, chip: SinsemillaChip, ecc_chip: EccChip, + note_commit_chip: NoteCommitChip, g_d: &NonIdentityEccPoint, pk_d: &NonIdentityEccPoint, value: AssignedCell, @@ -1568,7 +1583,7 @@ impl NoteCommitConfig { psi: AssignedCell, rcm: Option, ) -> Result>, Error> { - let lookup_config = self.sinsemilla_config.lookup_config(); + let lookup_config = chip.config().lookup_config(); // `a` = bits 0..=249 of `x(g_d)` let a = MessagePiece::from_subpieces( @@ -1616,9 +1631,17 @@ impl NoteCommitConfig { DecomposeH::decompose(&lookup_config, chip.clone(), &mut layouter, &psi)?; // Check decomposition of `y(g_d)`. - let b_2 = self.y_canonicity(layouter.namespace(|| "y(g_d) decomposition"), g_d.y(), b_2)?; + let b_2 = y_canonicity( + &lookup_config, + ¬e_commit_chip.config.y_canon, + layouter.namespace(|| "y(g_d) decomposition"), + g_d.y(), + b_2, + )?; // Check decomposition of `y(pk_d)`. - let d_1 = self.y_canonicity( + let d_1 = y_canonicity( + &lookup_config, + ¬e_commit_chip.config.y_canon, layouter.namespace(|| "y(pk_d) decomposition"), pk_d.y(), d_1, @@ -1654,46 +1677,55 @@ impl NoteCommitConfig { let g_2 = z1_g.clone(); let z13_g = zs[6][13].clone(); - let (a_prime, z13_a_prime) = self.canon_bitshift_130( + let (a_prime, z13_a_prime) = canon_bitshift_130( + &lookup_config, layouter.namespace(|| "x(g_d) canonicity"), a.inner().cell_value(), )?; - let (b3_c_prime, z14_b3_c_prime) = self.pkd_x_canonicity( + let (b3_c_prime, z14_b3_c_prime) = pkd_x_canonicity( + &lookup_config, layouter.namespace(|| "x(pk_d) canonicity"), b_3.clone(), c.inner().cell_value(), )?; - let (e1_f_prime, z14_e1_f_prime) = self.rho_canonicity( + let (e1_f_prime, z14_e1_f_prime) = rho_canonicity( + &lookup_config, layouter.namespace(|| "rho canonicity"), e_1.clone(), f.inner().cell_value(), )?; - let (g1_g2_prime, z13_g1_g2_prime) = - self.psi_canonicity(layouter.namespace(|| "psi canonicity"), g_1.clone(), g_2)?; + let (g1_g2_prime, z13_g1_g2_prime) = psi_canonicity( + &lookup_config, + layouter.namespace(|| "psi canonicity"), + g_1.clone(), + g_2, + )?; - let b_1 = self + let cfg = note_commit_chip.config; + + let b_1 = cfg .b .assign(&mut layouter, b, b_0.clone(), b_1, b_2, b_3.clone())?; - let d_0 = self + let d_0 = cfg .d .assign(&mut layouter, d, d_0, d_1, d_2.clone(), z1_d.clone())?; - self.e.assign(&mut layouter, e, e_0.clone(), e_1.clone())?; + cfg.e.assign(&mut layouter, e, e_0.clone(), e_1.clone())?; - let g_0 = self + let g_0 = cfg .g .assign(&mut layouter, g, g_0, g_1.clone(), z1_g.clone())?; - let h_1 = self.h.assign(&mut layouter, h, h_0.clone(), h_1)?; + let h_1 = cfg.h.assign(&mut layouter, h, h_0.clone(), h_1)?; - self.g_d + cfg.g_d .assign(&mut layouter, g_d, a, b_0, b_1, a_prime, z13_a, z13_a_prime)?; - self.pk_d.assign( + cfg.pk_d.assign( &mut layouter, pk_d, b_3, @@ -1704,9 +1736,9 @@ impl NoteCommitConfig { z14_b3_c_prime, )?; - self.value.assign(&mut layouter, value, d_2, z1_d, e_0)?; + cfg.value.assign(&mut layouter, value, d_2, z1_d, e_0)?; - self.rho.assign( + cfg.rho.assign( &mut layouter, rho, e_1, @@ -1717,7 +1749,7 @@ impl NoteCommitConfig { z14_e1_f_prime, )?; - self.psi.assign( + cfg.psi.assign( &mut layouter, psi, g_1, @@ -1734,7 +1766,7 @@ impl NoteCommitConfig { // A canonicity check helper used in checking x(g_d), y(g_d), and y(pk_d). fn canon_bitshift_130( - &self, + lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, a: AssignedCell, ) -> Result { @@ -1752,7 +1784,7 @@ impl NoteCommitConfig { let t_p = pallas::Base::from_u128(T_P); a + two_pow_130 - t_p }); - let zs = self.sinsemilla_config.lookup_config().witness_check( + let zs = lookup_config.witness_check( layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"), a_prime, 13, @@ -1766,7 +1798,7 @@ impl NoteCommitConfig { // Check canonicity of `x(pk_d)` encoding fn pkd_x_canonicity( - &self, + lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, b_3: RangeConstrained>, c: AssignedCell, @@ -1791,7 +1823,7 @@ impl NoteCommitConfig { b_3 + (two_pow_4 * c) + two_pow_140 - t_p }); - let zs = self.sinsemilla_config.lookup_config().witness_check( + let zs = lookup_config.witness_check( layouter.namespace(|| "Decompose low 140 bits of (b_3 + 2^4 c + 2^140 - t_P)"), b3_c_prime, 14, @@ -1805,7 +1837,7 @@ impl NoteCommitConfig { // Check canonicity of `rho` encoding fn rho_canonicity( - &self, + lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, e_1: RangeConstrained>, f: AssignedCell, @@ -1830,7 +1862,7 @@ impl NoteCommitConfig { // Decompose the low 140 bits of e1_f_prime = e_1 + 2^4 f + 2^140 - t_P, // and output the running sum at the end of it. // If e1_f_prime < 2^140, the running sum will be 0. - let zs = self.sinsemilla_config.lookup_config().witness_check( + let zs = lookup_config.witness_check( layouter.namespace(|| "Decompose low 140 bits of (e_1 + 2^4 f + 2^140 - t_P)"), e1_f_prime, 14, @@ -1844,7 +1876,7 @@ impl NoteCommitConfig { // Check canonicity of `psi` encoding fn psi_canonicity( - &self, + lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, g_1: RangeConstrained>, g_2: AssignedCell, @@ -1867,7 +1899,7 @@ impl NoteCommitConfig { g_1 + (two_pow_9 * g_2) + two_pow_130 - t_p }); - let zs = self.sinsemilla_config.lookup_config().witness_check( + let zs = lookup_config.witness_check( layouter.namespace(|| "Decompose low 130 bits of (g_1 + (2^9)g_2 + 2^130 - t_P)"), g1_g2_prime, 13, @@ -1882,7 +1914,8 @@ impl NoteCommitConfig { // Check canonicity of y-coordinate given its LSB as a value. // Also, witness the LSB and return the witnessed cell. fn y_canonicity( - &self, + lookup_config: &LookupRangeCheckConfig, + y_canon: &YCanonicity, mut layouter: impl Layouter, y: AssignedCell, lsb: RangeConstrained>, @@ -1894,7 +1927,7 @@ impl NoteCommitConfig { // Range-constrain k_0 to be 9 bits. let k_0 = RangeConstrained::witness_short( - &self.sinsemilla_config.lookup_config(), + lookup_config, layouter.namespace(|| "k_0"), y.value(), 1..10, @@ -1905,7 +1938,7 @@ impl NoteCommitConfig { // Range-constrain k_2 to be 4 bits. let k_2 = RangeConstrained::witness_short( - &self.sinsemilla_config.lookup_config(), + lookup_config, layouter.namespace(|| "k_2"), y.value(), 250..254, @@ -1926,7 +1959,7 @@ impl NoteCommitConfig { let two_pow_10 = pallas::Base::from(1 << 10); lsb + two * k_0 + two_pow_10 * k_1 }); - let zs = self.sinsemilla_config.lookup_config().witness_check( + let zs = lookup_config.witness_check( layouter.namespace(|| "Decompose j = LSB + (2)k_0 + (2^10)k_1"), j, 25, @@ -1937,12 +1970,13 @@ impl NoteCommitConfig { // Decompose j_prime = j + 2^130 - t_P using 13 ten-bit lookups. // We can reuse the canon_bitshift_130 logic here. - let (j_prime, z13_j_prime) = self.canon_bitshift_130( + let (j_prime, z13_j_prime) = canon_bitshift_130( + lookup_config, layouter.namespace(|| "j_prime = j + 2^130 - t_P"), j.clone(), )?; - self.y_canon.assign( + y_canon.assign( &mut layouter, y, lsb, @@ -1964,7 +1998,10 @@ mod tests { use super::NoteCommitConfig; use crate::{ - circuit::gadget::assign_free_advice, + circuit::{ + gadget::assign_free_advice, + note_commit::{gadgets, NoteCommitChip}, + }, constants::{ fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, L_ORCHARD_BASE, L_VALUE, T_Q, @@ -2068,7 +2105,7 @@ mod tests { range_check, ); let note_commit_config = - NoteCommitConfig::configure(meta, advices, sinsemilla_config); + NoteCommitChip::configure(meta, advices, sinsemilla_config); let ecc_config = EccChip::::configure( meta, @@ -2101,6 +2138,9 @@ mod tests { // Construct an ECC chip let ecc_chip = EccChip::construct(ecc_config); + // Construct a NoteCommit chip + let note_commit_chip = NoteCommitChip::construct(note_commit_config.clone()); + // Witness g_d let g_d = { let g_d = self.gd_x.zip(self.gd_y_lsb).map(|(x, y_lsb)| { @@ -2167,10 +2207,11 @@ mod tests { let rcm = pallas::Scalar::random(OsRng); - let cm = note_commit_config.assign_region( + let cm = gadgets::note_commit( layouter.namespace(|| "Hash NoteCommit pieces"), sinsemilla_chip, ecc_chip.clone(), + note_commit_chip, g_d.inner(), pk_d.inner(), value_var,