diff --git a/Cargo.toml b/Cargo.toml index 12c30880..231bbe89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,8 +78,15 @@ harness = false name = "circuit" harness = false +[[bench]] +name = "poseidon" +harness = false + [profile.release] debug = true [profile.bench] debug = true + +[patch.crates-io] +halo2 = { git = "https://github.com/zcash/halo2.git", rev = "afd7bc5469674cd08eae1634225fd02706a36a4f" } diff --git a/benches/circuit.rs b/benches/circuit.rs index 8a1f2ce5..84ffa1e5 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -73,7 +73,7 @@ fn criterion_benchmark(c: &mut Criterion) { .unwrap() .apply_signatures(rng, [0; 32], &[]) .unwrap(); - assert_eq!(bundle.verify_proof(&vk), Ok(())); + assert!(bundle.verify_proof(&vk).is_ok()); group.bench_function(BenchmarkId::new("bundle", num_recipients), |b| { b.iter(|| bundle.authorization().proof().verify(&vk, &instances)); }); diff --git a/benches/poseidon.rs b/benches/poseidon.rs new file mode 100644 index 00000000..1d585448 --- /dev/null +++ b/benches/poseidon.rs @@ -0,0 +1,253 @@ +use ff::Field; +use halo2::{ + circuit::{Layouter, SimpleFloorPlanner}, + pasta::Fp, + plonk::{ + create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, + ConstraintSystem, Error, + }, + poly::commitment::Params, + transcript::{Blake2bRead, Blake2bWrite, Challenge255}, +}; +use pasta_curves::{pallas, vesta}; + +use orchard::{ + circuit::gadget::poseidon::{Hash, Pow5Chip, Pow5Config}, + primitives::poseidon::{self, ConstantLength, Spec}, +}; +use std::convert::TryInto; +use std::marker::PhantomData; + +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::rngs::OsRng; + +#[derive(Clone, Copy)] +struct HashCircuit +where + S: Spec + Clone + Copy, +{ + message: Option<[Fp; L]>, + // For the purpose of this test, witness the result. + // TODO: Move this into an instance column. + output: Option, + _spec: PhantomData, +} + +#[derive(Debug, Clone)] +struct MyConfig { + input: [Column; L], + poseidon_config: Pow5Config, +} + +impl Circuit + for HashCircuit +where + S: Spec + Copy + Clone, +{ + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self { + message: None, + output: None, + _spec: PhantomData, + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); + let partial_sbox = meta.advice_column(); + + let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + + meta.enable_constant(rc_b[0]); + + Self::Config { + input: state[..RATE].try_into().unwrap(), + poseidon_config: Pow5Chip::configure::( + meta, + state.try_into().unwrap(), + partial_sbox, + rc_a.try_into().unwrap(), + rc_b.try_into().unwrap(), + ), + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let chip = Pow5Chip::construct(config.poseidon_config.clone()); + + let message = layouter.assign_region( + || "load message", + |mut region| { + let message_word = |i: usize| { + let value = self.message.map(|message_vals| message_vals[i]); + region.assign_advice( + || format!("load message_{}", i), + config.input[i], + 0, + || value.ok_or(Error::Synthesis), + ) + }; + + let message: Result, Error> = (0..L).map(message_word).collect(); + Ok(message?.try_into().unwrap()) + }, + )?; + + let hasher = Hash::<_, _, S, ConstantLength, WIDTH, RATE>::init( + chip, + layouter.namespace(|| "init"), + )?; + let output = hasher.hash(layouter.namespace(|| "hash"), message)?; + + layouter.assign_region( + || "constrain output", + |mut region| { + let expected_var = region.assign_advice( + || "load output", + config.input[0], + 0, + || self.output.ok_or(Error::Synthesis), + )?; + region.constrain_equal(output.cell(), expected_var.cell()) + }, + ) + } +} + +#[derive(Debug, Clone, Copy)] +struct MySpec; + +impl Spec for MySpec<3, 2> { + fn full_rounds() -> usize { + 8 + } + + fn partial_rounds() -> usize { + 56 + } + + fn sbox(val: Fp) -> Fp { + val.pow_vartime(&[5]) + } + + fn secure_mds() -> usize { + 0 + } +} + +impl Spec for MySpec<9, 8> { + fn full_rounds() -> usize { + 8 + } + + fn partial_rounds() -> usize { + 56 + } + + fn sbox(val: Fp) -> Fp { + val.pow_vartime(&[5]) + } + + fn secure_mds() -> usize { + 0 + } +} + +impl Spec for MySpec<12, 11> { + fn full_rounds() -> usize { + 8 + } + + fn partial_rounds() -> usize { + 56 + } + + fn sbox(val: Fp) -> Fp { + val.pow_vartime(&[5]) + } + + fn secure_mds() -> usize { + 0 + } +} + +const K: u32 = 6; + +fn bench_poseidon( + name: &str, + c: &mut Criterion, +) where + S: Spec + Copy + Clone, +{ + // Initialize the polynomial commitment parameters + let params: Params = Params::new(K); + + let empty_circuit = HashCircuit:: { + message: None, + output: None, + _spec: PhantomData, + }; + + // Initialize the proving key + let vk = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail"); + let pk = keygen_pk(¶ms, vk, &empty_circuit).expect("keygen_pk should not fail"); + + let prover_name = name.to_string() + "-prover"; + let verifier_name = name.to_string() + "-verifier"; + + let rng = OsRng; + let message = (0..L) + .map(|_| pallas::Base::random(rng)) + .collect::>() + .try_into() + .unwrap(); + let output = poseidon::Hash::<_, S, ConstantLength, WIDTH, RATE>::init().hash(message); + + let circuit = HashCircuit:: { + message: Some(message), + output: Some(output), + _spec: PhantomData, + }; + + c.bench_function(&prover_name, |b| { + b.iter(|| { + // Create a proof + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof(¶ms, &pk, &[circuit], &[&[]], &mut transcript) + .expect("proof generation should not fail") + }) + }); + + // Create a proof + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof(¶ms, &pk, &[circuit], &[&[]], &mut transcript) + .expect("proof generation should not fail"); + let proof = transcript.finalize(); + + c.bench_function(&verifier_name, |b| { + b.iter(|| { + let msm = params.empty_msm(); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + let guard = verify_proof(¶ms, pk.get_vk(), msm, &[&[]], &mut transcript).unwrap(); + let msm = guard.clone().use_challenges(); + assert!(msm.eval()); + }); + }); +} + +fn criterion_benchmark(c: &mut Criterion) { + bench_poseidon::, 3, 2, 2>("WIDTH = 3, RATE = 2", c); + bench_poseidon::, 9, 8, 8>("WIDTH = 9, RATE = 8", c); + bench_poseidon::, 12, 11, 11>("WIDTH = 12, RATE = 11", c); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/benches/primitives.rs b/benches/primitives.rs index df6747b5..e5cadd8a 100644 --- a/benches/primitives.rs +++ b/benches/primitives.rs @@ -21,7 +21,9 @@ fn bench_primitives(c: &mut Criterion) { let message = [pallas::Base::random(rng), pallas::Base::random(rng)]; group.bench_function("2-to-1", |b| { - b.iter(|| poseidon::Hash::init(P128Pow5T3, ConstantLength).hash(message)) + b.iter(|| { + poseidon::Hash::<_, P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message) + }) }); } diff --git a/book/macros.txt b/book/macros.txt index af590a07..d6fc74f0 100644 --- a/book/macros.txt +++ b/book/macros.txt @@ -32,4 +32,5 @@ # Circuit constraint helper methods \BoolCheck:{\texttt{bool\_check}({#1})} +\RangeCheck:{\texttt{range\_check}({#1, #2})} \ShortLookupRangeCheck:{\texttt{short\_lookup\_range\_check}({#1})} diff --git a/book/src/design/circuit/gadgets/decomposition.md b/book/src/design/circuit/gadgets/decomposition.md index 5b8eddac..2a3febeb 100644 --- a/book/src/design/circuit/gadgets/decomposition.md +++ b/book/src/design/circuit/gadgets/decomposition.md @@ -73,4 +73,4 @@ $$q_\mathit{lookup} \cdot \left(q_\mathit{running} \cdot (z_i - 2^K \cdot z_{i+1 where $z_i$ and $\textsf{word}$ are the same cell (but distinguished here for clarity of usage). ## Short range decomposition -For a short range (for instance, $[0, \texttt{range})$ where $\texttt{range} \leq 8$), we can range-constrain each word using a degree-$\texttt{range}$ polynomial constraint instead of a lookup: $$\texttt{range\_check(word, range)} = \texttt{word} \cdot (1 - \texttt{word}) \cdots (\texttt{range} - 1 - \texttt{word}).$$ +For a short range (for instance, $[0, \texttt{range})$ where $\texttt{range} \leq 8$), we can range-constrain each word using a degree-$\texttt{range}$ polynomial constraint instead of a lookup: $$\RangeCheck{word}{range} = \texttt{word} \cdot (1 - \texttt{word}) \cdots (\texttt{range} - 1 - \texttt{word}).$$ diff --git a/book/src/design/circuit/gadgets/ecc/fixed-base-scalar-mul.md b/book/src/design/circuit/gadgets/ecc/fixed-base-scalar-mul.md index c5388a39..244a955c 100644 --- a/book/src/design/circuit/gadgets/ecc/fixed-base-scalar-mul.md +++ b/book/src/design/circuit/gadgets/ecc/fixed-base-scalar-mul.md @@ -28,10 +28,10 @@ $$ \begin{array}{|c|l|} \hline \text{Degree} & \text{Constraint} \\\hline -9 & q_\text{decompose-base-field} \cdot \texttt{range\_check}(\text{word}, 2^3) = 0 \\\hline +9 & q_\text{decompose-base-field} \cdot \RangeCheck{\text{word}}{2^3} = 0 \\\hline \end{array} $$ -where $\texttt{range\_check}(\text{word}, \texttt{range}) = \text{word} \cdot (1 - \text{word}) \cdots (\texttt{range} - 1 - \text{word}).$ +where $\RangeCheck{\text{word}}{\texttt{range}} = \text{word} \cdot (1 - \text{word}) \cdots (\texttt{range} - 1 - \text{word}).$ ### Base field element We support using a base field element as the scalar in fixed-base multiplication. This occurs, for example, in the scalar multiplication for the nullifier computation of the Action circuit $\mathsf{DeriveNullifier_{nk}} = \mathsf{Extract}_\mathbb{P}\left(\left[(\mathsf{PRF_{nk}^{nfOrchard}}(\rho) + \psi) \bmod{q_\mathbb{P}}\right]\mathcal{K}^\mathsf{Orchard} + \mathsf{cm}\right)$: here, the scalar $$\left[(\mathsf{PRF_{nk}^{nfOrchard}}(\rho) + \psi) \bmod{q_\mathbb{P}}\right]$$ is the result of a base field addition. @@ -47,8 +47,8 @@ $$ \begin{array}{|c|l|} \hline \text{Degree} & \text{Constraint} \\\hline -5 & q_\text{canon-base-field} \cdot \texttt{range\_check}(\alpha_1, 2^2) = 0 \\\hline -3 & q_\text{canon-base-field} \cdot \texttt{range\_check}(\alpha_2, 2^1) = 0 \\\hline +5 & q_\text{canon-base-field} \cdot \RangeCheck{\alpha_1}{2^2} = 0 \\\hline +3 & q_\text{canon-base-field} \cdot \RangeCheck{\alpha_2}{2^1} = 0 \\\hline 2 & q_\text{canon-base-field} \cdot \left(z_{84} - (\alpha_1 + \alpha_2 \cdot 2^2)\right) = 0 \\\hline \end{array} $$ @@ -102,10 +102,11 @@ $$ \begin{array}{|c|l|l|} \hline \text{Degree} & \text{Constraint} & \text{Comment} \\\hline -3 & q_\text{scalar-fixed-short} \cdot \left(k_{21} \cdot (1 - k_{21})\right) = 0 & \text{The last window must be a single bit.}\\\hline +3 & q_\text{scalar-fixed-short} \cdot \BoolCheck{k_{21}} = 0 & \text{The last window must be a single bit.}\\\hline 3 & q_\text{scalar-fixed-short} \cdot \left(s^2 - 1\right) = 0 &\text{The sign must be $1$ or $-1$.}\\\hline \end{array} $$ +where $\BoolCheck{x} = x \cdot (1 - x)$. ## Load fixed base Then, we precompute multiples of the fixed base $B$ for each window. This takes the form of a window table: $M[0..W)[0..8)$ such that: diff --git a/book/src/design/circuit/gadgets/ecc/var-base-scalar-mul.md b/book/src/design/circuit/gadgets/ecc/var-base-scalar-mul.md index 9d193db1..6f912d15 100644 --- a/book/src/design/circuit/gadgets/ecc/var-base-scalar-mul.md +++ b/book/src/design/circuit/gadgets/ecc/var-base-scalar-mul.md @@ -103,7 +103,7 @@ $\begin{array}{l} \text{Initialize } A_{254} = [2] T. \\ \\ \text{for } i \text{ from } 254 \text{ down to } 4: \\ -\hspace{1.5em} (\mathbf{k}_i)(\mathbf{k}_i-1) = 0 \\ +\hspace{1.5em} \BoolCheck{\mathbf{k}_i} = 0 \\ \hspace{1.5em} \mathbf{z}_{i} = 2\mathbf{z}_{i+1} + \mathbf{k}_{i} \\ \hspace{1.5em} x_{P,i} = x_T \\ \hspace{1.5em} y_{P,i} = (2 \mathbf{k}_i - 1) \cdot y_T \hspace{2em}\text{(conditionally negate)} \\ @@ -114,7 +114,8 @@ $\begin{array}{l} \hspace{1.5em} \lambda_{2,i} \cdot (x_{A,i} - x_{A,i-1}) = y_{A,i} + y_{A,i-1}, \\ \end{array}$ -where $x_{R,i} = (\lambda_{1,i}^2 - x_{A,i} - x_T).$ After substitution of $x_{P,i}, y_{P,i}, x_{R,i}, y_{A,i}$, and $y_{A,i-1}$, this becomes: +where $x_{R,i} = (\lambda_{1,i}^2 - x_{A,i} - x_T).$ The helper $\BoolCheck{x} = x \cdot (1 - x)$. +After substitution of $x_{P,i}, y_{P,i}, x_{R,i}, y_{A,i}$, and $y_{A,i-1}$, this becomes: $\begin{array}{l} \text{Initialize } A_{254} = [2] T. \\ @@ -122,7 +123,7 @@ $\begin{array}{l} \text{for } i \text{ from } 254 \text{ down to } 4: \\ \hspace{1.5em} \text{// let } \mathbf{k}_{i} = \mathbf{z}_{i} - 2\mathbf{z}_{i+1} \\ \hspace{1.5em} \text{// let } y_{A,i} = \frac{(\lambda_{1,i} + \lambda_{2,i}) \cdot (x_{A,i} - (\lambda_{1,i}^2 - x_{A,i} - x_T))}{2} \\[2ex] -\hspace{1.5em} (\mathbf{k}_i)(\mathbf{k}_i-1) = 0 \\ +\hspace{1.5em} \BoolCheck{\mathbf{k}_i} = 0 \\ \hspace{1.5em} \lambda_{1,i} \cdot (x_{A,i} - x_T) = y_{A,i} - (2 \mathbf{k}_i - 1) \cdot y_T \\ \hspace{1.5em} \lambda_{2,i}^2 = x_{A,i-1} + \lambda_{1,i}^2 - x_T \\[1ex] \hspace{1.5em} \begin{cases} @@ -138,7 +139,7 @@ The bits $\mathbf{k}_{3 \dots 1}$ are used in three further steps, using [comple $\begin{array}{l} \text{for } i \text{ from } 3 \text{ down to } 1: \\ \hspace{1.5em} \text{// let } \mathbf{k}_{i} = \mathbf{z}_{i} - 2\mathbf{z}_{i+1} \\[0.5ex] -\hspace{1.5em} (\mathbf{k}_i)(\mathbf{k}_i-1) = 0 \\ +\hspace{1.5em} \BoolCheck{\mathbf{k}_i} = 0 \\ \hspace{1.5em} (x_{A,i-1}, y_{A,i-1}) = \left((x_{A,i}, y_{A,i}) + (x_T, y_T)\right) + (x_{A,i}, y_{A,i}) \end{array}$ @@ -202,7 +203,7 @@ $$ \text{Degree} & \text{Constraint} \\\hline 2 & q_2 \cdot \left(x_{T,cur} - x_{T,next}\right) = 0 \\\hline 2 & q_2 \cdot \left(y_{T,cur} - y_{T,next}\right) = 0 \\\hline -3 & q_2 \cdot \mathbf{k}_i \cdot (\mathbf{k}_i - 1) = 0, \text{ where } \mathbf{k}_i = \mathbf{z}_{i} - 2\mathbf{z}_{i+1} \\\hline +3 & q_2 \cdot \BoolCheck{\mathbf{k}_i} = 0, \text{ where } \mathbf{k}_i = \mathbf{z}_{i} - 2\mathbf{z}_{i+1} \\\hline 4 & q_2 \cdot \left(\lambda_{1,i} \cdot (x_{A,i} - x_{T,i}) - y_{A,i} + (2\mathbf{k}_i - 1) \cdot y_{T,i}\right) = 0 \\\hline 3 & q_2 \cdot \left(\lambda_{2,i}^2 - x_{A,i-1} - \lambda_{1,i}^2 + x_{T,i}\right) = 0 \\\hline 3 & q_2 \cdot \left(\lambda_{2,i} \cdot (x_{A,i} - x_{A,i-1}) - y_{A,i} - y_{A,i-1}\right) = 0 \\\hline @@ -222,7 +223,7 @@ $$ \begin{array}{|c|l|} \hline \text{Degree} & \text{Constraint} \\\hline -3 & q_3 \cdot \mathbf{k}_i \cdot (\mathbf{k}_i - 1) = 0, \text{ where } \mathbf{k}_i = \mathbf{z}_{i} - 2\mathbf{z}_{i+1} \\\hline +3 & q_3 \cdot \BoolCheck{\mathbf{k}_i} = 0, \text{ where } \mathbf{k}_i = \mathbf{z}_{i} - 2\mathbf{z}_{i+1} \\\hline 4 & q_3 \cdot \left(\lambda_{1,i} \cdot (x_{A,i} - x_{T,i}) - y_{A,i} + (2\mathbf{k}_i - 1) \cdot y_{T,i}\right) = 0 \\\hline 3 & q_3 \cdot \left(\lambda_{2,i}^2 - x_{A,i-1} - \lambda_{1,i}^2 + x_{T,i}\right) = 0 \\\hline 3 & q_3 \cdot \left(\lambda_{2,i} \cdot (x_{A,i} - x_{A,i-1}) - y_{A,i} - y_{A,i-1}^\text{witnessed}\right) = 0 \\\hline diff --git a/src/circuit.rs b/src/circuit.rs index 594a49f2..9d68fa7c 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -2,16 +2,13 @@ use group::{Curve, GroupEncoding}; use halo2::{ - circuit::{floor_planner, Layouter}, + circuit::{floor_planner, AssignedCell, Layouter}, plonk::{self, Advice, Column, Expression, Instance as InstanceColumn, Selector}, poly::Rotation, transcript::{Blake2bRead, Blake2bWrite}, }; use memuse::DynamicUsage; -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, vesta, -}; +use pasta_curves::{arithmetic::CurveAffine, pallas, vesta}; use crate::{ constants::{ @@ -40,10 +37,7 @@ use gadget::{ chip::{EccChip, EccConfig}, FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point, }, - poseidon::{ - Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig, - StateWord, Word, - }, + poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig}, sinsemilla::{ chip::{SinsemillaChip, SinsemillaConfig, SinsemillaHashDomains}, commit_ivk::CommitIvkConfig, @@ -53,14 +47,14 @@ use gadget::{ }, note_commit::NoteCommitConfig, }, - utilities::{copy, CellValue, UtilitiesInstructions, Var}, + utilities::UtilitiesInstructions, }; use std::convert::TryInto; use self::gadget::utilities::lookup_range_check::LookupRangeCheckConfig; -pub(crate) mod gadget; +pub mod gadget; /// Size of the Orchard circuit. const K: u32 = 11; @@ -85,7 +79,7 @@ pub struct Config { q_add: Selector, advices: [Column; 10], ecc_config: EccConfig, - poseidon_config: PoseidonConfig, + poseidon_config: PoseidonConfig, merkle_config_1: MerkleConfig, merkle_config_2: MerkleConfig, sinsemilla_config_1: SinsemillaConfig, @@ -120,7 +114,7 @@ pub struct Circuit { } impl UtilitiesInstructions for Circuit { - type Var = CellValue; + type Var = AssignedCell; } impl plonk::Circuit for Circuit { @@ -239,12 +233,11 @@ impl plonk::Circuit for Circuit { // Configuration for curve point operations. // This uses 10 advice columns and spans the whole circuit. - let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check.clone()); + let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check); // Configuration for the Poseidon hash. - let poseidon_config = PoseidonChip::configure( + let poseidon_config = PoseidonChip::configure::( meta, - poseidon::P128Pow5T3, // We place the state columns after the partial_sbox column so that the // pad-and-add region can be layed out more efficiently. advices[6..9].try_into().unwrap(), @@ -264,7 +257,7 @@ impl plonk::Circuit for Circuit { advices[6], lagrange_coeffs[0], lookup, - range_check.clone(), + range_check, ); let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone()); @@ -381,16 +374,14 @@ impl plonk::Circuit for Circuit { let v_old = self.load_private( layouter.namespace(|| "witness v_old"), config.advices[0], - self.v_old - .map(|v_old| pallas::Base::from_u64(v_old.inner())), + self.v_old.map(|v_old| pallas::Base::from(v_old.inner())), )?; // Witness v_new. let v_new = self.load_private( layouter.namespace(|| "witness v_new"), config.advices[0], - self.v_new - .map(|v_new| pallas::Base::from_u64(v_new.inner())), + self.v_new.map(|v_new| pallas::Base::from(v_new.inner())), )?; (psi_old, rho_old, cm_old, g_d_old, ak, nk, v_old, v_new) @@ -409,7 +400,7 @@ impl plonk::Circuit for Circuit { leaf_pos: self.pos, path, }; - let leaf = *cm_old.extract_p().inner(); + let leaf = cm_old.extract_p().inner().clone(); merkle_inputs.calculate_root(layouter.namespace(|| "MerkleCRH"), leaf)? }; @@ -419,12 +410,8 @@ impl plonk::Circuit for Circuit { let v_net = { // 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 v_old = self.v_old.map(|v_old| pallas::Base::from(v_old.inner())); + let v_new = self.v_new.map(|v_new| pallas::Base::from(v_new.inner())); let magnitude_sign = v_old.zip(v_new).map(|(v_old, v_new)| { let is_negative = v_old < v_new; @@ -458,7 +445,7 @@ impl plonk::Circuit for Circuit { let (commitment, _) = { let value_commit_v = ValueCommitV::get(); let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), value_commit_v); - value_commit_v.mul(layouter.namespace(|| "[v_net] ValueCommitV"), v_net)? + value_commit_v.mul(layouter.namespace(|| "[v_net] ValueCommitV"), v_net.clone())? }; // blind = [rcv] ValueCommitR @@ -485,40 +472,16 @@ impl plonk::Circuit for Circuit { let nf_old = { // hash_old = poseidon_hash(nk, rho_old) let hash_old = { - let message = [nk, rho_old]; - - let poseidon_message = layouter.assign_region( - || "load message", - |mut region| { - let mut message_word = |i: usize| { - let value = message[i].value(); - let var = region.assign_advice( - || format!("load message_{}", i), - config.poseidon_config.state[i], - 0, - || value.ok_or(plonk::Error::SynthesisError), - )?; - region.constrain_equal(var, message[i].cell())?; - Ok(Word::<_, _, poseidon::P128Pow5T3, 3, 2>::from_inner( - StateWord::new(var, value), - )) - }; - - Ok([message_word(0)?, message_word(1)?]) - }, - )?; - - let poseidon_hasher = PoseidonHash::init( - config.poseidon_chip(), - layouter.namespace(|| "Poseidon init"), - ConstantLength::<2>, - )?; - let poseidon_output = poseidon_hasher.hash( + let poseidon_message = [nk.clone(), rho_old.clone()]; + let poseidon_hasher = + PoseidonHash::<_, _, poseidon::P128Pow5T3, ConstantLength<2>, 3, 2>::init( + config.poseidon_chip(), + layouter.namespace(|| "Poseidon init"), + )?; + poseidon_hasher.hash( layouter.namespace(|| "Poseidon hash (nk, rho_old)"), poseidon_message, - )?; - let poseidon_output: CellValue = poseidon_output.inner().into(); - poseidon_output + )? }; // Add hash output to psi. @@ -529,32 +492,19 @@ impl plonk::Circuit for Circuit { |mut region| { config.q_add.enable(&mut region, 0)?; - copy( - &mut region, - || "copy hash_old", - config.advices[7], - 0, - &hash_old, - )?; - copy( - &mut region, - || "copy psi_old", - config.advices[8], - 0, - &psi_old, - )?; + hash_old.copy_advice(|| "copy hash_old", &mut region, config.advices[7], 0)?; + psi_old.copy_advice(|| "copy psi_old", &mut region, config.advices[8], 0)?; let scalar_val = hash_old .value() .zip(psi_old.value()) .map(|(hash_old, psi_old)| hash_old + psi_old); - let cell = region.assign_advice( + region.assign_advice( || "poseidon_hash(nk, rho_old) + psi_old", config.advices[6], 0, - || scalar_val.ok_or(plonk::Error::SynthesisError), - )?; - Ok(CellValue::new(cell, scalar_val)) + || scalar_val.ok_or(plonk::Error::Synthesis), + ) }, )?; @@ -609,7 +559,7 @@ impl plonk::Circuit for Circuit { config.sinsemilla_chip_1(), ecc_chip.clone(), layouter.namespace(|| "CommitIvk"), - *ak.extract_p().inner(), + ak.extract_p().inner().clone(), nk, rivk, )? @@ -647,7 +597,7 @@ impl plonk::Circuit for Circuit { config.ecc_chip(), g_d_old.inner(), pk_d_old.inner(), - v_old, + v_old.clone(), rho_old, psi_old, rcm_old, @@ -703,8 +653,8 @@ impl plonk::Circuit for Circuit { config.ecc_chip(), g_d_new.inner(), pk_d_new.inner(), - v_new, - *nf_old.inner(), + v_new.clone(), + nf_old.inner().clone(), psi_new, rcm_new, )?; @@ -720,19 +670,13 @@ impl plonk::Circuit for Circuit { layouter.assign_region( || "v_old - v_new = magnitude * sign", |mut region| { - copy(&mut region, || "v_old", config.advices[0], 0, &v_old)?; - copy(&mut region, || "v_new", config.advices[1], 0, &v_new)?; - let (magnitude, sign) = v_net; - copy( - &mut region, - || "v_net magnitude", - config.advices[2], - 0, - &magnitude, - )?; - copy(&mut region, || "v_net sign", config.advices[3], 0, &sign)?; + v_old.copy_advice(|| "v_old", &mut region, config.advices[0], 0)?; + v_new.copy_advice(|| "v_new", &mut region, config.advices[1], 0)?; + let (magnitude, sign) = v_net.clone(); + magnitude.copy_advice(|| "v_net magnitude", &mut region, config.advices[2], 0)?; + sign.copy_advice(|| "v_net sign", &mut region, config.advices[3], 0)?; - copy(&mut region, || "anchor", config.advices[4], 0, &anchor)?; + anchor.copy_advice(|| "anchor", &mut region, config.advices[4], 0)?; region.assign_advice_from_instance( || "pub input anchor", config.primary, @@ -861,8 +805,8 @@ impl Instance { instance[RK_X] = *rk.x(); instance[RK_Y] = *rk.y(); instance[CMX] = self.cmx.inner(); - instance[ENABLE_SPEND] = vesta::Scalar::from_u64(self.enable_spend.into()); - instance[ENABLE_OUTPUT] = vesta::Scalar::from_u64(self.enable_output.into()); + instance[ENABLE_SPEND] = vesta::Scalar::from(u64::from(self.enable_spend)); + instance[ENABLE_OUTPUT] = vesta::Scalar::from(u64::from(self.enable_output)); [instance] } @@ -1094,7 +1038,7 @@ mod tests { halo2::dev::CircuitLayout::default() .show_labels(false) .view_height(0..(1 << 11)) - .render(K as usize, &circuit, &root) + .render(K, &circuit, &root) .unwrap(); } } diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 52c7723e..4bf51c93 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -1,13 +1,15 @@ +//! Gadgets used in the Orchard circuit. + use pasta_curves::pallas; use ecc::chip::EccChip; -use poseidon::Pow5T3Chip as PoseidonChip; +use poseidon::Pow5Chip as PoseidonChip; use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}; pub(crate) mod ecc; -pub(crate) mod poseidon; +pub mod poseidon; pub(crate) mod sinsemilla; -pub(crate) mod utilities; +pub mod utilities; impl super::Config { pub(super) fn ecc_chip(&self) -> EccChip { @@ -30,7 +32,7 @@ impl super::Config { MerkleChip::construct(self.merkle_config_2.clone()) } - pub(super) fn poseidon_chip(&self) -> PoseidonChip { + pub(super) fn poseidon_chip(&self) -> PoseidonChip { PoseidonChip::construct(self.poseidon_config.clone()) } } diff --git a/src/circuit/gadget/ecc.rs b/src/circuit/gadget/ecc.rs index 1764e500..e8aaeefd 100644 --- a/src/circuit/gadget/ecc.rs +++ b/src/circuit/gadget/ecc.rs @@ -508,7 +508,9 @@ mod tests { use super::chip::{EccChip, EccConfig}; use crate::circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig; - struct MyCircuit {} + struct MyCircuit { + test_errors: bool, + } #[allow(non_snake_case)] impl Circuit for MyCircuit { @@ -516,7 +518,7 @@ mod tests { type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { - MyCircuit {} + MyCircuit { test_errors: false } } fn configure(meta: &mut ConstraintSystem) -> Self::Config { @@ -634,6 +636,7 @@ mod tests { q_val, &q, &p_neg, + self.test_errors, )?; } @@ -678,7 +681,7 @@ mod tests { #[test] fn ecc_chip() { let k = 13; - let circuit = MyCircuit {}; + let circuit = MyCircuit { test_errors: true }; let prover = MockProver::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())) } @@ -692,7 +695,7 @@ mod tests { root.fill(&WHITE).unwrap(); let root = root.titled("Ecc Chip Layout", ("sans-serif", 60)).unwrap(); - let circuit = MyCircuit {}; + let circuit = MyCircuit { test_errors: false }; halo2::dev::CircuitLayout::default() .render(13, &circuit, &root) .unwrap(); diff --git a/src/circuit/gadget/ecc/chip.rs b/src/circuit/gadget/ecc/chip.rs index f9e18265..c911e224 100644 --- a/src/circuit/gadget/ecc/chip.rs +++ b/src/circuit/gadget/ecc/chip.rs @@ -1,21 +1,23 @@ use super::EccInstructions; use crate::{ circuit::gadget::utilities::{ - copy, decompose_running_sum::RunningSumConfig, lookup_range_check::LookupRangeCheckConfig, - CellValue, UtilitiesInstructions, Var, + lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions, }, constants::{self, NullifierK, OrchardFixedBasesFull, ValueCommitV}, primitives::sinsemilla, }; use arrayvec::ArrayVec; +use ff::Field; use group::prime::PrimeCurveAffine; use halo2::{ - circuit::{Chip, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Selector}, + circuit::{AssignedCell, Chip, Layouter}, + plonk::{Advice, Column, ConstraintSystem, Error, Fixed}, }; use pasta_curves::{arithmetic::CurveAffine, pallas}; +use std::convert::TryInto; + pub(super) mod add; pub(super) mod add_incomplete; pub(super) mod mul; @@ -25,12 +27,12 @@ pub(super) mod witness_point; /// A curve point represented in affine (x, y) coordinates, or the /// identity represented as (0, 0). /// Each coordinate is assigned to a cell. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct EccPoint { /// x-coordinate - x: CellValue, + x: AssignedCell, /// y-coordinate - y: CellValue, + y: AssignedCell, } impl EccPoint { @@ -39,8 +41,8 @@ impl EccPoint { /// This is an internal API that we only use where we know we have a valid curve point /// (specifically inside Sinsemilla). pub(in crate::circuit::gadget) fn from_coordinates_unchecked( - x: CellValue, - y: CellValue, + x: AssignedCell, + y: AssignedCell, ) -> Self { EccPoint { x, y } } @@ -49,10 +51,10 @@ impl EccPoint { pub fn point(&self) -> Option { match (self.x.value(), self.y.value()) { (Some(x), Some(y)) => { - if x == pallas::Base::zero() && y == pallas::Base::zero() { + if x.is_zero_vartime() && y.is_zero_vartime() { Some(pallas::Affine::identity()) } else { - Some(pallas::Affine::from_xy(x, y).unwrap()) + Some(pallas::Affine::from_xy(*x, *y).unwrap()) } } _ => None, @@ -60,29 +62,29 @@ impl EccPoint { } /// The cell containing the affine short-Weierstrass x-coordinate, /// or 0 for the zero point. - pub fn x(&self) -> CellValue { - self.x + pub fn x(&self) -> AssignedCell { + self.x.clone() } /// The cell containing the affine short-Weierstrass y-coordinate, /// or 0 for the zero point. - pub fn y(&self) -> CellValue { - self.y + pub fn y(&self) -> AssignedCell { + self.y.clone() } #[cfg(test)] fn is_identity(&self) -> Option { - self.x.value().map(|x| x == pallas::Base::zero()) + self.x.value().map(|x| x.is_zero_vartime()) } } /// A non-identity point represented in affine (x, y) coordinates. /// Each coordinate is assigned to a cell. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct NonIdentityEccPoint { /// x-coordinate - x: CellValue, + x: AssignedCell, /// y-coordinate - y: CellValue, + y: AssignedCell, } impl NonIdentityEccPoint { @@ -91,8 +93,8 @@ impl NonIdentityEccPoint { /// This is an internal API that we only use where we know we have a valid non-identity /// curve point (specifically inside Sinsemilla). pub(in crate::circuit::gadget) fn from_coordinates_unchecked( - x: CellValue, - y: CellValue, + x: AssignedCell, + y: AssignedCell, ) -> Self { NonIdentityEccPoint { x, y } } @@ -101,19 +103,19 @@ impl NonIdentityEccPoint { pub fn point(&self) -> Option { match (self.x.value(), self.y.value()) { (Some(x), Some(y)) => { - assert!(x != pallas::Base::zero() && y != pallas::Base::zero()); - Some(pallas::Affine::from_xy(x, y).unwrap()) + assert!(!x.is_zero_vartime() && !y.is_zero_vartime()); + Some(pallas::Affine::from_xy(*x, *y).unwrap()) } _ => None, } } /// The cell containing the affine short-Weierstrass x-coordinate. - pub fn x(&self) -> CellValue { - self.x + pub fn x(&self) -> AssignedCell { + self.x.clone() } /// The cell containing the affine short-Weierstrass y-coordinate. - pub fn y(&self) -> CellValue { - self.y + pub fn y(&self) -> AssignedCell { + self.y.clone() } } @@ -133,47 +135,27 @@ pub struct EccConfig { /// Advice columns needed by instructions in the ECC chip. pub advices: [Column; 10], - /// Coefficients of interpolation polynomials for x-coordinates (used in fixed-base scalar multiplication) - pub lagrange_coeffs: [Column; constants::H], - /// Fixed z such that y + z = u^2 some square, and -y + z is a non-square. (Used in fixed-base scalar multiplication) - pub fixed_z: Column, - /// Incomplete addition - pub q_add_incomplete: Selector, + add_incomplete: add_incomplete::Config, /// Complete addition - pub q_add: Selector, + add: add::Config, - /// Variable-base scalar multiplication (hi half) - pub q_mul_hi: (Selector, Selector, Selector), - /// Variable-base scalar multiplication (lo half) - pub q_mul_lo: (Selector, Selector, Selector), - /// Selector used to enforce boolean decomposition in variable-base scalar mul - pub q_mul_decompose_var: Selector, - /// Selector used to enforce switching logic on LSB in variable-base scalar mul - pub q_mul_lsb: Selector, - /// Variable-base scalar multiplication (overflow check) - pub q_mul_overflow: Selector, + /// Variable-base scalar multiplication + mul: mul::Config, /// Fixed-base full-width scalar multiplication - pub q_mul_fixed_full: Selector, + mul_fixed_full: mul_fixed::full_width::Config, /// Fixed-base signed short scalar multiplication - pub q_mul_fixed_short: Selector, - /// Canonicity checks on base field element used as scalar in fixed-base mul - pub q_mul_fixed_base_field: Selector, - /// Running sum decomposition of a scalar used in fixed-base mul. This is used - /// when the scalar is a signed short exponent or a base-field element. - pub q_mul_fixed_running_sum: Selector, + mul_fixed_short: mul_fixed::short::Config, + /// Fixed-base mul using a base field element as a scalar + mul_fixed_base_field: mul_fixed::base_field_elem::Config, - /// Witness point (can be identity) - pub q_point: Selector, - /// Witness non-identity point - pub q_point_non_id: Selector, + /// Witness point + witness_point: witness_point::Config, /// Lookup range check using 10-bit lookup table pub lookup_config: LookupRangeCheckConfig, - /// Running sum decomposition. - pub running_sum_config: RunningSumConfig, } /// A chip implementing EccInstructions @@ -196,7 +178,7 @@ impl Chip for EccChip { } impl UtilitiesInstructions for EccChip { - type Var = CellValue; + type Var = AssignedCell; } impl EccChip { @@ -214,113 +196,59 @@ impl EccChip { lagrange_coeffs: [Column; 8], range_check: LookupRangeCheckConfig, ) -> >::Config { - // The following columns need to be equality-enabled for their use in sub-configs: - // - // add::Config and add_incomplete::Config: - // - advices[0]: x_p, - // - advices[1]: y_p, - // - advices[2]: x_qr, - // - advices[3]: y_qr, - // - // mul_fixed::Config: - // - advices[4]: window - // - advices[5]: u - // - // mul_fixed::base_field_element::Config: - // - [advices[6], advices[7], advices[8]]: canon_advices - // - // mul::overflow::Config: - // - [advices[0], advices[1], advices[2]]: advices - // - // mul::incomplete::Config - // - advices[4]: lambda1 - // - advices[9]: z - // - // mul::complete::Config: - // - advices[9]: z_complete - // - // 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()); - } - - let q_mul_fixed_running_sum = meta.selector(); - let running_sum_config = - RunningSumConfig::configure(meta, q_mul_fixed_running_sum, advices[4]); - - let config = EccConfig { - advices, - lagrange_coeffs, - fixed_z: meta.fixed_column(), - q_add_incomplete: meta.selector(), - q_add: meta.selector(), - q_mul_hi: (meta.selector(), meta.selector(), meta.selector()), - q_mul_lo: (meta.selector(), meta.selector(), meta.selector()), - q_mul_decompose_var: meta.selector(), - q_mul_overflow: meta.selector(), - q_mul_lsb: meta.selector(), - q_mul_fixed_full: meta.selector(), - q_mul_fixed_short: meta.selector(), - q_mul_fixed_base_field: meta.selector(), - q_mul_fixed_running_sum, - q_point: meta.selector(), - q_point_non_id: meta.selector(), - lookup_config: range_check, - running_sum_config, - }; - // Create witness point gate - { - let config: witness_point::Config = (&config).into(); - config.create_gate(meta); - } - + let witness_point = witness_point::Config::configure(meta, advices[0], advices[1]); // Create incomplete point addition gate - { - let config: add_incomplete::Config = (&config).into(); - config.create_gate(meta); - } + let add_incomplete = + add_incomplete::Config::configure(meta, advices[0], advices[1], advices[2], advices[3]); // Create complete point addition gate - { - let add_config: add::Config = (&config).into(); - add_config.create_gate(meta); - } + let add = add::Config::configure( + meta, advices[0], advices[1], advices[2], advices[3], advices[4], advices[5], + advices[6], advices[7], advices[8], + ); // Create variable-base scalar mul gates - { - let mul_config: mul::Config = (&config).into(); - mul_config.create_gate(meta); - } + let mul = mul::Config::configure(meta, add, range_check, advices); - // Create gate that is used both in fixed-base mul using a short signed exponent, - // and fixed-base mul using a base field element. - { - // The const generic does not matter when creating gates. - let mul_fixed_config: mul_fixed::Config<{ constants::NUM_WINDOWS }> = (&config).into(); - mul_fixed_config.running_sum_coords_gate(meta); - } + // Create config that is shared across short, base-field, and full-width + // fixed-base scalar mul. + let mul_fixed = mul_fixed::Config::configure( + meta, + lagrange_coeffs, + advices[4], + advices[0], + advices[1], + advices[5], + add, + add_incomplete, + ); // Create gate that is only used in full-width fixed-base scalar mul. - { - let mul_fixed_full_config: mul_fixed::full_width::Config = (&config).into(); - mul_fixed_full_config.create_gate(meta); - } + let mul_fixed_full = mul_fixed::full_width::Config::configure(meta, mul_fixed); // Create gate that is only used in short fixed-base scalar mul. - { - let short_config: mul_fixed::short::Config = (&config).into(); - short_config.create_gate(meta); - } + let mul_fixed_short = mul_fixed::short::Config::configure(meta, mul_fixed); // Create gate that is only used in fixed-base mul using a base field element. - { - let base_field_config: mul_fixed::base_field_elem::Config = (&config).into(); - base_field_config.create_gate(meta); - } + let mul_fixed_base_field = mul_fixed::base_field_elem::Config::configure( + meta, + advices[6..9].try_into().unwrap(), + range_check, + mul_fixed, + ); - config + EccConfig { + advices, + add_incomplete, + add, + mul, + mul_fixed_full, + mul_fixed_short, + mul_fixed_base_field, + witness_point, + lookup_config: range_check, + } } } @@ -332,9 +260,15 @@ impl EccChip { #[derive(Clone, Debug)] pub struct EccScalarFixed { value: Option, - windows: ArrayVec, { constants::NUM_WINDOWS }>, + windows: ArrayVec, { constants::NUM_WINDOWS }>, } +// TODO: Make V a `u64` +type MagnitudeCell = AssignedCell; +// TODO: Make V an enum Sign { Positive, Negative } +type SignCell = AssignedCell; +type MagnitudeSign = (MagnitudeCell, SignCell); + /// A signed short scalar used for fixed-base scalar multiplication. /// A short scalar must have magnitude in the range [0..2^64), with /// a sign of either 1 or -1. @@ -349,9 +283,10 @@ pub struct EccScalarFixed { /// k_21 must be a single bit, i.e. 0 or 1. #[derive(Clone, Debug)] pub struct EccScalarFixedShort { - magnitude: CellValue, - sign: CellValue, - running_sum: ArrayVec, { constants::NUM_WINDOWS_SHORT + 1 }>, + magnitude: MagnitudeCell, + sign: SignCell, + running_sum: + ArrayVec, { constants::NUM_WINDOWS_SHORT + 1 }>, } /// A base field element used for fixed-base scalar multiplication. @@ -365,23 +300,23 @@ pub struct EccScalarFixedShort { /// `base_field_elem`. #[derive(Clone, Debug)] struct EccBaseFieldElemFixed { - base_field_elem: CellValue, - running_sum: ArrayVec, { constants::NUM_WINDOWS + 1 }>, + base_field_elem: AssignedCell, + running_sum: ArrayVec, { constants::NUM_WINDOWS + 1 }>, } impl EccBaseFieldElemFixed { - fn base_field_elem(&self) -> CellValue { - self.base_field_elem + fn base_field_elem(&self) -> AssignedCell { + self.base_field_elem.clone() } } impl EccInstructions for EccChip { type ScalarFixed = EccScalarFixed; type ScalarFixedShort = EccScalarFixedShort; - type ScalarVar = CellValue; + type ScalarVar = AssignedCell; type Point = EccPoint; type NonIdentityPoint = NonIdentityEccPoint; - type X = CellValue; + type X = AssignedCell; type FixedPoints = OrchardFixedBasesFull; type FixedPointsBaseField = NullifierK; type FixedPointsShort = ValueCommitV; @@ -408,7 +343,7 @@ impl EccInstructions for EccChip { layouter: &mut impl Layouter, value: Option, ) -> Result { - let config: witness_point::Config = self.config().into(); + let config = self.config().witness_point; layouter.assign_region( || "witness point", |mut region| config.point(value, 0, &mut region), @@ -420,7 +355,7 @@ impl EccInstructions for EccChip { layouter: &mut impl Layouter, value: Option, ) -> Result { - let config: witness_point::Config = self.config().into(); + let config = self.config().witness_point; layouter.assign_region( || "witness non-identity point", |mut region| config.point_non_id(value, 0, &mut region), @@ -438,7 +373,7 @@ impl EccInstructions for EccChip { a: &Self::NonIdentityPoint, b: &Self::NonIdentityPoint, ) -> Result { - let config: add_incomplete::Config = self.config().into(); + let config = self.config().add_incomplete; layouter.assign_region( || "incomplete point addition", |mut region| config.assign_region(a, b, 0, &mut region), @@ -451,7 +386,7 @@ impl EccInstructions for EccChip { a: &A, b: &B, ) -> Result { - let config: add::Config = self.config().into(); + let config = self.config().add; layouter.assign_region( || "complete point addition", |mut region| { @@ -466,10 +401,10 @@ impl EccInstructions for EccChip { scalar: &Self::Var, base: &Self::NonIdentityPoint, ) -> Result<(Self::Point, Self::ScalarVar), Error> { - let config: mul::Config = self.config().into(); + let config = self.config().mul; config.assign( layouter.namespace(|| "variable-base scalar mul"), - *scalar, + scalar.clone(), base, ) } @@ -480,7 +415,7 @@ impl EccInstructions for EccChip { scalar: Option, base: &Self::FixedPoints, ) -> Result<(Self::Point, Self::ScalarFixed), Error> { - let config: mul_fixed::full_width::Config = self.config().into(); + let config = self.config().mul_fixed_full; config.assign( layouter.namespace(|| format!("fixed-base mul of {:?}", base)), scalar, @@ -491,10 +426,10 @@ impl EccInstructions for EccChip { fn mul_fixed_short( &self, layouter: &mut impl Layouter, - magnitude_sign: (CellValue, CellValue), + magnitude_sign: MagnitudeSign, base: &Self::FixedPointsShort, ) -> Result<(Self::Point, Self::ScalarFixedShort), Error> { - let config: mul_fixed::short::Config = self.config().into(); + let config: mul_fixed::short::Config = self.config().mul_fixed_short; config.assign( layouter.namespace(|| format!("short fixed-base mul of {:?}", base)), magnitude_sign, @@ -505,10 +440,10 @@ impl EccInstructions for EccChip { fn mul_fixed_base_field_elem( &self, layouter: &mut impl Layouter, - base_field_elem: CellValue, + base_field_elem: AssignedCell, base: &Self::FixedPointsBaseField, ) -> Result { - let config: mul_fixed::base_field_elem::Config = self.config().into(); + let config = self.config().mul_fixed_base_field; config.assign( layouter.namespace(|| format!("base-field elem fixed-base mul of {:?}", base)), base_field_elem, diff --git a/src/circuit/gadget/ecc/chip/add.rs b/src/circuit/gadget/ecc/chip/add.rs index 34520ed8..489c10e9 100644 --- a/src/circuit/gadget/ecc/chip/add.rs +++ b/src/circuit/gadget/ecc/chip/add.rs @@ -1,9 +1,8 @@ use std::array; -use super::{copy, CellValue, EccConfig, EccPoint, Var}; -use ff::Field; +use super::EccPoint; +use ff::{BatchInvert, Field}; use halo2::{ - arithmetic::BatchInvert, circuit::Region, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, poly::Rotation, @@ -11,7 +10,7 @@ use halo2::{ use pasta_curves::{arithmetic::FieldExt, pallas}; use std::collections::HashSet; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Config { q_add: Selector, // lambda @@ -34,24 +33,43 @@ pub struct Config { delta: Column, } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { - Self { - q_add: ecc_config.q_add, - x_p: ecc_config.advices[0], - y_p: ecc_config.advices[1], - x_qr: ecc_config.advices[2], - y_qr: ecc_config.advices[3], - lambda: ecc_config.advices[4], - alpha: ecc_config.advices[5], - beta: ecc_config.advices[6], - gamma: ecc_config.advices[7], - delta: ecc_config.advices[8], - } - } -} - impl Config { + #[allow(clippy::too_many_arguments)] + pub(super) fn configure( + meta: &mut ConstraintSystem, + x_p: Column, + y_p: Column, + x_qr: Column, + y_qr: Column, + lambda: Column, + alpha: Column, + beta: Column, + gamma: Column, + delta: Column, + ) -> Self { + meta.enable_equality(x_p.into()); + meta.enable_equality(y_p.into()); + meta.enable_equality(x_qr.into()); + meta.enable_equality(y_qr.into()); + + let config = Self { + q_add: meta.selector(), + x_p, + y_p, + x_qr, + y_qr, + lambda, + alpha, + beta, + gamma, + delta, + }; + + config.create_gate(meta); + + config + } + pub(crate) fn advice_columns(&self) -> HashSet> { core::array::IntoIter::new([ self.x_p, @@ -71,7 +89,7 @@ impl Config { core::array::IntoIter::new([self.x_qr, self.y_qr]).collect() } - pub(crate) fn create_gate(&self, meta: &mut ConstraintSystem) { + fn create_gate(&self, meta: &mut ConstraintSystem) { meta.create_gate("complete addition gates", |meta| { let q_add = meta.query_selector(self.q_add); let x_p = meta.query_advice(self.x_p, Rotation::cur()); @@ -103,8 +121,8 @@ impl Config { // Useful constants let one = Expression::Constant(pallas::Base::one()); - let two = Expression::Constant(pallas::Base::from_u64(2)); - let three = Expression::Constant(pallas::Base::from_u64(3)); + let two = Expression::Constant(pallas::Base::from(2)); + let three = Expression::Constant(pallas::Base::from(3)); // (x_q − x_p)⋅((x_q − x_p)⋅λ − (y_q−y_p)) = 0 let poly1 = { @@ -205,12 +223,12 @@ impl Config { self.q_add.enable(region, offset)?; // Copy point `p` into `x_p`, `y_p` columns - copy(region, || "x_p", self.x_p, offset, &p.x)?; - copy(region, || "y_p", self.y_p, offset, &p.y)?; + p.x.copy_advice(|| "x_p", region, self.x_p, offset)?; + p.y.copy_advice(|| "y_p", region, self.y_p, offset)?; // Copy point `q` into `x_qr`, `y_qr` columns - copy(region, || "x_q", self.x_qr, offset, &q.x)?; - copy(region, || "y_q", self.y_qr, offset, &q.y)?; + q.x.copy_advice(|| "x_q", region, self.x_qr, offset)?; + q.y.copy_advice(|| "y_q", region, self.y_qr, offset)?; let (x_p, y_p) = (p.x.value(), p.y.value()); let (x_q, y_q) = (q.x.value(), q.y.value()); @@ -230,7 +248,7 @@ impl Config { let gamma = x_q; let delta = y_q + y_p; - let mut inverses = [alpha, beta, gamma, delta]; + let mut inverses = [alpha, *beta, *gamma, delta]; inverses.batch_invert(); inverses }); @@ -243,28 +261,13 @@ impl Config { }; // Assign α = inv0(x_q - x_p) - region.assign_advice( - || "α", - self.alpha, - offset, - || alpha.ok_or(Error::SynthesisError), - )?; + region.assign_advice(|| "α", self.alpha, offset, || alpha.ok_or(Error::Synthesis))?; // Assign β = inv0(x_p) - region.assign_advice( - || "β", - self.beta, - offset, - || beta.ok_or(Error::SynthesisError), - )?; + region.assign_advice(|| "β", self.beta, offset, || beta.ok_or(Error::Synthesis))?; // Assign γ = inv0(x_q) - region.assign_advice( - || "γ", - self.gamma, - offset, - || gamma.ok_or(Error::SynthesisError), - )?; + region.assign_advice(|| "γ", self.gamma, offset, || gamma.ok_or(Error::Synthesis))?; // Assign δ = inv0(y_q + y_p) if x_q = x_p, 0 otherwise region.assign_advice( @@ -272,11 +275,11 @@ impl Config { self.delta, offset, || { - let x_p = x_p.ok_or(Error::SynthesisError)?; - let x_q = x_q.ok_or(Error::SynthesisError)?; + let x_p = x_p.ok_or(Error::Synthesis)?; + let x_q = x_q.ok_or(Error::Synthesis)?; if x_q == x_p { - delta.ok_or(Error::SynthesisError) + delta.ok_or(Error::Synthesis) } else { Ok(pallas::Base::zero()) } @@ -297,9 +300,9 @@ impl Config { // know that x_q != x_p in this branch. (y_q - y_p) * alpha } else { - if y_p != pallas::Base::zero() { + if !y_p.is_zero_vartime() { // 3(x_p)^2 - let three_x_p_sq = pallas::Base::from_u64(3) * x_p.square(); + let three_x_p_sq = pallas::Base::from(3) * x_p.square(); // 1 / 2(y_p) let inv_two_y_p = y_p.invert().unwrap() * pallas::Base::TWO_INV; // λ = 3(x_p)^2 / 2(y_p) @@ -313,7 +316,7 @@ impl Config { || "λ", self.lambda, offset, - || lambda.ok_or(Error::SynthesisError), + || lambda.ok_or(Error::Synthesis), )?; // Calculate (x_r, y_r) @@ -324,13 +327,13 @@ impl Config { .zip(lambda) .map(|((((x_p, y_p), x_q), y_q), lambda)| { { - if x_p == pallas::Base::zero() { + if x_p.is_zero_vartime() { // 0 + Q = Q - (x_q, y_q) - } else if x_q == pallas::Base::zero() { + (*x_q, *y_q) + } else if x_q.is_zero_vartime() { // P + 0 = P - (x_p, y_p) - } else if (x_q == x_p) && (y_q == -y_p) { + (*x_p, *y_p) + } else if (x_q == x_p) && (*y_q == -y_p) { // P + (-P) maps to (0,0) (pallas::Base::zero(), pallas::Base::zero()) } else { @@ -349,7 +352,7 @@ impl Config { || "x_r", self.x_qr, offset + 1, - || x_r.ok_or(Error::SynthesisError), + || x_r.ok_or(Error::Synthesis), )?; // Assign y_r @@ -358,12 +361,12 @@ impl Config { || "y_r", self.y_qr, offset + 1, - || y_r.ok_or(Error::SynthesisError), + || y_r.ok_or(Error::Synthesis), )?; let result = EccPoint { - x: CellValue::::new(x_r_cell, x_r), - y: CellValue::::new(y_r_cell, y_r), + x: x_r_cell, + y: y_r_cell, }; #[cfg(test)] @@ -411,7 +414,9 @@ pub mod tests { // Check complete addition P + (-P) let zero = { let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?; - assert!(result.inner().is_identity().unwrap()); + if let Some(is_identity) = result.inner().is_identity() { + assert!(is_identity); + } result }; diff --git a/src/circuit/gadget/ecc/chip/add_incomplete.rs b/src/circuit/gadget/ecc/chip/add_incomplete.rs index 55495e1e..470563d6 100644 --- a/src/circuit/gadget/ecc/chip/add_incomplete.rs +++ b/src/circuit/gadget/ecc/chip/add_incomplete.rs @@ -1,6 +1,7 @@ use std::{array, collections::HashSet}; -use super::{copy, CellValue, EccConfig, NonIdentityEccPoint, Var}; +use super::NonIdentityEccPoint; +use ff::Field; use group::Curve; use halo2::{ circuit::Region, @@ -9,7 +10,7 @@ use halo2::{ }; use pasta_curves::{arithmetic::CurveAffine, pallas}; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Config { q_add_incomplete: Selector, // x-coordinate of P in P + Q = R @@ -22,24 +23,37 @@ pub struct Config { pub y_qr: Column, } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { - Self { - q_add_incomplete: ecc_config.q_add_incomplete, - x_p: ecc_config.advices[0], - y_p: ecc_config.advices[1], - x_qr: ecc_config.advices[2], - y_qr: ecc_config.advices[3], - } - } -} - impl Config { + pub(super) fn configure( + meta: &mut ConstraintSystem, + x_p: Column, + y_p: Column, + x_qr: Column, + y_qr: Column, + ) -> Self { + meta.enable_equality(x_p.into()); + meta.enable_equality(y_p.into()); + meta.enable_equality(x_qr.into()); + meta.enable_equality(y_qr.into()); + + let config = Self { + q_add_incomplete: meta.selector(), + x_p, + y_p, + x_qr, + y_qr, + }; + + config.create_gate(meta); + + config + } + pub(crate) fn advice_columns(&self) -> HashSet> { core::array::IntoIter::new([self.x_p, self.y_p, self.x_qr, self.y_qr]).collect() } - pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + fn create_gate(&self, meta: &mut ConstraintSystem) { meta.create_gate("incomplete addition gates", |meta| { let q_add_incomplete = meta.query_selector(self.q_add_incomplete); let x_p = meta.query_advice(self.x_p, Rotation::cur()); @@ -83,13 +97,13 @@ impl Config { .zip(y_q) .map(|(((x_p, y_p), x_q), y_q)| { // P is point at infinity - if (x_p == pallas::Base::zero() && y_p == pallas::Base::zero()) + if (x_p.is_zero_vartime() && y_p.is_zero_vartime()) // Q is point at infinity - || (x_q == pallas::Base::zero() && y_q == pallas::Base::zero()) + || (x_q.is_zero_vartime() && y_q.is_zero_vartime()) // x_p = x_q || (x_p == x_q) { - Err(Error::SynthesisError) + Err(Error::Synthesis) } else { Ok(()) } @@ -97,12 +111,12 @@ impl Config { .transpose()?; // Copy point `p` into `x_p`, `y_p` columns - copy(region, || "x_p", self.x_p, offset, &p.x)?; - copy(region, || "y_p", self.y_p, offset, &p.y)?; + p.x.copy_advice(|| "x_p", region, self.x_p, offset)?; + p.y.copy_advice(|| "y_p", region, self.y_p, offset)?; // Copy point `q` into `x_qr`, `y_qr` columns - copy(region, || "x_q", self.x_qr, offset, &q.x)?; - copy(region, || "y_q", self.y_qr, offset, &q.y)?; + q.x.copy_advice(|| "x_q", region, self.x_qr, offset)?; + q.y.copy_advice(|| "y_q", region, self.y_qr, offset)?; // Compute the sum `P + Q = R` let r = { @@ -123,7 +137,7 @@ impl Config { || "x_r", self.x_qr, offset + 1, - || x_r.ok_or(Error::SynthesisError), + || x_r.ok_or(Error::Synthesis), )?; let y_r = r.1; @@ -131,12 +145,12 @@ impl Config { || "y_r", self.y_qr, offset + 1, - || y_r.ok_or(Error::SynthesisError), + || y_r.ok_or(Error::Synthesis), )?; let result = NonIdentityEccPoint { - x: CellValue::::new(x_r_var, x_r), - y: CellValue::::new(y_r_var, y_r), + x: x_r_var, + y: y_r_var, }; Ok(result) @@ -162,6 +176,7 @@ pub mod tests { q_val: pallas::Affine, q: &NonIdentityPoint, p_neg: &NonIdentityPoint, + test_errors: bool, ) -> Result<(), Error> { // P + Q { @@ -174,13 +189,15 @@ pub mod tests { result.constrain_equal(layouter.namespace(|| "constrain P + Q"), &witnessed_result)?; } - // P + P should return an error - p.add_incomplete(layouter.namespace(|| "P + P"), p) - .expect_err("P + P should return an error"); + if test_errors { + // P + P should return an error + p.add_incomplete(layouter.namespace(|| "P + P"), p) + .expect_err("P + P should return an error"); - // P + (-P) should return an error - p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg) - .expect_err("P + (-P) should return an error"); + // P + (-P) should return an error + p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg) + .expect_err("P + (-P) should return an error"); + } Ok(()) } diff --git a/src/circuit/gadget/ecc/chip/mul.rs b/src/circuit/gadget/ecc/chip/mul.rs index e3db3bde..33746ac6 100644 --- a/src/circuit/gadget/ecc/chip/mul.rs +++ b/src/circuit/gadget/ecc/chip/mul.rs @@ -1,13 +1,20 @@ -use super::{add, CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var}; -use crate::{circuit::gadget::utilities::copy, constants::T_Q}; -use std::ops::{Deref, Range}; +use super::{add, EccPoint, NonIdentityEccPoint}; +use crate::{ + circuit::gadget::utilities::{bool_check, lookup_range_check::LookupRangeCheckConfig, ternary}, + constants::T_Q, + primitives::sinsemilla, +}; +use std::{ + convert::TryInto, + ops::{Deref, Range}, +}; use bigint::U256; use ff::PrimeField; use halo2::{ arithmetic::FieldExt, - circuit::{Layouter, Region}, - plonk::{ConstraintSystem, Error, Expression, Selector}, + circuit::{AssignedCell, Layouter, Region}, + plonk::{Advice, Column, ConstraintSystem, Error, Selector}, poly::Rotation, }; @@ -30,41 +37,61 @@ const INCOMPLETE_RANGE: Range = 0..INCOMPLETE_LEN; // (It is a coincidence that k_{130} matches the boundary of the // overflow check described in [the book](https://zcash.github.io/halo2/design/gadgets/ecc/var-base-scalar-mul.html#overflow-check).) const INCOMPLETE_HI_RANGE: Range = 0..(INCOMPLETE_LEN / 2); +const INCOMPLETE_HI_LEN: usize = INCOMPLETE_LEN / 2; // Bits k_{254} to k_{4} inclusive are used in incomplete addition. // The `lo` half is k_{129} to k_{4} inclusive (length 126 bits). -const INCOMPLETE_LO_RANGE: Range = (INCOMPLETE_LEN / 2)..INCOMPLETE_LEN; +const INCOMPLETE_LO_RANGE: Range = INCOMPLETE_HI_LEN..INCOMPLETE_LEN; +const INCOMPLETE_LO_LEN: usize = INCOMPLETE_LEN - INCOMPLETE_HI_LEN; // Bits k_{3} to k_{1} inclusive are used in complete addition. // Bit k_{0} is handled separately. const COMPLETE_RANGE: Range = INCOMPLETE_LEN..(INCOMPLETE_LEN + NUM_COMPLETE_BITS); +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Config { // Selector used to check switching logic on LSB q_mul_lsb: Selector, // Configuration used in complete addition add_config: add::Config, // Configuration used for `hi` bits of the scalar - hi_config: incomplete::HiConfig, + hi_config: incomplete::Config, // Configuration used for `lo` bits of the scalar - lo_config: incomplete::LoConfig, + lo_config: incomplete::Config, // Configuration used for complete addition part of double-and-add algorithm complete_config: complete::Config, // Configuration used to check for overflow overflow_config: overflow::Config, } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { +impl Config { + pub(super) fn configure( + meta: &mut ConstraintSystem, + add_config: add::Config, + lookup_config: LookupRangeCheckConfig, + advices: [Column; 10], + ) -> Self { + let hi_config = incomplete::Config::configure( + meta, advices[9], advices[3], advices[0], advices[1], advices[4], advices[5], + ); + let lo_config = incomplete::Config::configure( + meta, advices[6], advices[7], advices[0], advices[1], advices[8], advices[2], + ); + let complete_config = complete::Config::configure(meta, advices[9], add_config); + let overflow_config = + overflow::Config::configure(meta, lookup_config, advices[6..9].try_into().unwrap()); + let config = Self { - q_mul_lsb: ecc_config.q_mul_lsb, - add_config: ecc_config.into(), - hi_config: ecc_config.into(), - lo_config: ecc_config.into(), - complete_config: ecc_config.into(), - overflow_config: ecc_config.into(), + q_mul_lsb: meta.selector(), + add_config, + hi_config, + lo_config, + complete_config, + overflow_config, }; + config.create_gate(meta); + assert_eq!( config.hi_config.x_p, config.lo_config.x_p, "x_p is shared across hi and lo halves." @@ -78,23 +105,31 @@ impl From<&EccConfig> for Config { // z and lambda1 are assigned on the same row as the add_config output. // Therefore, z and lambda1 must not overlap with add_config.x_qr, add_config.y_qr. let add_config_outputs = config.add_config.output_columns(); - for config in [&(*config.hi_config), &(*config.lo_config)].iter() { + { assert!( - !add_config_outputs.contains(&config.z), + !add_config_outputs.contains(&config.hi_config.z), "incomplete config z cannot overlap with complete addition columns." ); assert!( - !add_config_outputs.contains(&config.lambda1), + !add_config_outputs.contains(&config.hi_config.lambda1), + "incomplete config lambda1 cannot overlap with complete addition columns." + ); + } + { + assert!( + !add_config_outputs.contains(&config.lo_config.z), + "incomplete config z cannot overlap with complete addition columns." + ); + assert!( + !add_config_outputs.contains(&config.lo_config.lambda1), "incomplete config lambda1 cannot overlap with complete addition columns." ); } config } -} -impl Config { - pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + fn create_gate(&self, meta: &mut ConstraintSystem) { // If `lsb` is 0, (x, y) = (x_p, -y_p). If `lsb` is 1, (x, y) = (0,0). meta.create_gate("LSB check", |meta| { let q_mul_lsb = meta.query_selector(self.q_mul_lsb); @@ -108,15 +143,14 @@ impl Config { // z_0 = 2 * z_1 + k_0 // => k_0 = z_0 - 2 * z_1 - let lsb = z_0 - z_1 * pallas::Base::from_u64(2); - let one_minus_lsb = Expression::Constant(pallas::Base::one()) - lsb.clone(); + let lsb = z_0 - z_1 * pallas::Base::from(2); - let bool_check = lsb.clone() * one_minus_lsb.clone(); + let bool_check = bool_check(lsb.clone()); // `lsb` = 0 => (x_p, y_p) = (x, -y) // `lsb` = 1 => (x_p, y_p) = (0,0) - let lsb_x = (lsb.clone() * x_p.clone()) + one_minus_lsb.clone() * (x_p - base_x); - let lsb_y = (lsb * y_p.clone()) + one_minus_lsb * (y_p + base_y); + let lsb_x = ternary(lsb.clone(), x_p.clone(), x_p - base_x); + let lsb_y = ternary(lsb, y_p.clone(), y_p + base_y); std::array::IntoIter::new([ ("bool_check", bool_check), @@ -125,26 +159,21 @@ impl Config { ]) .map(move |(name, poly)| (name, q_mul_lsb.clone() * poly)) }); - - self.hi_config.create_gate(meta); - self.lo_config.create_gate(meta); - self.complete_config.create_gate(meta); - self.overflow_config.create_gate(meta); } pub(super) fn assign( &self, mut layouter: impl Layouter, - alpha: CellValue, + alpha: AssignedCell, base: &NonIdentityEccPoint, - ) -> Result<(EccPoint, CellValue), Error> { + ) -> Result<(EccPoint, AssignedCell), Error> { let (result, zs): (EccPoint, Vec>) = layouter.assign_region( || "variable-base scalar mul", |mut region| { let offset = 0; // Case `base` into an `EccPoint` for later use. - let base_point: EccPoint = (*base).into(); + let base_point: EccPoint = base.clone().into(); // Decompose `k = alpha + t_q` bitwise (big-endian bit order). let bits = decompose_for_scalar_mul(alpha.value()); @@ -163,16 +192,12 @@ impl Config { let offset = offset + 1; // Initialize the running sum for scalar decomposition to zero - let z_init = { - let z_init_cell = region.assign_advice_from_constant( - || "z_init = 0", - self.hi_config.z, - offset, - pallas::Base::zero(), - )?; - - Z(CellValue::new(z_init_cell, Some(pallas::Base::zero()))) - }; + let z_init = Z(region.assign_advice_from_constant( + || "z_init = 0", + self.hi_config.z, + offset, + pallas::Base::zero(), + )?); // Double-and-add (incomplete addition) for the `hi` half of the scalar decomposition let (x_a, y_a, zs_incomplete_hi) = self.hi_config.double_and_add( @@ -180,7 +205,7 @@ impl Config { offset, base, bits_incomplete_hi, - (X(acc.x), Y(acc.y), z_init), + (X(acc.x), Y(acc.y), z_init.clone()), )?; // Double-and-add (incomplete addition) for the `lo` half of the scalar decomposition @@ -190,7 +215,7 @@ impl Config { offset, base, bits_incomplete_lo, - (x_a, y_a, *z), + (x_a, y_a, z.clone()), )?; // Move from incomplete addition to complete addition. @@ -214,7 +239,7 @@ impl Config { &base_point, x_a, y_a, - *z, + z.clone(), )? }; @@ -222,8 +247,8 @@ impl Config { let offset = offset + COMPLETE_RANGE.len() * 2; // Process the least significant bit - let z_1 = zs_complete.last().unwrap(); - let (result, z_0) = self.process_lsb(&mut region, offset, base, acc, *z_1, lsb)?; + let z_1 = zs_complete.last().unwrap().clone(); + let (result, z_0) = self.process_lsb(&mut region, offset, base, acc, z_1, lsb)?; #[cfg(test)] // Check that the correct multiple is obtained. @@ -233,7 +258,7 @@ impl Config { let base = base.point(); let alpha = alpha .value() - .map(|alpha| pallas::Scalar::from_bytes(&alpha.to_bytes()).unwrap()); + .map(|alpha| pallas::Scalar::from_repr(alpha.to_repr()).unwrap()); let real_mul = base.zip(alpha).map(|(base, alpha)| base * alpha); let result = result.point(); @@ -261,8 +286,11 @@ impl Config { }, )?; - self.overflow_config - .overflow_check(layouter.namespace(|| "overflow check"), alpha, &zs)?; + self.overflow_config.overflow_check( + layouter.namespace(|| "overflow check"), + alpha.clone(), + &zs, + )?; Ok((result, alpha)) } @@ -298,39 +326,29 @@ impl Config { // Assign z_0 = 2⋅z_1 + k_0 let z_0 = { let z_0_val = z_1.value().zip(lsb).map(|(z_1, lsb)| { - let lsb = pallas::Base::from_u64(lsb as u64); - z_1 * pallas::Base::from_u64(2) + lsb + let lsb = pallas::Base::from(lsb as u64); + z_1 * pallas::Base::from(2) + lsb }); let z_0_cell = region.assign_advice( || "z_0", self.complete_config.z_complete, offset + 1, - || z_0_val.ok_or(Error::SynthesisError), + || z_0_val.ok_or(Error::Synthesis), )?; - Z(CellValue::new(z_0_cell, z_0_val)) + Z(z_0_cell) }; // Copy in `base_x`, `base_y` to use in the LSB gate - copy( - region, - || "copy base_x", - self.add_config.x_p, - offset + 1, - &base.x(), - )?; - copy( - region, - || "copy base_y", - self.add_config.y_p, - offset + 1, - &base.y(), - )?; + base.x() + .copy_advice(|| "copy base_x", region, self.add_config.x_p, offset + 1)?; + base.y() + .copy_advice(|| "copy base_y", region, self.add_config.y_p, offset + 1)?; // If `lsb` is 0, return `Acc + (-P)`. If `lsb` is 1, simply return `Acc + 0`. let x = if let Some(lsb) = lsb { if !lsb { - base.x.value() + base.x.value().cloned() } else { Some(pallas::Base::zero()) } @@ -352,19 +370,19 @@ impl Config { || "x", self.add_config.x_p, offset, - || x.ok_or(Error::SynthesisError), + || x.ok_or(Error::Synthesis), )?; let y_cell = region.assign_advice( || "y", self.add_config.y_p, offset, - || y.ok_or(Error::SynthesisError), + || y.ok_or(Error::Synthesis), )?; let p = EccPoint { - x: CellValue::::new(x_cell, x), - y: CellValue::::new(y_cell, y), + x: x_cell, + y: y_cell, }; // Return the result of the final complete addition as `[scalar]B` @@ -376,44 +394,44 @@ impl Config { #[derive(Clone, Debug)] // `x`-coordinate of the accumulator. -struct X(CellValue); +struct X(AssignedCell); impl Deref for X { - type Target = CellValue; + type Target = AssignedCell; fn deref(&self) -> &Self::Target { &self.0 } } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] // `y`-coordinate of the accumulator. -struct Y(CellValue); +struct Y(AssignedCell); impl Deref for Y { - type Target = CellValue; + type Target = AssignedCell; fn deref(&self) -> &Self::Target { &self.0 } } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] // Cumulative sum `z` used to decompose the scalar. -struct Z(CellValue); +struct Z(AssignedCell); impl Deref for Z { - type Target = CellValue; + type Target = AssignedCell; fn deref(&self) -> &Self::Target { &self.0 } } -fn decompose_for_scalar_mul(scalar: Option) -> Vec> { +fn decompose_for_scalar_mul(scalar: Option<&pallas::Base>) -> Vec> { let bitstring = scalar.map(|scalar| { // We use `k = scalar + t_q` in the double-and-add algorithm, where // the scalar field `F_q = 2^254 + t_q`. // Note that the addition `scalar + t_q` is not reduced. // - let scalar = U256::from_little_endian(&scalar.to_bytes()); + let scalar = U256::from_little_endian(&scalar.to_repr()); let t_q = U256::from_little_endian(&T_Q.to_le_bytes()); let k = scalar + t_q; @@ -445,7 +463,7 @@ fn decompose_for_scalar_mul(scalar: Option) -> Vec> { #[cfg(test)] pub mod tests { - use group::Curve; + use group::{ff::PrimeField, Curve}; use halo2::{ circuit::{Chip, Layouter}, plonk::Error, @@ -479,7 +497,7 @@ pub mod tests { ) -> Result<(), Error> { // Move scalar from base field into scalar field (which always fits // for Pallas). - let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap(); + let scalar = pallas::Scalar::from_repr(scalar_val.to_repr()).unwrap(); let expected = NonIdentityPoint::new( chip, layouter.namespace(|| "expected point"), @@ -517,7 +535,9 @@ pub mod tests { chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?; p.mul(layouter.namespace(|| "[0]B"), &scalar)? }; - assert!(result.inner().is_identity().unwrap()); + if let Some(is_identity) = result.inner().is_identity() { + assert!(is_identity); + } } // [-1]B (the largest possible base field element) diff --git a/src/circuit/gadget/ecc/chip/mul/complete.rs b/src/circuit/gadget/ecc/chip/mul/complete.rs index 030f6f95..3b040b23 100644 --- a/src/circuit/gadget/ecc/chip/mul/complete.rs +++ b/src/circuit/gadget/ecc/chip/mul/complete.rs @@ -1,5 +1,6 @@ -use super::super::{add, copy, CellValue, EccConfig, EccPoint, Var}; +use super::super::{add, EccPoint}; use super::{COMPLETE_RANGE, X, Y, Z}; +use crate::circuit::gadget::utilities::{bool_check, ternary}; use halo2::{ circuit::Region, @@ -7,8 +8,9 @@ use halo2::{ poly::Rotation, }; -use pasta_curves::{arithmetic::FieldExt, pallas}; +use pasta_curves::pallas; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Config { // Selector used to constrain the cells used in complete addition. q_mul_decompose_var: Selector, @@ -18,30 +20,30 @@ pub struct Config { add_config: add::Config, } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { +impl Config { + pub(super) fn configure( + meta: &mut ConstraintSystem, + z_complete: Column, + add_config: add::Config, + ) -> Self { + meta.enable_equality(z_complete.into()); + let config = Self { - q_mul_decompose_var: ecc_config.q_mul_decompose_var, - z_complete: ecc_config.advices[9], - add_config: ecc_config.into(), + q_mul_decompose_var: meta.selector(), + z_complete, + add_config, }; - let add_config_advices = config.add_config.advice_columns(); - assert!( - !add_config_advices.contains(&config.z_complete), - "z_complete cannot overlap with complete addition columns." - ); + config.create_gate(meta); config } -} -impl Config { /// Gate used to check scalar decomposition is correct. /// This is used to check the bits used in complete addition, since the incomplete /// addition gate (controlled by `q_mul`) already checks scalar decomposition for /// the other bits. - pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + fn create_gate(&self, meta: &mut ConstraintSystem) { // | y_p | z_complete | // -------------------- // | y_p | z_{i + 1} | @@ -57,10 +59,9 @@ impl Config { let z_next = meta.query_advice(self.z_complete, Rotation::next()); // k_{i} = z_{i} - 2⋅z_{i+1} - let k = z_next - Expression::Constant(pallas::Base::from_u64(2)) * z_prev; - let k_minus_one = k.clone() - Expression::Constant(pallas::Base::one()); - // (k_i) ⋅ (k_i - 1) = 0 - let bool_check = k.clone() * k_minus_one.clone(); + let k = z_next - Expression::Constant(pallas::Base::from(2)) * z_prev; + // (k_i) ⋅ (1 - k_i) = 0 + let bool_check = bool_check(k.clone()); // base_y let base_y = meta.query_advice(self.z_complete, Rotation::cur()); @@ -69,7 +70,7 @@ impl Config { // k_i = 0 => y_p = -base_y // k_i = 1 => y_p = base_y - let y_switch = k_minus_one * (base_y.clone() + y_p.clone()) + k * (base_y - y_p); + let y_switch = ternary(k, base_y.clone() - y_p.clone(), base_y + y_p); std::array::IntoIter::new([("bool_check", bool_check), ("y_switch", y_switch)]) .map(move |(name, poly)| (name, q_mul_decompose_var.clone() * poly)) @@ -105,16 +106,15 @@ impl Config { } // Use x_a, y_a output from incomplete addition - let mut acc = EccPoint { x: *x_a, y: *y_a }; + let mut acc = EccPoint { x: x_a.0, y: y_a.0 }; // Copy running sum `z` from incomplete addition let mut z = { - let z = copy( - region, + let z = z.copy_advice( || "Copy `z` running sum from incomplete addition", + region, self.z_complete, offset, - &z, )?; Z(z) }; @@ -137,45 +137,48 @@ impl Config { z = { // z_next = z_cur * 2 + k_next let z_val = z.value().zip(k.as_ref()).map(|(z_val, k)| { - pallas::Base::from_u64(2) * z_val + pallas::Base::from_u64(*k as u64) + pallas::Base::from(2) * z_val + pallas::Base::from(*k as u64) }); let z_cell = region.assign_advice( || "z", self.z_complete, row + offset + 2, - || z_val.ok_or(Error::SynthesisError), + || z_val.ok_or(Error::Synthesis), )?; - Z(CellValue::new(z_cell, z_val)) + Z(z_cell) }; - zs.push(z); + zs.push(z.clone()); // Assign `y_p` for complete addition. let y_p = { - let base_y = copy( - region, + let base_y = base.y.copy_advice( || "Copy `base.y`", + region, self.z_complete, row + offset + 1, - &base.y, )?; // If the bit is set, use `y`; if the bit is not set, use `-y` - let y_p = base_y - .value() - .zip(k.as_ref()) - .map(|(base_y, k)| if !k { -base_y } else { base_y }); + let y_p = + base_y + .value() + .cloned() + .zip(k.as_ref()) + .map(|(base_y, k)| if !k { -base_y } else { base_y }); - let y_p_cell = region.assign_advice( + region.assign_advice( || "y_p", self.add_config.y_p, row + offset, - || y_p.ok_or(Error::SynthesisError), - )?; - CellValue::::new(y_p_cell, y_p) + || y_p.ok_or(Error::Synthesis), + )? }; // U = P if the bit is set; U = -P is the bit is not set. - let U = EccPoint { x: base.x, y: y_p }; + let U = EccPoint { + x: base.x.clone(), + y: y_p, + }; // Acc + U let tmp_acc = self diff --git a/src/circuit/gadget/ecc/chip/mul/incomplete.rs b/src/circuit/gadget/ecc/chip/mul/incomplete.rs index b6f7d2be..6c3bfff8 100644 --- a/src/circuit/gadget/ecc/chip/mul/incomplete.rs +++ b/src/circuit/gadget/ecc/chip/mul/incomplete.rs @@ -1,7 +1,6 @@ -use std::ops::Deref; - -use super::super::{copy, CellValue, EccConfig, NonIdentityEccPoint, Var}; -use super::{INCOMPLETE_HI_RANGE, INCOMPLETE_LO_RANGE, X, Y, Z}; +use super::super::NonIdentityEccPoint; +use super::{X, Y, Z}; +use crate::circuit::gadget::utilities::bool_check; use ff::Field; use halo2::{ circuit::Region, @@ -11,10 +10,8 @@ use halo2::{ use pasta_curves::{arithmetic::FieldExt, pallas}; -#[derive(Copy, Clone)] -pub(super) struct Config { - // Number of bits covered by this incomplete range. - num_bits: usize, +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) struct Config { // Selectors used to constrain the cells used in incomplete addition. pub(super) q_mul: (Selector, Selector, Selector), // Cumulative sum used to decompose the scalar. @@ -31,61 +28,36 @@ pub(super) struct Config { pub(super) lambda2: Column, } -// Columns used in processing the `hi` bits of the scalar. -// `x_p, y_p` are shared across the `hi` and `lo` halves. -pub(super) struct HiConfig(Config); -impl From<&EccConfig> for HiConfig { - fn from(ecc_config: &EccConfig) -> Self { - let config = Config { - num_bits: INCOMPLETE_HI_RANGE.len(), - q_mul: ecc_config.q_mul_hi, - x_p: ecc_config.advices[0], - y_p: ecc_config.advices[1], - z: ecc_config.advices[9], - x_a: ecc_config.advices[3], - lambda1: ecc_config.advices[4], - lambda2: ecc_config.advices[5], +impl Config { + pub(super) fn configure( + meta: &mut ConstraintSystem, + z: Column, + x_a: Column, + x_p: Column, + y_p: Column, + lambda1: Column, + lambda2: Column, + ) -> Self { + meta.enable_equality(z.into()); + meta.enable_equality(lambda1.into()); + + let config = Self { + q_mul: (meta.selector(), meta.selector(), meta.selector()), + z, + x_a, + x_p, + y_p, + lambda1, + lambda2, }; - Self(config) - } -} -impl Deref for HiConfig { - type Target = Config; - fn deref(&self) -> &Config { - &self.0 - } -} + config.create_gate(meta); -// Columns used in processing the `lo` bits of the scalar. -// `x_p, y_p` are shared across the `hi` and `lo` halves. -pub(super) struct LoConfig(Config); -impl From<&EccConfig> for LoConfig { - fn from(ecc_config: &EccConfig) -> Self { - let config = Config { - num_bits: INCOMPLETE_LO_RANGE.len(), - q_mul: ecc_config.q_mul_lo, - x_p: ecc_config.advices[0], - y_p: ecc_config.advices[1], - z: ecc_config.advices[6], - x_a: ecc_config.advices[7], - lambda1: ecc_config.advices[8], - lambda2: ecc_config.advices[2], - }; - Self(config) + config } -} -impl Deref for LoConfig { - type Target = Config; - fn deref(&self) -> &Config { - &self.0 - } -} - -impl Config { // Gate for incomplete addition part of variable-base scalar multiplication. - pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + fn create_gate(&self, meta: &mut ConstraintSystem) { // Closure to compute x_{R,i} = λ_{1,i}^2 - x_{A,i} - x_{P,i} let x_r = |meta: &mut VirtualCells, rotation: Rotation| { let x_a = meta.query_advice(self.x_a, rotation); @@ -131,13 +103,13 @@ impl Config { // The current bit in the scalar decomposition, k_i = z_i - 2⋅z_{i+1}. // Recall that we assigned the cumulative variable `z_i` in descending order, // i from n down to 0. So z_{i+1} corresponds to the `z_prev` query. - let k = z_cur - z_prev * pallas::Base::from_u64(2); + let k = z_cur - z_prev * pallas::Base::from(2); // Check booleanity of decomposition. - let bool_check = k.clone() * (one.clone() - k.clone()); + let bool_check = bool_check(k.clone()); // λ_{1,i}⋅(x_{A,i} − x_{P,i}) − y_{A,i} + (2k_i - 1) y_{P,i} = 0 let gradient_1 = lambda1_cur * (x_a_cur.clone() - x_p_cur) - y_a_cur.clone() - + (k * pallas::Base::from_u64(2) - one) * y_p_cur; + + (k * pallas::Base::from(2) - one) * y_p_cur; // λ_{2,i}^2 − x_{A,i-1} − x_{R,i} − x_{A,i} = 0 let secant_line = lambda2_cur.clone().square() @@ -215,21 +187,21 @@ impl Config { acc: (X, Y, Z), ) -> Result<(X, Y, Vec>), Error> { // Check that we have the correct number of bits for this double-and-add. - assert_eq!(bits.len(), self.num_bits); + assert_eq!(bits.len(), NUM_BITS); // Handle exceptional cases - let (x_p, y_p) = (base.x.value(), base.y.value()); - let (x_a, y_a) = (acc.0.value(), acc.1.value()); + let (x_p, y_p) = (base.x.value().cloned(), base.y.value().cloned()); + let (x_a, y_a) = (acc.0.value().cloned(), acc.1.value().cloned()); if let (Some(x_a), Some(y_a), Some(x_p), Some(y_p)) = (x_a, y_a, x_p, y_p) { // A is point at infinity - if (x_p == pallas::Base::zero() && y_p == pallas::Base::zero()) + if (x_p.is_zero_vartime() && y_p.is_zero_vartime()) // Q is point at infinity - || (x_a == pallas::Base::zero() && y_a == pallas::Base::zero()) + || (x_a.is_zero_vartime() && y_a.is_zero_vartime()) // x_p = x_a || (x_p == x_a) { - return Err(Error::SynthesisError); + return Err(Error::Synthesis); } } @@ -240,24 +212,28 @@ impl Config { let offset = offset + 1; // q_mul_2 = 1 on all rows after offset 0, excluding the last row. - for idx in 0..(self.num_bits - 1) { + for idx in 0..(NUM_BITS - 1) { self.q_mul.1.enable(region, offset + idx)?; } // q_mul_3 = 1 on the last row. - self.q_mul.2.enable(region, offset + self.num_bits - 1)?; + self.q_mul.2.enable(region, offset + NUM_BITS - 1)?; } // Initialise double-and-add let (mut x_a, mut y_a, mut z) = { // Initialise the running `z` sum for the scalar bits. - let z = copy(region, || "starting z", self.z, offset, &acc.2)?; + let z = acc.2.copy_advice(|| "starting z", region, self.z, offset)?; // Initialise acc - let x_a = copy(region, || "starting x_a", self.x_a, offset + 1, &acc.0)?; - let y_a = copy(region, || "starting y_a", self.lambda1, offset, &acc.1)?; + let x_a = acc + .0 + .copy_advice(|| "starting x_a", region, self.x_a, offset + 1)?; + let y_a = acc + .1 + .copy_advice(|| "starting y_a", region, self.lambda1, offset)?; - (x_a, y_a.value(), z) + (x_a, y_a.value().cloned(), z) }; // Increase offset by 1; we used row 0 for initializing `z`. @@ -269,30 +245,30 @@ impl Config { // Incomplete addition for (row, k) in bits.iter().enumerate() { // z_{i} = 2 * z_{i+1} + k_i - let z_val = z.value().zip(k.as_ref()).map(|(z_val, k)| { - pallas::Base::from_u64(2) * z_val + pallas::Base::from_u64(*k as u64) - }); - let z_cell = region.assign_advice( + let z_val = z + .value() + .zip(k.as_ref()) + .map(|(z_val, k)| pallas::Base::from(2) * z_val + pallas::Base::from(*k as u64)); + z = region.assign_advice( || "z", self.z, row + offset, - || z_val.ok_or(Error::SynthesisError), + || z_val.ok_or(Error::Synthesis), )?; - z = CellValue::new(z_cell, z_val); - zs.push(Z(z)); + zs.push(Z(z.clone())); // Assign `x_p`, `y_p` region.assign_advice( || "x_p", self.x_p, row + offset, - || x_p.ok_or(Error::SynthesisError), + || x_p.ok_or(Error::Synthesis), )?; region.assign_advice( || "y_p", self.y_p, row + offset, - || y_p.ok_or(Error::SynthesisError), + || y_p.ok_or(Error::Synthesis), )?; // If the bit is set, use `y`; if the bit is not set, use `-y` @@ -310,7 +286,7 @@ impl Config { || "lambda1", self.lambda1, row + offset, - || lambda1.ok_or(Error::SynthesisError), + || lambda1.ok_or(Error::Synthesis), )?; // x_R = λ1^2 - x_A - x_P @@ -326,13 +302,13 @@ impl Config { .zip(x_a.value()) .zip(x_r) .map(|(((lambda1, y_a), x_a), x_r)| { - pallas::Base::from_u64(2) * y_a * (x_a - x_r).invert().unwrap() - lambda1 + pallas::Base::from(2) * y_a * (x_a - x_r).invert().unwrap() - lambda1 }); region.assign_advice( || "lambda2", self.lambda2, row + offset, - || lambda2.ok_or(Error::SynthesisError), + || lambda2.ok_or(Error::Synthesis), )?; // Compute and assign `x_a` for the next row @@ -341,30 +317,26 @@ impl Config { .zip(x_r) .map(|((lambda2, x_a), x_r)| lambda2.square() - x_a - x_r); y_a = lambda2 - .zip(x_a.value()) + .zip(x_a.value().cloned()) .zip(x_a_new) .zip(y_a) .map(|(((lambda2, x_a), x_a_new), y_a)| lambda2 * (x_a - x_a_new) - y_a); let x_a_val = x_a_new; - let x_a_cell = region.assign_advice( + x_a = region.assign_advice( || "x_a", self.x_a, row + offset + 1, - || x_a_val.ok_or(Error::SynthesisError), + || x_a_val.ok_or(Error::Synthesis), )?; - x_a = CellValue::new(x_a_cell, x_a_val); } // Witness final y_a - let y_a = { - let cell = region.assign_advice( - || "y_a", - self.lambda1, - offset + self.num_bits, - || y_a.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, y_a) - }; + let y_a = region.assign_advice( + || "y_a", + self.lambda1, + offset + NUM_BITS, + || y_a.ok_or(Error::Synthesis), + )?; Ok((X(x_a), Y(y_a), zs)) } diff --git a/src/circuit/gadget/ecc/chip/mul/overflow.rs b/src/circuit/gadget/ecc/chip/mul/overflow.rs index 109616b8..30ab00ee 100644 --- a/src/circuit/gadget/ecc/chip/mul/overflow.rs +++ b/src/circuit/gadget/ecc/chip/mul/overflow.rs @@ -1,9 +1,9 @@ -use super::super::{copy, CellValue, EccConfig, Var}; use super::Z; use crate::{ circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q, primitives::sinsemilla, }; +use halo2::circuit::AssignedCell; use halo2::{ circuit::Layouter, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, @@ -15,6 +15,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas}; use std::iter; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Config { // Selector to check z_0 = alpha + t_q (mod p) q_mul_overflow: Selector, @@ -24,24 +25,28 @@ pub struct Config { advices: [Column; 3], } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { - Self { - q_mul_overflow: ecc_config.q_mul_overflow, - lookup_config: ecc_config.lookup_config.clone(), - // Use advice columns that don't conflict with the either the incomplete - // additions in fixed-base scalar mul, or the lookup range checks. - advices: [ - ecc_config.advices[6], - ecc_config.advices[7], - ecc_config.advices[8], - ], - } - } -} - impl Config { - pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + pub(super) fn configure( + meta: &mut ConstraintSystem, + lookup_config: LookupRangeCheckConfig, + advices: [Column; 3], + ) -> Self { + for advice in advices.iter() { + meta.enable_equality((*advice).into()); + } + + let config = Self { + q_mul_overflow: meta.selector(), + lookup_config, + advices, + }; + + config.create_gate(meta); + + config + } + + fn create_gate(&self, meta: &mut ConstraintSystem) { meta.create_gate("overflow checks", |meta| { let q_mul_overflow = meta.query_selector(self.q_mul_overflow); @@ -94,7 +99,7 @@ impl Config { pub(super) fn overflow_check( &self, mut layouter: impl Layouter, - alpha: CellValue, + alpha: AssignedCell, zs: &[Z], // [z_0, z_1, ..., z_{254}, z_{255}] ) -> Result<(), Error> { // s = alpha + k_254 ⋅ 2^130 is witnessed here, and then copied into @@ -102,7 +107,7 @@ impl Config { // In the overflow check gate, we check that s is properly derived // from alpha and k_254. let s = { - let k_254 = *zs[254]; + let k_254 = zs[254].clone(); let s_val = alpha .value() .zip(k_254.value()) @@ -111,13 +116,12 @@ impl Config { layouter.assign_region( || "s = alpha + k_254 ⋅ 2^130", |mut region| { - let s_cell = region.assign_advice( + region.assign_advice( || "s = alpha + k_254 ⋅ 2^130", self.advices[0], 0, - || s_val.ok_or(Error::SynthesisError), - )?; - Ok(CellValue::new(s_cell, s_val)) + || s_val.ok_or(Error::Synthesis), + ) }, )? }; @@ -125,7 +129,7 @@ impl Config { // Subtract the first 130 low bits of s = alpha + k_254 ⋅ 2^130 // using thirteen 10-bit lookups, s_{0..=129} let s_minus_lo_130 = - self.s_minus_lo_130(layouter.namespace(|| "decompose s_{0..=129}"), s)?; + self.s_minus_lo_130(layouter.namespace(|| "decompose s_{0..=129}"), s.clone())?; layouter.assign_region( || "overflow check", @@ -136,21 +140,15 @@ impl Config { self.q_mul_overflow.enable(&mut region, offset + 1)?; // Copy `z_0` - copy(&mut region, || "copy z_0", self.advices[0], offset, &*zs[0])?; + zs[0].copy_advice(|| "copy z_0", &mut region, self.advices[0], offset)?; // Copy `z_130` - copy( - &mut region, - || "copy z_130", - self.advices[0], - offset + 1, - &*zs[130], - )?; + zs[130].copy_advice(|| "copy z_130", &mut region, self.advices[0], offset + 1)?; // Witness η = inv0(z_130), where inv0(x) = 0 if x = 0, 1/x otherwise { let eta = zs[130].value().map(|z_130| { - if z_130 == pallas::Base::zero() { + if z_130.is_zero_vartime() { pallas::Base::zero() } else { z_130.invert().unwrap() @@ -160,39 +158,31 @@ impl Config { || "η = inv0(z_130)", self.advices[0], offset + 2, - || eta.ok_or(Error::SynthesisError), + || eta.ok_or(Error::Synthesis), )?; } // Copy `k_254` = z_254 - copy( - &mut region, - || "copy k_254", - self.advices[1], - offset, - &*zs[254], - )?; + zs[254].copy_advice(|| "copy k_254", &mut region, self.advices[1], offset)?; // Copy original alpha - copy( - &mut region, + alpha.copy_advice( || "copy original alpha", + &mut region, self.advices[1], offset + 1, - &alpha, )?; // Copy weighted sum of the decomposition of s = alpha + k_254 ⋅ 2^130. - copy( - &mut region, + s_minus_lo_130.copy_advice( || "copy s_minus_lo_130", + &mut region, self.advices[1], offset + 2, - &s_minus_lo_130, )?; // Copy witnessed s to check that it was properly derived from alpha and k_254. - copy(&mut region, || "copy s", self.advices[2], offset + 1, &s)?; + s.copy_advice(|| "copy s", &mut region, self.advices[2], offset + 1)?; Ok(()) }, @@ -204,8 +194,8 @@ impl Config { fn s_minus_lo_130( &self, mut layouter: impl Layouter, - s: CellValue, - ) -> Result, Error> { + s: AssignedCell, + ) -> Result, Error> { // Number of k-bit words we can use in the lookup decomposition. let num_words = 130 / sinsemilla::K; assert!(num_words * sinsemilla::K == 130); @@ -218,6 +208,6 @@ impl Config { false, )?; // (s - (2^0 s_0 + 2^1 s_1 + ... + 2^129 s_129)) / 2^130 - Ok(zs[zs.len() - 1]) + Ok(zs[zs.len() - 1].clone()) } } diff --git a/src/circuit/gadget/ecc/chip/mul_fixed.rs b/src/circuit/gadget/ecc/chip/mul_fixed.rs index 2f470111..b5e47338 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed.rs @@ -1,15 +1,16 @@ use super::{ - add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccConfig, EccScalarFixed, - EccScalarFixedShort, NonIdentityEccPoint, Var, + add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort, + NonIdentityEccPoint, }; +use crate::circuit::gadget::utilities::decompose_running_sum::RunningSumConfig; use crate::constants::{ self, load::{NullifierK, OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV, WindowUs}, }; -use group::Curve; +use group::{ff::PrimeField, Curve}; use halo2::{ - circuit::Region, + circuit::{AssignedCell, Region}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, VirtualCells}, poly::Rotation, }; @@ -24,10 +25,10 @@ pub mod full_width; pub mod short; lazy_static! { - static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(2); + static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from(2); // H = 2^3 (3-bit window) - static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(constants::H as u64); - static ref H_BASE: pallas::Base = pallas::Base::from_u64(constants::H as u64); + static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from(constants::H as u64); + static ref H_BASE: pallas::Base = pallas::Base::from(constants::H as u64); } // A sum type for both full-width and short bases. This enables us to use the @@ -75,9 +76,9 @@ impl OrchardFixedBases { } } -#[derive(Clone, Debug)] -pub struct Config { - q_mul_fixed_running_sum: Selector, +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Config { + running_sum_config: RunningSumConfig, // The fixed Lagrange interpolation coefficients for `x_p`. lagrange_coeffs: [Column; constants::H], // The fixed `z` for each window such that `y + z = u^2`. @@ -97,18 +98,34 @@ pub struct Config { add_incomplete_config: add_incomplete::Config, } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { +impl Config { + #[allow(clippy::too_many_arguments)] + pub(super) fn configure( + meta: &mut ConstraintSystem, + lagrange_coeffs: [Column; constants::H], + window: Column, + x_p: Column, + y_p: Column, + u: Column, + add_config: add::Config, + add_incomplete_config: add_incomplete::Config, + ) -> Self { + meta.enable_equality(window.into()); + meta.enable_equality(u.into()); + + let q_running_sum = meta.selector(); + let running_sum_config = RunningSumConfig::configure(meta, q_running_sum, window); + let config = Self { - q_mul_fixed_running_sum: ecc_config.q_mul_fixed_running_sum, - lagrange_coeffs: ecc_config.lagrange_coeffs, - fixed_z: ecc_config.fixed_z, - x_p: ecc_config.advices[0], - y_p: ecc_config.advices[1], - window: ecc_config.advices[4], - u: ecc_config.advices[5], - add_config: ecc_config.into(), - add_incomplete_config: ecc_config.into(), + running_sum_config, + lagrange_coeffs, + fixed_z: meta.fixed_column(), + window, + x_p, + y_p, + u, + add_config, + add_incomplete_config, }; // Check relationships between this config and `add_config`. @@ -141,11 +158,11 @@ impl From<&EccConfig> for Config { ); } + config.running_sum_coords_gate(meta); + config } -} -impl Config { /// Check that each window in the running sum decomposition uses the correct y_p /// and interpolated x_p. /// @@ -155,16 +172,17 @@ impl Config { /// This gate is not used in the mul_fixed::full_width helper, since the full-width /// scalar is witnessed directly as three-bit windows instead of being decomposed /// via a running sum. - pub(crate) fn running_sum_coords_gate(&self, meta: &mut ConstraintSystem) { + fn running_sum_coords_gate(&self, meta: &mut ConstraintSystem) { meta.create_gate("Running sum coordinates check", |meta| { - let q_mul_fixed_running_sum = meta.query_selector(self.q_mul_fixed_running_sum); + let q_mul_fixed_running_sum = + meta.query_selector(self.running_sum_config.q_range_check); let z_cur = meta.query_advice(self.window, Rotation::cur()); let z_next = meta.query_advice(self.window, Rotation::next()); // z_{i+1} = (z_i - a_i) / 2^3 // => a_i = z_i - z_{i+1} * 2^3 - let word = z_cur - z_next * pallas::Base::from_u64(constants::H as u64); + let word = z_cur - z_next * pallas::Base::from(constants::H as u64); self.coords_check(meta, q_mul_fixed_running_sum, word) }); @@ -213,7 +231,7 @@ impl Config { } #[allow(clippy::type_complexity)] - fn assign_region_inner( + fn assign_region_inner( &self, region: &mut Region<'_, pallas::Base>, offset: usize, @@ -222,7 +240,7 @@ impl Config { coords_check_toggle: Selector, ) -> Result<(NonIdentityEccPoint, NonIdentityEccPoint), Error> { // Assign fixed columns for given fixed base - self.assign_fixed_constants(region, offset, base, coords_check_toggle)?; + self.assign_fixed_constants::(region, offset, base, coords_check_toggle)?; // Initialize accumulator let acc = self.initialize_accumulator(region, offset, base, scalar)?; @@ -231,12 +249,12 @@ impl Config { let acc = self.add_incomplete(region, offset, acc, base, scalar)?; // Process most significant window using complete addition - let mul_b = self.process_msb(region, offset, base, scalar)?; + let mul_b = self.process_msb::(region, offset, base, scalar)?; Ok((acc, mul_b)) } - fn assign_fixed_constants( + fn assign_fixed_constants( &self, region: &mut Region<'_, pallas::Base>, offset: usize, @@ -335,38 +353,31 @@ impl Config { assert!(x != pallas::Base::zero()); x }); - let x_cell = region.assign_advice( + let x = region.assign_advice( || format!("mul_b_x, window {}", w), self.x_p, offset + w, - || x.ok_or(Error::SynthesisError), + || x.ok_or(Error::Synthesis), )?; - let x = CellValue::new(x_cell, x); let y = mul_b.map(|mul_b| { let y = *mul_b.y(); assert!(y != pallas::Base::zero()); y }); - let y_cell = region.assign_advice( + let y = region.assign_advice( || format!("mul_b_y, window {}", w), self.y_p, offset + w, - || y.ok_or(Error::SynthesisError), + || y.ok_or(Error::Synthesis), )?; - let y = CellValue::new(y_cell, y); NonIdentityEccPoint { x, y } }; // Assign u = (y_p + z_w).sqrt() let u_val = k_usize.map(|k| base_u[w].0[k]); - region.assign_advice( - || "u", - self.u, - offset + w, - || u_val.ok_or(Error::SynthesisError), - )?; + region.assign_advice(|| "u", self.u, offset + w, || u_val.ok_or(Error::Synthesis))?; Ok(mul_b) } @@ -416,7 +427,7 @@ impl Config { Ok(acc) } - fn process_msb( + fn process_msb( &self, region: &mut Region<'_, pallas::Base>, offset: usize, @@ -431,7 +442,7 @@ impl Config { || "u", self.u, offset + NUM_WINDOWS - 1, - || u_val.ok_or(Error::SynthesisError), + || u_val.ok_or(Error::Synthesis), )?; } @@ -458,27 +469,24 @@ impl Config { assert!(x != pallas::Base::zero()); x }); - let x_cell = region.assign_advice( + let x = region.assign_advice( || format!("mul_b_x, window {}", NUM_WINDOWS - 1), self.x_p, offset + NUM_WINDOWS - 1, - || x.ok_or(Error::SynthesisError), + || x.ok_or(Error::Synthesis), )?; - let x = CellValue::new(x_cell, x); - let y = mul_b.map(|mul_b| { let y = *mul_b.y(); assert!(y != pallas::Base::zero()); y }); - let y_cell = region.assign_advice( + let y = region.assign_advice( || format!("mul_b_y, window {}", NUM_WINDOWS - 1), self.y_p, offset + NUM_WINDOWS - 1, - || y.ok_or(Error::SynthesisError), + || y.ok_or(Error::Synthesis), )?; - let y = CellValue::new(y_cell, y); NonIdentityEccPoint { x, y } }; @@ -515,7 +523,7 @@ impl ScalarFixed { // The scalar decomposition was done in the base field. For computation // outside the circuit, we now convert them back into the scalar field. fn windows_field(&self) -> Vec> { - let running_sum_to_windows = |zs: Vec>| { + let running_sum_to_windows = |zs: Vec>| { (0..(zs.len() - 1)) .map(|idx| { let z_cur = zs[idx].value(); @@ -523,7 +531,7 @@ impl ScalarFixed { let word = z_cur .zip(z_next) .map(|(z_cur, z_next)| z_cur - z_next * *H_BASE); - word.map(|word| pallas::Scalar::from_bytes(&word.to_bytes()).unwrap()) + word.map(|word| pallas::Scalar::from_repr(word.to_repr()).unwrap()) }) .collect::>() }; @@ -535,7 +543,7 @@ impl ScalarFixed { .iter() .map(|bits| { bits.value() - .map(|value| pallas::Scalar::from_bytes(&value.to_bytes()).unwrap()) + .map(|value| pallas::Scalar::from_repr(value.to_repr()).unwrap()) }) .collect::>(), } diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs b/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs index ff05a331..99eefe39 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs @@ -1,14 +1,14 @@ -use super::super::{EccBaseFieldElemFixed, EccConfig, EccPoint, NullifierK}; +use super::super::{EccBaseFieldElemFixed, EccPoint, NullifierK}; use super::H_BASE; use crate::{ circuit::gadget::utilities::{ - bitrange_subset, copy, decompose_running_sum::RunningSumConfig, - lookup_range_check::LookupRangeCheckConfig, range_check, CellValue, Var, + bitrange_subset, lookup_range_check::LookupRangeCheckConfig, range_check, }, constants::{self, T_P}, primitives::sinsemilla, }; +use halo2::circuit::AssignedCell; use halo2::{ circuit::Layouter, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, @@ -18,24 +18,30 @@ use pasta_curves::{arithmetic::FieldExt, pallas}; use std::convert::TryInto; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Config { - q_mul_fixed_running_sum: Selector, q_mul_fixed_base_field: Selector, canon_advices: [Column; 3], lookup_config: LookupRangeCheckConfig, - running_sum_config: RunningSumConfig, - super_config: super::Config<{ constants::NUM_WINDOWS }>, + super_config: super::Config, } -impl From<&EccConfig> for Config { - fn from(config: &EccConfig) -> Self { +impl Config { + pub(crate) fn configure( + meta: &mut ConstraintSystem, + canon_advices: [Column; 3], + lookup_config: LookupRangeCheckConfig, + super_config: super::Config, + ) -> Self { + for advice in canon_advices.iter() { + meta.enable_equality((*advice).into()); + } + let config = Self { - q_mul_fixed_running_sum: config.q_mul_fixed_running_sum, - q_mul_fixed_base_field: config.q_mul_fixed_base_field, - canon_advices: [config.advices[6], config.advices[7], config.advices[8]], - lookup_config: config.lookup_config.clone(), - running_sum_config: config.running_sum_config.clone(), - super_config: config.into(), + q_mul_fixed_base_field: meta.selector(), + canon_advices, + lookup_config, + super_config, }; let add_incomplete_advices = config.super_config.add_incomplete_config.advice_columns(); @@ -46,14 +52,12 @@ impl From<&EccConfig> for Config { ); } - assert_eq!(config.running_sum_config.z, config.super_config.window); + config.create_gate(meta); config } -} -impl Config { - pub fn create_gate(&self, meta: &mut ConstraintSystem) { + fn create_gate(&self, meta: &mut ConstraintSystem) { // Check that the base field element is canonical. meta.create_gate("Canonicity checks", |meta| { let q_mul_fixed_base_field = meta.query_selector(self.q_mul_fixed_base_field); @@ -85,7 +89,7 @@ impl Config { let alpha_2_range_check = range_check(alpha_2.clone(), 1 << 1); // Check that α_1 + 2^2 α_2 = z_84_alpha let z_84_alpha_check = z_84_alpha.clone() - - (alpha_1.clone() + alpha_2.clone() * pallas::Base::from_u64(1 << 2)); + - (alpha_1.clone() + alpha_2.clone() * pallas::Base::from(1 << 2)); std::iter::empty() .chain(Some(("alpha_1_range_check", alpha_1_range_check))) @@ -156,7 +160,7 @@ impl Config { pub fn assign( &self, mut layouter: impl Layouter, - scalar: CellValue, + scalar: AssignedCell, base: NullifierK, ) -> Result { let (scalar, acc, mul_b) = layouter.assign_region( @@ -166,27 +170,29 @@ impl Config { // Decompose scalar let scalar = { - let running_sum = self.running_sum_config.copy_decompose( + let running_sum = self.super_config.running_sum_config.copy_decompose( &mut region, offset, - scalar, + scalar.clone(), true, constants::L_ORCHARD_BASE, constants::NUM_WINDOWS, )?; EccBaseFieldElemFixed { - base_field_elem: running_sum[0], + base_field_elem: running_sum[0].clone(), running_sum: (*running_sum).as_slice().try_into().unwrap(), } }; - let (acc, mul_b) = self.super_config.assign_region_inner( - &mut region, - offset, - &(&scalar).into(), - base.into(), - self.q_mul_fixed_running_sum, - )?; + let (acc, mul_b) = self + .super_config + .assign_region_inner::<{ constants::NUM_WINDOWS }>( + &mut region, + offset, + &(&scalar).into(), + base.into(), + self.super_config.running_sum_config.q_range_check, + )?; Ok((scalar, acc, mul_b)) }, @@ -197,8 +203,8 @@ impl Config { || "Base-field elem fixed-base mul (complete addition)", |mut region| { self.super_config.add_config.assign_region( - &mul_b.into(), - &acc.into(), + &mul_b.clone().into(), + &acc.clone().into(), 0, &mut region, ) @@ -208,13 +214,13 @@ impl Config { #[cfg(test)] // Check that the correct multiple is obtained. { - use group::Curve; + use group::{ff::PrimeField, Curve}; let base: super::OrchardFixedBases = base.into(); let scalar = &scalar .base_field_elem() .value() - .map(|scalar| pallas::Scalar::from_bytes(&scalar.to_bytes()).unwrap()); + .map(|scalar| pallas::Scalar::from_repr(scalar.to_repr()).unwrap()); let real_mul = scalar.map(|scalar| base.generator() * scalar); let result = result.point(); @@ -243,9 +249,9 @@ impl Config { // => z_13_alpha_0_prime = 0 // let (alpha, running_sum) = (scalar.base_field_elem, &scalar.running_sum); - let z_43_alpha = running_sum[43]; - let z_44_alpha = running_sum[44]; - let z_84_alpha = running_sum[84]; + let z_43_alpha = running_sum[43].clone(); + let z_44_alpha = running_sum[44].clone(); + let z_84_alpha = running_sum[84].clone(); // α_0 = α - z_84_alpha * 2^252 let alpha_0 = alpha @@ -269,9 +275,9 @@ impl Config { 13, false, )?; - let alpha_0_prime = zs[0]; + let alpha_0_prime = zs[0].clone(); - (alpha_0_prime, zs[13]) + (alpha_0_prime, zs[13].clone()) }; layouter.assign_region( @@ -285,21 +291,14 @@ impl Config { let offset = 0; // Copy α - copy( - &mut region, - || "Copy α", - self.canon_advices[0], - offset, - &alpha, - )?; + alpha.copy_advice(|| "Copy α", &mut region, self.canon_advices[0], offset)?; // z_84_alpha = the top three bits of alpha. - copy( - &mut region, + z_84_alpha.copy_advice( || "Copy z_84_alpha", + &mut region, self.canon_advices[2], offset, - &z_84_alpha, )?; } @@ -308,12 +307,11 @@ impl Config { let offset = 1; // Copy alpha_0_prime = alpha_0 + 2^130 - t_p. // We constrain this in the custom gate to be derived correctly. - copy( - &mut region, + alpha_0_prime.copy_advice( || "Copy α_0 + 2^130 - t_p", + &mut region, self.canon_advices[0], offset, - &alpha_0_prime, )?; // Decompose α into three pieces, @@ -325,7 +323,7 @@ impl Config { || "α_1 = α[252..=253]", self.canon_advices[1], offset, - || alpha_1.ok_or(Error::SynthesisError), + || alpha_1.ok_or(Error::Synthesis), )?; // Witness the MSB α_2 = α[254] @@ -334,7 +332,7 @@ impl Config { || "α_2 = α[254]", self.canon_advices[2], offset, - || alpha_2.ok_or(Error::SynthesisError), + || alpha_2.ok_or(Error::Synthesis), )?; } @@ -342,30 +340,27 @@ impl Config { { let offset = 2; // Copy z_13_alpha_0_prime - copy( - &mut region, + z_13_alpha_0_prime.copy_advice( || "Copy z_13_alpha_0_prime", + &mut region, self.canon_advices[0], offset, - &z_13_alpha_0_prime, )?; // Copy z_44_alpha - copy( - &mut region, + z_44_alpha.copy_advice( || "Copy z_44_alpha", + &mut region, self.canon_advices[1], offset, - &z_44_alpha, )?; // Copy z_43_alpha - copy( - &mut region, + z_43_alpha.copy_advice( || "Copy z_43_alpha", + &mut region, self.canon_advices[2], offset, - &z_43_alpha, )?; } @@ -379,7 +374,7 @@ impl Config { #[cfg(test)] pub mod tests { - use group::Curve; + use group::{ff::PrimeField, Curve}; use halo2::{ circuit::{Chip, Layouter}, plonk::Error, @@ -426,7 +421,7 @@ pub mod tests { result: Point, ) -> Result<(), Error> { // Move scalar from base field into scalar field (which always fits for Pallas). - let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap(); + let scalar = pallas::Scalar::from_repr(scalar_val.to_repr()).unwrap(); let expected = NonIdentityPoint::new( chip, layouter.namespace(|| "expected point"), @@ -460,11 +455,11 @@ pub mod tests { // (There is another *non-canonical* sequence // 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.) { - let h = pallas::Base::from_u64(constants::H as u64); + let h = pallas::Base::from(constants::H as u64); let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334" .chars() .fold(pallas::Base::zero(), |acc, c| { - acc * &h + &pallas::Base::from_u64(c.to_digit(8).unwrap().into()) + acc * &h + &pallas::Base::from(c.to_digit(8).unwrap() as u64) }); let result = { let scalar_fixed = chip.load_private( @@ -492,7 +487,9 @@ pub mod tests { chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_fixed))?; base.mul(layouter.namespace(|| "mul by zero"), scalar_fixed)? }; - assert!(result.inner().is_identity().unwrap()); + if let Some(is_identity) = result.inner().is_identity() { + assert!(is_identity); + } } // [-1]B is the largest base field element diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs b/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs index 7212a049..10b09146 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs @@ -1,33 +1,39 @@ -use super::super::{EccConfig, EccPoint, EccScalarFixed, OrchardFixedBasesFull}; +use super::super::{EccPoint, EccScalarFixed, OrchardFixedBasesFull}; use crate::{ - circuit::gadget::utilities::{range_check, CellValue, Var}, + circuit::gadget::utilities::range_check, constants::{self, util, L_ORCHARD_SCALAR, NUM_WINDOWS}, }; use arrayvec::ArrayVec; use halo2::{ - circuit::{Layouter, Region}, + circuit::{AssignedCell, Layouter, Region}, plonk::{ConstraintSystem, Error, Selector}, poly::Rotation, }; -use pasta_curves::{arithmetic::FieldExt, pallas}; +use pasta_curves::pallas; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Config { q_mul_fixed_full: Selector, - super_config: super::Config, -} - -impl From<&EccConfig> for Config { - fn from(config: &EccConfig) -> Self { - Self { - q_mul_fixed_full: config.q_mul_fixed_full, - super_config: config.into(), - } - } + super_config: super::Config, } impl Config { - pub fn create_gate(&self, meta: &mut ConstraintSystem) { + pub(crate) fn configure( + meta: &mut ConstraintSystem, + super_config: super::Config, + ) -> Self { + let config = Self { + q_mul_fixed_full: meta.selector(), + super_config, + }; + + config.create_gate(meta); + + config + } + + fn create_gate(&self, meta: &mut ConstraintSystem) { // Check that each window `k` is within 3 bits meta.create_gate("Full-width fixed-base scalar mul", |meta| { let q_mul_fixed_full = meta.query_selector(self.q_mul_fixed_full); @@ -70,7 +76,7 @@ impl Config { scalar: Option, offset: usize, region: &mut Region<'_, pallas::Base>, - ) -> Result, NUM_WINDOWS>, Error> { + ) -> Result, NUM_WINDOWS>, Error> { // Enable `q_mul_fixed_full` selector for idx in 0..NUM_WINDOWS { self.q_mul_fixed_full.enable(region, offset + idx)?; @@ -79,20 +85,21 @@ impl Config { // Decompose scalar into `k-bit` windows let scalar_windows: Option> = scalar.map(|scalar| { util::decompose_word::( - scalar, + &scalar, SCALAR_NUM_BITS, constants::FIXED_BASE_WINDOW_SIZE, ) }); // Store the scalar decomposition - let mut windows: ArrayVec, NUM_WINDOWS> = ArrayVec::new(); + let mut windows: ArrayVec, NUM_WINDOWS> = + ArrayVec::new(); let scalar_windows: Vec> = if let Some(windows) = scalar_windows { assert_eq!(windows.len(), NUM_WINDOWS); windows .into_iter() - .map(|window| Some(pallas::Base::from_u64(window as u64))) + .map(|window| Some(pallas::Base::from(window as u64))) .collect() } else { vec![None; NUM_WINDOWS] @@ -103,9 +110,9 @@ impl Config { || format!("k[{:?}]", offset + idx), self.super_config.window, offset + idx, - || window.ok_or(Error::SynthesisError), + || window.ok_or(Error::Synthesis), )?; - windows.push(CellValue::new(window_cell, window)); + windows.push(window_cell); } Ok(windows) @@ -124,13 +131,15 @@ impl Config { let scalar = self.witness(&mut region, offset, scalar)?; - let (acc, mul_b) = self.super_config.assign_region_inner( - &mut region, - offset, - &(&scalar).into(), - base.into(), - self.q_mul_fixed_full, - )?; + let (acc, mul_b) = self + .super_config + .assign_region_inner::<{ constants::NUM_WINDOWS }>( + &mut region, + offset, + &(&scalar).into(), + base.into(), + self.q_mul_fixed_full, + )?; Ok((scalar, acc, mul_b)) }, @@ -141,8 +150,8 @@ impl Config { || "Full-width fixed-base mul (last window, complete addition)", |mut region| { self.super_config.add_config.assign_region( - &mul_b.into(), - &acc.into(), + &mul_b.clone().into(), + &acc.clone().into(), 0, &mut region, ) @@ -263,11 +272,11 @@ pub mod tests { // (There is another *non-canonical* sequence // 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.) { - let h = pallas::Scalar::from_u64(constants::H as u64); + let h = pallas::Scalar::from(constants::H as u64); let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334" .chars() .fold(pallas::Scalar::zero(), |acc, c| { - acc * &h + &pallas::Scalar::from_u64(c.to_digit(8).unwrap().into()) + acc * &h + &pallas::Scalar::from(c.to_digit(8).unwrap() as u64) }); let (result, _) = base.mul(layouter.namespace(|| "mul with double"), Some(scalar_fixed))?; @@ -286,7 +295,9 @@ pub mod tests { { let scalar_fixed = pallas::Scalar::zero(); let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), Some(scalar_fixed))?; - assert!(result.inner().is_identity().unwrap()); + if let Some(is_identity) = result.inner().is_identity() { + assert!(is_identity); + } } // [-1]B is the largest scalar field element. diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/short.rs b/src/circuit/gadget/ecc/chip/mul_fixed/short.rs index ac22756b..8292da8a 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/short.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/short.rs @@ -1,9 +1,9 @@ use std::{array, convert::TryInto}; -use super::super::{EccConfig, EccPoint, EccScalarFixedShort}; +use super::super::{EccPoint, EccScalarFixedShort}; use crate::{ - circuit::gadget::utilities::{copy, decompose_running_sum::RunningSumConfig, CellValue, Var}, - constants::{ValueCommitV, FIXED_BASE_WINDOW_SIZE, L_VALUE, NUM_WINDOWS_SHORT}, + circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check}, + constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT}, }; use halo2::{ @@ -13,28 +13,29 @@ use halo2::{ }; use pasta_curves::pallas; -#[derive(Clone)] +#[derive(Clone, Debug, Copy, Eq, PartialEq)] pub struct Config { // Selector used for fixed-base scalar mul with short signed exponent. q_mul_fixed_short: Selector, - q_mul_fixed_running_sum: Selector, - running_sum_config: RunningSumConfig, - super_config: super::Config, -} - -impl From<&EccConfig> for Config { - fn from(config: &EccConfig) -> Self { - Self { - q_mul_fixed_short: config.q_mul_fixed_short, - q_mul_fixed_running_sum: config.q_mul_fixed_running_sum, - running_sum_config: config.running_sum_config.clone(), - super_config: config.into(), - } - } + super_config: super::Config, } impl Config { - pub(crate) fn create_gate(&self, meta: &mut ConstraintSystem) { + pub(crate) fn configure( + meta: &mut ConstraintSystem, + super_config: super::Config, + ) -> Self { + let config = Self { + q_mul_fixed_short: meta.selector(), + super_config, + }; + + config.create_gate(meta); + + config + } + + fn create_gate(&self, meta: &mut ConstraintSystem) { meta.create_gate("Short fixed-base mul gate", |meta| { let q_mul_fixed_short = meta.query_selector(self.q_mul_fixed_short); let y_p = meta.query_advice(self.super_config.y_p, Rotation::cur()); @@ -46,7 +47,7 @@ impl Config { let one = Expression::Constant(pallas::Base::one()); // Check that last window is either 0 or 1. - let last_window_check = last_window.clone() * (one.clone() - last_window); + let last_window_check = bool_check(last_window); // Check that sign is either 1 or -1. let sign_check = sign.clone() * sign.clone() - one; @@ -73,15 +74,15 @@ impl Config { &self, region: &mut Region<'_, pallas::Base>, offset: usize, - magnitude_sign: (CellValue, CellValue), + magnitude_sign: MagnitudeSign, ) -> Result { let (magnitude, sign) = magnitude_sign; // Decompose magnitude - let running_sum = self.running_sum_config.copy_decompose( + let running_sum = self.super_config.running_sum_config.copy_decompose( region, offset, - magnitude, + magnitude.clone(), true, L_VALUE, NUM_WINDOWS_SHORT, @@ -97,7 +98,7 @@ impl Config { pub fn assign( &self, mut layouter: impl Layouter, - magnitude_sign: (CellValue, CellValue), + magnitude_sign: MagnitudeSign, base: &ValueCommitV, ) -> Result<(EccPoint, EccScalarFixedShort), Error> { let (scalar, acc, mul_b) = layouter.assign_region( @@ -106,14 +107,14 @@ impl Config { let offset = 0; // Decompose the scalar - let scalar = self.decompose(&mut region, offset, magnitude_sign)?; + let scalar = self.decompose(&mut region, offset, magnitude_sign.clone())?; - let (acc, mul_b) = self.super_config.assign_region_inner( + let (acc, mul_b) = self.super_config.assign_region_inner::( &mut region, offset, &(&scalar).into(), base.clone().into(), - self.q_mul_fixed_running_sum, + self.super_config.running_sum_config.q_range_check, )?; Ok((scalar, acc, mul_b)) @@ -127,8 +128,8 @@ impl Config { let offset = 0; // Add to the cumulative sum to get `[magnitude]B`. let magnitude_mul = self.super_config.add_config.assign_region( - &mul_b.into(), - &acc.into(), + &mul_b.clone().into(), + &acc.clone().into(), offset, &mut region, )?; @@ -137,32 +138,25 @@ impl Config { let offset = offset + 1; // Copy sign to `window` column - let sign = copy( - &mut region, + let sign = scalar.sign.copy_advice( || "sign", + &mut region, self.super_config.window, offset, - &scalar.sign, )?; // Copy last window to `u` column. // (Although the last window is not a `u` value; we are copying it into the `u` // column because there is an available cell there.) - let z_21 = scalar.running_sum[21]; - copy( - &mut region, - || "last_window", - self.super_config.u, - offset, - &z_21, - )?; + let z_21 = scalar.running_sum[21].clone(); + z_21.copy_advice(|| "last_window", &mut region, self.super_config.u, offset)?; // Conditionally negate `y`-coordinate let y_val = if let Some(sign) = sign.value() { - if sign == -pallas::Base::one() { - magnitude_mul.y.value().map(|y: pallas::Base| -y) + if sign == &-pallas::Base::one() { + magnitude_mul.y.value().cloned().map(|y: pallas::Base| -y) } else { - magnitude_mul.y.value() + magnitude_mul.y.value().cloned() } } else { None @@ -176,12 +170,12 @@ impl Config { || "y_var", self.super_config.y_p, offset, - || y_val.ok_or(Error::SynthesisError), + || y_val.ok_or(Error::Synthesis), )?; Ok(EccPoint { x: magnitude_mul.x, - y: CellValue::new(y_var, y_val), + y: y_var, }) }, )?; @@ -193,12 +187,10 @@ impl Config { // Invalid values result in constraint failures which are // tested at the circuit-level. { - use group::Curve; - use pasta_curves::arithmetic::FieldExt; + use group::{ff::PrimeField, Curve}; if let (Some(magnitude), Some(sign)) = (scalar.magnitude.value(), scalar.sign.value()) { - let magnitude_is_valid = - magnitude <= pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64); + let magnitude_is_valid = magnitude <= &pallas::Base::from(0xFFFF_FFFF_FFFF_FFFFu64); let sign_is_valid = sign * sign == pallas::Base::one(); if magnitude_is_valid && sign_is_valid { let base: super::OrchardFixedBases = base.clone().into(); @@ -207,10 +199,9 @@ impl Config { |(magnitude, sign)| { // Move magnitude from base field into scalar field (which always fits // for Pallas). - let magnitude = - pallas::Scalar::from_bytes(&magnitude.to_bytes()).unwrap(); + let magnitude = pallas::Scalar::from_repr(magnitude.to_repr()).unwrap(); - let sign = if sign == pallas::Base::one() { + let sign = if sign == &pallas::Base::one() { pallas::Scalar::one() } else { -pallas::Scalar::one() @@ -236,16 +227,20 @@ impl Config { #[cfg(test)] pub mod tests { - use group::Curve; + use group::{ff::PrimeField, Curve}; use halo2::{ - circuit::{Chip, Layouter}, + arithmetic::CurveAffine, + circuit::{AssignedCell, Chip, Layouter}, plonk::{Any, Error}, }; use pasta_curves::{arithmetic::FieldExt, pallas}; use crate::circuit::gadget::{ - ecc::{chip::EccChip, FixedPointShort, NonIdentityPoint, Point}, - utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions}, + ecc::{ + chip::{EccChip, MagnitudeSign}, + FixedPointShort, NonIdentityPoint, Point, + }, + utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions}, }; use crate::constants::load::ValueCommitV; @@ -264,7 +259,7 @@ pub mod tests { mut layouter: impl Layouter, magnitude: pallas::Base, sign: pallas::Base, - ) -> Result<(CellValue, CellValue), Error> { + ) -> Result { let column = chip.config().advices[0]; let magnitude = chip.load_private(layouter.namespace(|| "magnitude"), column, Some(magnitude))?; @@ -289,25 +284,21 @@ pub mod tests { } let magnitude_signs = [ - ( - "random [a]B", - pallas::Base::from_u64(rand::random::()), - { - let mut random_sign = pallas::Base::one(); - if rand::random::() { - random_sign = -random_sign; - } - random_sign - }, - ), + ("random [a]B", pallas::Base::from(rand::random::()), { + let mut random_sign = pallas::Base::one(); + if rand::random::() { + random_sign = -random_sign; + } + random_sign + }), ( "[2^64 - 1]B", - pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64), + pallas::Base::from(0xFFFF_FFFF_FFFF_FFFFu64), pallas::Base::one(), ), ( "-[2^64 - 1]B", - pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64), + pallas::Base::from(0xFFFF_FFFF_FFFF_FFFFu64), -pallas::Base::one(), ), // There is a single canonical sequence of window values for which a doubling occurs on the last step: @@ -315,12 +306,12 @@ pub mod tests { // [0xB6DB_6DB6_DB6D_B6DC] B ( "mul_with_double", - pallas::Base::from_u64(0xB6DB_6DB6_DB6D_B6DCu64), + pallas::Base::from(0xB6DB_6DB6_DB6D_B6DCu64), pallas::Base::one(), ), ( "mul_with_double negative", - pallas::Base::from_u64(0xB6DB_6DB6_DB6D_B6DCu64), + pallas::Base::from(0xB6DB_6DB6_DB6D_B6DCu64), -pallas::Base::one(), ), ]; @@ -337,7 +328,7 @@ pub mod tests { }; // Move from base field into scalar field let scalar = { - let magnitude = pallas::Scalar::from_bytes(&magnitude.to_bytes()).unwrap(); + let magnitude = pallas::Scalar::from_repr(magnitude.to_repr()).unwrap(); let sign = if *sign == pallas::Base::one() { pallas::Scalar::one() } else { @@ -369,7 +360,9 @@ pub mod tests { )?; value_commit_v.mul(layouter.namespace(|| *name), magnitude_sign)? }; - assert!(result.inner().is_identity().unwrap()); + if let Some(is_identity) = result.inner().is_identity() { + assert!(is_identity); + } } Ok(()) @@ -377,10 +370,7 @@ pub mod tests { #[test] fn invalid_magnitude_sign() { - use crate::circuit::gadget::{ - ecc::chip::EccConfig, - utilities::{CellValue, UtilitiesInstructions}, - }; + use crate::circuit::gadget::{ecc::chip::EccConfig, utilities::UtilitiesInstructions}; use halo2::{ circuit::{Layouter, SimpleFloorPlanner}, dev::{MockProver, VerifyFailure}, @@ -391,10 +381,12 @@ pub mod tests { struct MyCircuit { magnitude: Option, sign: Option, + // For test checking + magnitude_error: Option, } impl UtilitiesInstructions for MyCircuit { - type Var = CellValue; + type Var = AssignedCell; } impl Circuit for MyCircuit { @@ -445,7 +437,7 @@ pub mod tests { ) -> Result<(), Error> { let column = config.advices[0]; - let short_config: super::Config = (&config).into(); + let short_config = config.mul_fixed_short; let magnitude_sign = { let magnitude = self.load_private( layouter.namespace(|| "load magnitude"), @@ -463,6 +455,25 @@ pub mod tests { } } + // Copied from halo2::dev::util + fn format_value(v: pallas::Base) -> String { + use ff::Field; + if v.is_zero_vartime() { + "0".into() + } else if v == pallas::Base::one() { + "1".into() + } else if v == -pallas::Base::one() { + "-1".into() + } else { + // Format value as hex. + let s = format!("{:?}", v); + // Remove leading zeroes. + let s = s.strip_prefix("0x").unwrap(); + let s = s.trim_start_matches('0'); + format!("0x{}", s) + } + } + // Magnitude larger than 64 bits should fail { let circuits = [ @@ -470,31 +481,41 @@ pub mod tests { MyCircuit { magnitude: Some(pallas::Base::from_u128(1 << 64)), sign: Some(pallas::Base::one()), + magnitude_error: Some(pallas::Base::from(1 << 1)), }, // -2^64 MyCircuit { magnitude: Some(pallas::Base::from_u128(1 << 64)), sign: Some(-pallas::Base::one()), + magnitude_error: Some(pallas::Base::from(1 << 1)), }, // 2^66 MyCircuit { magnitude: Some(pallas::Base::from_u128(1 << 66)), sign: Some(pallas::Base::one()), + magnitude_error: Some(pallas::Base::from(1 << 3)), }, // -2^66 MyCircuit { magnitude: Some(pallas::Base::from_u128(1 << 66)), sign: Some(-pallas::Base::one()), + magnitude_error: Some(pallas::Base::from(1 << 3)), }, // 2^254 MyCircuit { magnitude: Some(pallas::Base::from_u128(1 << 127).square()), sign: Some(pallas::Base::one()), + magnitude_error: Some( + pallas::Base::from_u128(1 << 95).square() * pallas::Base::from(2), + ), }, // -2^254 MyCircuit { magnitude: Some(pallas::Base::from_u128(1 << 127).square()), sign: Some(-pallas::Base::one()), + magnitude_error: Some( + pallas::Base::from_u128(1 << 95).square() * pallas::Base::from(2), + ), }, ]; @@ -510,7 +531,11 @@ pub mod tests { "last_window_check" ) .into(), - row: 26 + row: 26, + cell_values: vec![( + ((Any::Advice, 5).into(), 0).into(), + format_value(circuit.magnitude_error.unwrap()), + )], }, VerifyFailure::Permutation { column: (Any::Fixed, 9).into(), @@ -527,9 +552,19 @@ pub mod tests { // Sign that is not +/- 1 should fail { + let magnitude_u64 = rand::random::(); let circuit = MyCircuit { - magnitude: Some(pallas::Base::from_u64(rand::random::())), + magnitude: Some(pallas::Base::from(magnitude_u64)), sign: Some(pallas::Base::zero()), + magnitude_error: None, + }; + + let negation_check_y = { + *(ValueCommitV::get().generator * pallas::Scalar::from(magnitude_u64)) + .to_affine() + .coordinates() + .unwrap() + .y() }; let prover = MockProver::::run(11, &circuit, vec![]).unwrap(); @@ -539,7 +574,8 @@ pub mod tests { VerifyFailure::ConstraintNotSatisfied { constraint: ((17, "Short fixed-base mul gate").into(), 1, "sign_check") .into(), - row: 26 + row: 26, + cell_values: vec![(((Any::Advice, 4).into(), 0).into(), "0".to_string())], }, VerifyFailure::ConstraintNotSatisfied { constraint: ( @@ -548,7 +584,18 @@ pub mod tests { "negation_check" ) .into(), - row: 26 + row: 26, + cell_values: vec![ + ( + ((Any::Advice, 1).into(), 0).into(), + format_value(negation_check_y), + ), + ( + ((Any::Advice, 3).into(), 0).into(), + format_value(negation_check_y), + ), + (((Any::Advice, 4).into(), 0).into(), "0".to_string()), + ], } ]) ); diff --git a/src/circuit/gadget/ecc/chip/witness_point.rs b/src/circuit/gadget/ecc/chip/witness_point.rs index c0a5caef..1623529c 100644 --- a/src/circuit/gadget/ecc/chip/witness_point.rs +++ b/src/circuit/gadget/ecc/chip/witness_point.rs @@ -1,15 +1,20 @@ -use super::{CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var}; +use super::{EccPoint, NonIdentityEccPoint}; use group::prime::PrimeCurveAffine; use halo2::{ - circuit::Region, + circuit::{AssignedCell, Region}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells}, poly::Rotation, }; use pasta_curves::{arithmetic::CurveAffine, pallas}; -#[derive(Clone, Debug)] +type Coordinates = ( + AssignedCell, + AssignedCell, +); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Config { q_point: Selector, q_point_non_id: Selector, @@ -19,19 +24,25 @@ pub struct Config { pub y: Column, } -impl From<&EccConfig> for Config { - fn from(ecc_config: &EccConfig) -> Self { - Self { - q_point: ecc_config.q_point, - q_point_non_id: ecc_config.q_point_non_id, - x: ecc_config.advices[0], - y: ecc_config.advices[1], - } - } -} - impl Config { - pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + pub(super) fn configure( + meta: &mut ConstraintSystem, + x: Column, + y: Column, + ) -> Self { + let config = Self { + q_point: meta.selector(), + q_point_non_id: meta.selector(), + x, + y, + }; + + config.create_gate(meta); + + config + } + + fn create_gate(&self, meta: &mut ConstraintSystem) { let curve_eqn = |meta: &mut VirtualCells| { let x = meta.query_advice(self.x, Rotation::cur()); let y = meta.query_advice(self.y, Rotation::cur()); @@ -70,29 +81,18 @@ impl Config { value: Option<(pallas::Base, pallas::Base)>, offset: usize, region: &mut Region<'_, pallas::Base>, - ) -> Result<(CellValue, CellValue), Error> { + ) -> Result { // Assign `x` value let x_val = value.map(|value| value.0); - let x_var = region.assign_advice( - || "x", - self.x, - offset, - || x_val.ok_or(Error::SynthesisError), - )?; + let x_var = + region.assign_advice(|| "x", self.x, offset, || x_val.ok_or(Error::Synthesis))?; // Assign `y` value let y_val = value.map(|value| value.1); - let y_var = region.assign_advice( - || "y", - self.y, - offset, - || y_val.ok_or(Error::SynthesisError), - )?; + let y_var = + region.assign_advice(|| "y", self.y, offset, || y_val.ok_or(Error::Synthesis))?; - Ok(( - CellValue::::new(x_var, x_val), - CellValue::::new(y_var, y_val), - )) + Ok((x_var, y_var)) } /// Assigns a point that can be the identity. @@ -132,7 +132,7 @@ impl Config { if let Some(value) = value { // Return an error if the point is the identity. if value == pallas::Affine::identity() { - return Err(Error::SynthesisError); + return Err(Error::Synthesis); } }; diff --git a/src/circuit/gadget/poseidon.rs b/src/circuit/gadget/poseidon.rs index 8e6cd7c0..92c8f409 100644 --- a/src/circuit/gadget/poseidon.rs +++ b/src/circuit/gadget/poseidon.rs @@ -1,25 +1,39 @@ //! Gadget and chips for the Poseidon algebraic hash function. use std::array; +use std::convert::TryInto; use std::fmt; +use std::marker::PhantomData; +use group::ff::Field; use halo2::{ arithmetic::FieldExt, - circuit::{Chip, Layouter}, + circuit::{AssignedCell, Chip, Layouter}, plonk::Error, }; -mod pow5t3; -pub use pow5t3::{Pow5T3Chip, Pow5T3Config, StateWord}; +mod pow5; +pub use pow5::{Pow5Chip, Pow5Config, StateWord}; -use crate::primitives::poseidon::{ConstantLength, Domain, Spec, Sponge, SpongeState, State}; +use crate::primitives::poseidon::{ + Absorbing, ConstantLength, Domain, Spec, SpongeMode, Squeezing, State, +}; + +/// A word from the padded input to a Poseidon sponge. +#[derive(Clone, Debug)] +pub enum PaddedWord { + /// A message word provided by the prover. + Message(AssignedCell), + /// A padding word, that will be fixed in the circuit parameters. + Padding(F), +} /// The set of circuit instructions required to use the Poseidon permutation. pub trait PoseidonInstructions, const T: usize, const RATE: usize>: Chip { /// Variable representing the word over which the Poseidon permutation operates. - type Word: Copy + fmt::Debug; + type Word: Clone + fmt::Debug + From> + Into>; /// Applies the Poseidon permutation to the given state. fn permute( @@ -29,37 +43,35 @@ pub trait PoseidonInstructions, const T: usize, ) -> Result, Error>; } -/// The set of circuit instructions required to use the [`Duplex`] and [`Hash`] gadgets. +/// The set of circuit instructions required to use the [`Sponge`] and [`Hash`] gadgets. /// /// [`Hash`]: self::Hash -pub trait PoseidonDuplexInstructions< +pub trait PoseidonSpongeInstructions< F: FieldExt, S: Spec, + D: Domain, const T: usize, const RATE: usize, >: PoseidonInstructions { /// Returns the initial empty state for the given domain. - fn initial_state( - &self, - layouter: &mut impl Layouter, - domain: &impl Domain, - ) -> Result, Error>; + fn initial_state(&self, layouter: &mut impl Layouter) + -> Result, Error>; - /// Pads the given input (according to the specified domain) and adds it to the state. - fn pad_and_add( + /// Adds the given input to the state. + fn add_input( &self, layouter: &mut impl Layouter, - domain: &impl Domain, initial_state: &State, - input: &SpongeState, + input: &Absorbing, RATE>, ) -> Result, Error>; /// Extracts sponge output from the given state. - fn get_output(state: &State) -> SpongeState; + fn get_output(state: &State) -> Squeezing; } /// A word over which the Poseidon permutation operates. +#[derive(Debug)] pub struct Word< F: FieldExt, PoseidonChip: PoseidonInstructions, @@ -78,168 +90,187 @@ impl< const RATE: usize, > Word { - pub(crate) fn inner(&self) -> PoseidonChip::Word { - self.inner + /// The word contained in this gadget. + pub fn inner(&self) -> PoseidonChip::Word { + self.inner.clone() } - pub(crate) fn from_inner(inner: PoseidonChip::Word) -> Self { + /// Construct a [`Word`] gadget from the inner word. + pub fn from_inner(inner: PoseidonChip::Word) -> Self { Self { inner } } } -fn poseidon_duplex< +fn poseidon_sponge< F: FieldExt, - PoseidonChip: PoseidonDuplexInstructions, + PoseidonChip: PoseidonSpongeInstructions, S: Spec, - D: Domain, + D: Domain, const T: usize, const RATE: usize, >( chip: &PoseidonChip, mut layouter: impl Layouter, - domain: &D, state: &mut State, - input: &SpongeState, -) -> Result, Error> { - *state = chip.pad_and_add(&mut layouter, domain, state, input)?; + input: Option<&Absorbing, RATE>>, +) -> Result, Error> { + if let Some(input) = input { + *state = chip.add_input(&mut layouter, state, input)?; + } *state = chip.permute(&mut layouter, state)?; Ok(PoseidonChip::get_output(state)) } -/// A Poseidon duplex sponge. -pub struct Duplex< +/// A Poseidon sponge. +#[derive(Debug)] +pub struct Sponge< F: FieldExt, - PoseidonChip: PoseidonDuplexInstructions, + PoseidonChip: PoseidonSpongeInstructions, S: Spec, - D: Domain, + M: SpongeMode, + D: Domain, const T: usize, const RATE: usize, > { chip: PoseidonChip, - sponge: Sponge, + mode: M, state: State, - domain: D, + _marker: PhantomData, } impl< F: FieldExt, - PoseidonChip: PoseidonDuplexInstructions, + PoseidonChip: PoseidonSpongeInstructions, S: Spec, - D: Domain, + D: Domain, const T: usize, const RATE: usize, - > Duplex + > Sponge, RATE>, D, T, RATE> { /// Constructs a new duplex sponge for the given Poseidon specification. - pub fn new( - chip: PoseidonChip, - mut layouter: impl Layouter, - domain: D, - ) -> Result { - chip.initial_state(&mut layouter, &domain) - .map(|state| Duplex { - chip, - sponge: Sponge::Absorbing([None; RATE]), - state, - domain, - }) + pub fn new(chip: PoseidonChip, mut layouter: impl Layouter) -> Result { + chip.initial_state(&mut layouter).map(|state| Sponge { + chip, + mode: Absorbing( + (0..RATE) + .map(|_| None) + .collect::>() + .try_into() + .unwrap(), + ), + state, + _marker: PhantomData::default(), + }) } /// Absorbs an element into the sponge. pub fn absorb( &mut self, mut layouter: impl Layouter, - value: Word, + value: PaddedWord, ) -> Result<(), Error> { - match self.sponge { - Sponge::Absorbing(ref mut input) => { - for entry in input.iter_mut() { - if entry.is_none() { - *entry = Some(value.inner); - return Ok(()); - } - } - - // We've already absorbed as many elements as we can - let _ = poseidon_duplex( - &self.chip, - layouter.namespace(|| "PoseidonDuplex"), - &self.domain, - &mut self.state, - input, - )?; - self.sponge = Sponge::absorb(value.inner); - } - Sponge::Squeezing(_) => { - // Drop the remaining output elements - self.sponge = Sponge::absorb(value.inner); + for entry in self.mode.0.iter_mut() { + if entry.is_none() { + *entry = Some(value); + return Ok(()); } } + // We've already absorbed as many elements as we can + let _ = poseidon_sponge( + &self.chip, + layouter.namespace(|| "PoseidonSponge"), + &mut self.state, + Some(&self.mode), + )?; + self.mode = Absorbing::init_with(value); + Ok(()) } - /// Squeezes an element from the sponge. - pub fn squeeze( - &mut self, + /// Transitions the sponge into its squeezing state. + #[allow(clippy::type_complexity)] + pub fn finish_absorbing( + mut self, mut layouter: impl Layouter, - ) -> Result, Error> { - loop { - match self.sponge { - Sponge::Absorbing(ref input) => { - self.sponge = Sponge::Squeezing(poseidon_duplex( - &self.chip, - layouter.namespace(|| "PoseidonDuplex"), - &self.domain, - &mut self.state, - input, - )?); - } - Sponge::Squeezing(ref mut output) => { - for entry in output.iter_mut() { - if let Some(inner) = entry.take() { - return Ok(Word { inner }); - } - } + ) -> Result, D, T, RATE>, Error> + { + let mode = poseidon_sponge( + &self.chip, + layouter.namespace(|| "PoseidonSponge"), + &mut self.state, + Some(&self.mode), + )?; - // We've already squeezed out all available elements - self.sponge = Sponge::Absorbing([None; RATE]); - } - } - } + Ok(Sponge { + chip: self.chip, + mode, + state: self.state, + _marker: PhantomData::default(), + }) } } -/// A Poseidon hash function, built around a duplex sponge. -pub struct Hash< - F: FieldExt, - PoseidonChip: PoseidonDuplexInstructions, - S: Spec, - D: Domain, - const T: usize, - const RATE: usize, -> { - duplex: Duplex, -} - impl< F: FieldExt, - PoseidonChip: PoseidonDuplexInstructions, + PoseidonChip: PoseidonSpongeInstructions, S: Spec, - D: Domain, + D: Domain, + const T: usize, + const RATE: usize, + > Sponge, D, T, RATE> +{ + /// Squeezes an element from the sponge. + pub fn squeeze(&mut self, mut layouter: impl Layouter) -> Result, Error> { + loop { + for entry in self.mode.0.iter_mut() { + if let Some(inner) = entry.take() { + return Ok(inner.into()); + } + } + + // We've already squeezed out all available elements + self.mode = poseidon_sponge( + &self.chip, + layouter.namespace(|| "PoseidonSponge"), + &mut self.state, + None, + )?; + } + } +} + +/// A Poseidon hash function, built around a sponge. +#[derive(Debug)] +pub struct Hash< + F: FieldExt, + PoseidonChip: PoseidonSpongeInstructions, + S: Spec, + D: Domain, + const T: usize, + const RATE: usize, +> { + sponge: Sponge, RATE>, D, T, RATE>, +} + +impl< + F: FieldExt, + PoseidonChip: PoseidonSpongeInstructions, + S: Spec, + D: Domain, const T: usize, const RATE: usize, > Hash { /// Initializes a new hasher. - pub fn init(chip: PoseidonChip, layouter: impl Layouter, domain: D) -> Result { - Duplex::new(chip, layouter, domain).map(|duplex| Hash { duplex }) + pub fn init(chip: PoseidonChip, layouter: impl Layouter) -> Result { + Sponge::new(chip, layouter).map(|sponge| Hash { sponge }) } } impl< F: FieldExt, - PoseidonChip: PoseidonDuplexInstructions, + PoseidonChip: PoseidonSpongeInstructions, T, RATE>, S: Spec, const T: usize, const RATE: usize, @@ -250,12 +281,18 @@ impl< pub fn hash( mut self, mut layouter: impl Layouter, - message: [Word; L], - ) -> Result, Error> { - for (i, value) in array::IntoIter::new(message).enumerate() { - self.duplex + message: [AssignedCell; L], + ) -> Result, Error> { + for (i, value) in array::IntoIter::new(message) + .map(PaddedWord::Message) + .chain( as Domain>::padding(L).map(PaddedWord::Padding)) + .enumerate() + { + self.sponge .absorb(layouter.namespace(|| format!("absorb_{}", i)), value)?; } - self.duplex.squeeze(layouter.namespace(|| "squeeze")) + self.sponge + .finish_absorbing(layouter.namespace(|| "finish absorbing"))? + .squeeze(layouter.namespace(|| "squeeze")) } } diff --git a/src/circuit/gadget/poseidon/pow5.rs b/src/circuit/gadget/poseidon/pow5.rs new file mode 100644 index 00000000..3eba900d --- /dev/null +++ b/src/circuit/gadget/poseidon/pow5.rs @@ -0,0 +1,886 @@ +use std::convert::TryInto; +use std::iter; + +use halo2::{ + arithmetic::FieldExt, + circuit::{AssignedCell, Cell, Chip, Layouter, Region}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector}, + poly::Rotation, +}; + +use super::{PaddedWord, PoseidonInstructions, PoseidonSpongeInstructions}; +use crate::primitives::poseidon::{Domain, Mds, Spec, State}; +use crate::{ + circuit::gadget::utilities::Var, + primitives::poseidon::{Absorbing, Squeezing}, +}; + +/// Configuration for a [`Pow5Chip`]. +#[derive(Clone, Debug)] +pub struct Pow5Config { + pub(in crate::circuit) state: [Column; WIDTH], + partial_sbox: Column, + rc_a: [Column; WIDTH], + rc_b: [Column; WIDTH], + s_full: Selector, + s_partial: Selector, + s_pad_and_add: Selector, + + half_full_rounds: usize, + half_partial_rounds: usize, + alpha: [u64; 4], + round_constants: Vec<[F; WIDTH]>, + m_reg: Mds, + m_inv: Mds, +} + +/// A Poseidon chip using an $x^5$ S-Box. +/// +/// The chip is implemented using a single round per row for full rounds, and two rounds +/// per row for partial rounds. +#[derive(Debug)] +pub struct Pow5Chip { + config: Pow5Config, +} + +impl Pow5Chip { + /// Configures this chip for use in a circuit. + /// + /// # Side-effects + /// + /// All columns in `state` will be equality-enabled. + // + // TODO: Does the rate need to be hard-coded here, or only the width? It probably + // needs to be known wherever we implement the hashing gadget, but it isn't strictly + // necessary for the permutation. + pub fn configure>( + meta: &mut ConstraintSystem, + state: [Column; WIDTH], + partial_sbox: Column, + rc_a: [Column; WIDTH], + rc_b: [Column; WIDTH], + ) -> Pow5Config { + assert_eq!(RATE, WIDTH - 1); + // Generate constants for the Poseidon permutation. + // This gadget requires R_F and R_P to be even. + assert!(S::full_rounds() & 1 == 0); + assert!(S::partial_rounds() & 1 == 0); + let half_full_rounds = S::full_rounds() / 2; + let half_partial_rounds = S::partial_rounds() / 2; + let (round_constants, m_reg, m_inv) = S::constants(); + + // This allows state words to be initialized (by constraining them equal to fixed + // values), and used in a permutation from an arbitrary region. rc_a is used in + // every permutation round, while rc_b is empty in the initial and final full + // rounds, so we use rc_b as "scratch space" for fixed values (enabling potential + // layouter optimisations). + for column in iter::empty() + .chain(state.iter().cloned().map(|c| c.into())) + .chain(rc_b.iter().cloned().map(|c| c.into())) + { + meta.enable_equality(column); + } + + let s_full = meta.selector(); + let s_partial = meta.selector(); + let s_pad_and_add = meta.selector(); + + let alpha = [5, 0, 0, 0]; + let pow_5 = |v: Expression| { + let v2 = v.clone() * v.clone(); + v2.clone() * v2 * v + }; + + meta.create_gate("full round", |meta| { + let s_full = meta.query_selector(s_full); + + (0..WIDTH) + .map(|next_idx| { + let state_next = meta.query_advice(state[next_idx], Rotation::next()); + let expr = (0..WIDTH) + .map(|idx| { + let state_cur = meta.query_advice(state[idx], Rotation::cur()); + let rc_a = meta.query_fixed(rc_a[idx], Rotation::cur()); + pow_5(state_cur + rc_a) * m_reg[next_idx][idx] + }) + .reduce(|acc, term| acc + term) + .expect("WIDTH > 0"); + s_full.clone() * (expr - state_next) + }) + .collect::>() + }); + + meta.create_gate("partial rounds", |meta| { + let cur_0 = meta.query_advice(state[0], Rotation::cur()); + let mid_0 = meta.query_advice(partial_sbox, Rotation::cur()); + + let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur()); + let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur()); + + let s_partial = meta.query_selector(s_partial); + + use halo2::plonk::VirtualCells; + let mid = |idx: usize, meta: &mut VirtualCells| { + let mid = mid_0.clone() * m_reg[idx][0]; + (1..WIDTH).fold(mid, |acc, cur_idx| { + let cur = meta.query_advice(state[cur_idx], Rotation::cur()); + let rc_a = meta.query_fixed(rc_a[cur_idx], Rotation::cur()); + acc + (cur + rc_a) * m_reg[idx][cur_idx] + }) + }; + + let next = |idx: usize, meta: &mut VirtualCells| { + (0..WIDTH) + .map(|next_idx| { + let next = meta.query_advice(state[next_idx], Rotation::next()); + next * m_inv[idx][next_idx] + }) + .reduce(|acc, next| acc + next) + .expect("WIDTH > 0") + }; + + let partial_round_linear = |idx: usize, meta: &mut VirtualCells| { + let expr = { + let rc_b = meta.query_fixed(rc_b[idx], Rotation::cur()); + mid(idx, meta) + rc_b - next(idx, meta) + }; + s_partial.clone() * expr + }; + + std::iter::empty() + // state[0] round a + .chain(Some( + s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()), + )) + // state[0] round b + .chain(Some( + s_partial.clone() * (pow_5(mid(0, meta) + rc_b0) - next(0, meta)), + )) + .chain((1..WIDTH).map(|idx| partial_round_linear(idx, meta))) + .collect::>() + }); + + meta.create_gate("pad-and-add", |meta| { + let initial_state_rate = meta.query_advice(state[RATE], Rotation::prev()); + let output_state_rate = meta.query_advice(state[RATE], Rotation::next()); + + let s_pad_and_add = meta.query_selector(s_pad_and_add); + + let pad_and_add = |idx: usize| { + let initial_state = meta.query_advice(state[idx], Rotation::prev()); + let input = meta.query_advice(state[idx], Rotation::cur()); + let output_state = meta.query_advice(state[idx], Rotation::next()); + + // We pad the input by storing the required padding in fixed columns and + // then constraining the corresponding input columns to be equal to it. + s_pad_and_add.clone() * (initial_state + input - output_state) + }; + + (0..RATE) + .map(pad_and_add) + // The capacity element is never altered by the input. + .chain(Some( + s_pad_and_add.clone() * (initial_state_rate - output_state_rate), + )) + .collect::>() + }); + + Pow5Config { + state, + partial_sbox, + rc_a, + rc_b, + s_full, + s_partial, + s_pad_and_add, + half_full_rounds, + half_partial_rounds, + alpha, + round_constants, + m_reg, + m_inv, + } + } + + /// Construct a [`Pow5Chip`]. + pub fn construct(config: Pow5Config) -> Self { + Pow5Chip { config } + } +} + +impl Chip for Pow5Chip { + type Config = Pow5Config; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + +impl, const WIDTH: usize, const RATE: usize> + PoseidonInstructions for Pow5Chip +{ + type Word = StateWord; + + fn permute( + &self, + layouter: &mut impl Layouter, + initial_state: &State, + ) -> Result, Error> { + let config = self.config(); + + layouter.assign_region( + || "permute state", + |mut region| { + // Load the initial state into this region. + let state = Pow5State::load(&mut region, config, initial_state)?; + + let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| { + res.and_then(|state| state.full_round(&mut region, config, r, r)) + })?; + + let state = (0..config.half_partial_rounds).fold(Ok(state), |res, r| { + res.and_then(|state| { + state.partial_round( + &mut region, + config, + config.half_full_rounds + 2 * r, + config.half_full_rounds + r, + ) + }) + })?; + + let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| { + res.and_then(|state| { + state.full_round( + &mut region, + config, + config.half_full_rounds + 2 * config.half_partial_rounds + r, + config.half_full_rounds + config.half_partial_rounds + r, + ) + }) + })?; + + Ok(state.0) + }, + ) + } +} + +impl< + F: FieldExt, + S: Spec, + D: Domain, + const WIDTH: usize, + const RATE: usize, + > PoseidonSpongeInstructions for Pow5Chip +{ + fn initial_state( + &self, + layouter: &mut impl Layouter, + ) -> Result, Error> { + let config = self.config(); + let state = layouter.assign_region( + || format!("initial state for domain {}", D::name()), + |mut region| { + let mut state = Vec::with_capacity(WIDTH); + let mut load_state_word = |i: usize, value: F| -> Result<_, Error> { + let var = region.assign_advice_from_constant( + || format!("state_{}", i), + config.state[i], + 0, + value, + )?; + state.push(StateWord(var)); + + Ok(()) + }; + + for i in 0..RATE { + load_state_word(i, F::zero())?; + } + load_state_word(RATE, D::initial_capacity_element())?; + + Ok(state) + }, + )?; + + Ok(state.try_into().unwrap()) + } + + fn add_input( + &self, + layouter: &mut impl Layouter, + initial_state: &State, + input: &Absorbing, RATE>, + ) -> Result, Error> { + let config = self.config(); + layouter.assign_region( + || format!("add input for domain {}", D::name()), + |mut region| { + config.s_pad_and_add.enable(&mut region, 1)?; + + // Load the initial state into this region. + let load_state_word = |i: usize| { + initial_state[i] + .0 + .copy_advice( + || format!("load state_{}", i), + &mut region, + config.state[i], + 0, + ) + .map(StateWord) + }; + let initial_state: Result, Error> = + (0..WIDTH).map(load_state_word).collect(); + let initial_state = initial_state?; + + // Load the input into this region. + let load_input_word = |i: usize| { + let constraint_var = match input.0[i].clone() { + Some(PaddedWord::Message(word)) => word, + Some(PaddedWord::Padding(padding_value)) => region.assign_fixed( + || format!("load pad_{}", i), + config.rc_b[i], + 1, + || Ok(padding_value), + )?, + _ => panic!("Input is not padded"), + }; + constraint_var + .copy_advice( + || format!("load input_{}", i), + &mut region, + config.state[i], + 1, + ) + .map(StateWord) + }; + let input: Result, Error> = (0..RATE).map(load_input_word).collect(); + let input = input?; + + // Constrain the output. + let constrain_output_word = |i: usize| { + let value = initial_state[i].0.value().and_then(|initial_word| { + input + .get(i) + .map(|word| word.0.value().cloned()) + // The capacity element is never altered by the input. + .unwrap_or_else(|| Some(F::zero())) + .map(|input_word| *initial_word + input_word) + }); + region + .assign_advice( + || format!("load output_{}", i), + config.state[i], + 2, + || value.ok_or(Error::Synthesis), + ) + .map(StateWord) + }; + + let output: Result, Error> = (0..WIDTH).map(constrain_output_word).collect(); + output.map(|output| output.try_into().unwrap()) + }, + ) + } + + fn get_output(state: &State) -> Squeezing { + Squeezing( + state[..RATE] + .iter() + .map(|word| Some(word.clone())) + .collect::>() + .try_into() + .unwrap(), + ) + } +} + +/// A word in the Poseidon state. +#[derive(Clone, Debug)] +pub struct StateWord(AssignedCell); + +impl From> for AssignedCell { + fn from(state_word: StateWord) -> AssignedCell { + state_word.0 + } +} + +impl From> for StateWord { + fn from(cell_value: AssignedCell) -> StateWord { + StateWord(cell_value) + } +} + +impl Var for StateWord { + fn cell(&self) -> Cell { + self.0.cell() + } + + fn value(&self) -> Option { + self.0.value().cloned() + } +} + +#[derive(Debug)] +struct Pow5State([StateWord; WIDTH]); + +impl Pow5State { + fn full_round( + self, + region: &mut Region, + config: &Pow5Config, + round: usize, + offset: usize, + ) -> Result { + Self::round(region, config, round, offset, config.s_full, |_| { + let q = self.0.iter().enumerate().map(|(idx, word)| { + word.0 + .value() + .map(|v| *v + config.round_constants[round][idx]) + }); + let r: Option> = q.map(|q| q.map(|q| q.pow(&config.alpha))).collect(); + let m = &config.m_reg; + let state = m.iter().map(|m_i| { + r.as_ref().map(|r| { + r.iter() + .enumerate() + .fold(F::zero(), |acc, (j, r_j)| acc + m_i[j] * r_j) + }) + }); + + Ok((round + 1, state.collect::>().try_into().unwrap())) + }) + } + + fn partial_round( + self, + region: &mut Region, + config: &Pow5Config, + round: usize, + offset: usize, + ) -> Result { + Self::round(region, config, round, offset, config.s_partial, |region| { + let m = &config.m_reg; + let p: Option> = self.0.iter().map(|word| word.0.value().cloned()).collect(); + + let r: Option> = p.map(|p| { + let r_0 = (p[0] + config.round_constants[round][0]).pow(&config.alpha); + let r_i = p[1..] + .iter() + .enumerate() + .map(|(i, p_i)| *p_i + config.round_constants[round][i + 1]); + std::iter::empty().chain(Some(r_0)).chain(r_i).collect() + }); + + region.assign_advice( + || format!("round_{} partial_sbox", round), + config.partial_sbox, + offset, + || r.as_ref().map(|r| r[0]).ok_or(Error::Synthesis), + )?; + + let p_mid: Option> = m + .iter() + .map(|m_i| { + r.as_ref().map(|r| { + m_i.iter() + .zip(r.iter()) + .fold(F::zero(), |acc, (m_ij, r_j)| acc + *m_ij * r_j) + }) + }) + .collect(); + + // Load the second round constants. + let mut load_round_constant = |i: usize| { + region.assign_fixed( + || format!("round_{} rc_{}", round + 1, i), + config.rc_b[i], + offset, + || Ok(config.round_constants[round + 1][i]), + ) + }; + for i in 0..WIDTH { + load_round_constant(i)?; + } + + let r_mid: Option> = p_mid.map(|p| { + let r_0 = (p[0] + config.round_constants[round + 1][0]).pow(&config.alpha); + let r_i = p[1..] + .iter() + .enumerate() + .map(|(i, p_i)| *p_i + config.round_constants[round + 1][i + 1]); + std::iter::empty().chain(Some(r_0)).chain(r_i).collect() + }); + + let state: Vec> = m + .iter() + .map(|m_i| { + r_mid.as_ref().map(|r| { + m_i.iter() + .zip(r.iter()) + .fold(F::zero(), |acc, (m_ij, r_j)| acc + *m_ij * r_j) + }) + }) + .collect(); + + Ok((round + 2, state.try_into().unwrap())) + }) + } + + fn load( + region: &mut Region, + config: &Pow5Config, + initial_state: &State, WIDTH>, + ) -> Result { + let load_state_word = |i: usize| { + initial_state[i] + .0 + .copy_advice(|| format!("load state_{}", i), region, config.state[i], 0) + .map(StateWord) + }; + + let state: Result, _> = (0..WIDTH).map(load_state_word).collect(); + state.map(|state| Pow5State(state.try_into().unwrap())) + } + + fn round( + region: &mut Region, + config: &Pow5Config, + round: usize, + offset: usize, + round_gate: Selector, + round_fn: impl FnOnce(&mut Region) -> Result<(usize, [Option; WIDTH]), Error>, + ) -> Result { + // Enable the required gate. + round_gate.enable(region, offset)?; + + // Load the round constants. + let mut load_round_constant = |i: usize| { + region.assign_fixed( + || format!("round_{} rc_{}", round, i), + config.rc_a[i], + offset, + || Ok(config.round_constants[round][i]), + ) + }; + for i in 0..WIDTH { + load_round_constant(i)?; + } + + // Compute the next round's state. + let (next_round, next_state) = round_fn(region)?; + + let next_state_word = |i: usize| { + let value = next_state[i]; + let var = region.assign_advice( + || format!("round_{} state_{}", next_round, i), + config.state[i], + offset + 1, + || value.ok_or(Error::Synthesis), + )?; + Ok(StateWord(var)) + }; + + let next_state: Result, _> = (0..WIDTH).map(next_state_word).collect(); + next_state.map(|next_state| Pow5State(next_state.try_into().unwrap())) + } +} + +#[cfg(test)] +mod tests { + use ff::PrimeField; + use halo2::{ + arithmetic::FieldExt, + circuit::{Layouter, SimpleFloorPlanner}, + dev::MockProver, + pasta::Fp, + plonk::{Circuit, ConstraintSystem, Error}, + }; + use pasta_curves::pallas; + + use super::{PoseidonInstructions, Pow5Chip, Pow5Config, StateWord}; + use crate::{ + circuit::gadget::poseidon::Hash, + primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec}, + }; + use std::convert::TryInto; + use std::marker::PhantomData; + + struct PermuteCircuit, const WIDTH: usize, const RATE: usize>( + PhantomData, + ); + + impl, const WIDTH: usize, const RATE: usize> Circuit + for PermuteCircuit + { + type Config = Pow5Config; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + PermuteCircuit::(PhantomData) + } + + fn configure(meta: &mut ConstraintSystem) -> Pow5Config { + let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); + let partial_sbox = meta.advice_column(); + + let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + + Pow5Chip::configure::( + meta, + state.try_into().unwrap(), + partial_sbox, + rc_a.try_into().unwrap(), + rc_b.try_into().unwrap(), + ) + } + + fn synthesize( + &self, + config: Pow5Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let initial_state = layouter.assign_region( + || "prepare initial state", + |mut region| { + let state_word = |i: usize| { + let value = Some(Fp::from(i as u64)); + let var = region.assign_advice( + || format!("load state_{}", i), + config.state[i], + 0, + || value.ok_or(Error::Synthesis), + )?; + Ok(StateWord(var)) + }; + + let state: Result, Error> = (0..WIDTH).map(state_word).collect(); + Ok(state?.try_into().unwrap()) + }, + )?; + + let chip = Pow5Chip::construct(config.clone()); + let final_state = as PoseidonInstructions< + Fp, + S, + WIDTH, + RATE, + >>::permute(&chip, &mut layouter, &initial_state)?; + + // For the purpose of this test, compute the real final state inline. + let mut expected_final_state = (0..WIDTH) + .map(|idx| Fp::from(idx as u64)) + .collect::>() + .try_into() + .unwrap(); + let (round_constants, mds, _) = S::constants(); + poseidon::permute::<_, S, WIDTH, RATE>( + &mut expected_final_state, + &mds, + &round_constants, + ); + + layouter.assign_region( + || "constrain final state", + |mut region| { + let mut final_state_word = |i: usize| { + let var = region.assign_advice( + || format!("load final_state_{}", i), + config.state[i], + 0, + || Ok(expected_final_state[i]), + )?; + region.constrain_equal(final_state[i].0.cell(), var.cell()) + }; + + for i in 0..(WIDTH) { + final_state_word(i)?; + } + + Ok(()) + }, + ) + } + } + + #[test] + fn poseidon_permute() { + let k = 6; + let circuit = PermuteCircuit::(PhantomData); + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())) + } + + struct HashCircuit< + S: Spec, + const WIDTH: usize, + const RATE: usize, + const L: usize, + > { + message: Option<[Fp; L]>, + // For the purpose of this test, witness the result. + // TODO: Move this into an instance column. + output: Option, + _spec: PhantomData, + } + + impl, const WIDTH: usize, const RATE: usize, const L: usize> + Circuit for HashCircuit + { + type Config = Pow5Config; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self { + message: None, + output: None, + _spec: PhantomData, + } + } + + fn configure(meta: &mut ConstraintSystem) -> Pow5Config { + let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); + let partial_sbox = meta.advice_column(); + + let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + + meta.enable_constant(rc_b[0]); + + Pow5Chip::configure::( + meta, + state.try_into().unwrap(), + partial_sbox, + rc_a.try_into().unwrap(), + rc_b.try_into().unwrap(), + ) + } + + fn synthesize( + &self, + config: Pow5Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let chip = Pow5Chip::construct(config.clone()); + + let message = layouter.assign_region( + || "load message", + |mut region| { + let message_word = |i: usize| { + let value = self.message.map(|message_vals| message_vals[i]); + region.assign_advice( + || format!("load message_{}", i), + config.state[i], + 0, + || value.ok_or(Error::Synthesis), + ) + }; + + let message: Result, Error> = (0..L).map(message_word).collect(); + Ok(message?.try_into().unwrap()) + }, + )?; + + let hasher = Hash::<_, _, S, ConstantLength, WIDTH, RATE>::init( + chip, + layouter.namespace(|| "init"), + )?; + let output = hasher.hash(layouter.namespace(|| "hash"), message)?; + + layouter.assign_region( + || "constrain output", + |mut region| { + let expected_var = region.assign_advice( + || "load output", + config.state[0], + 0, + || self.output.ok_or(Error::Synthesis), + )?; + region.constrain_equal(output.cell(), expected_var.cell()) + }, + ) + } + } + + #[test] + fn poseidon_hash() { + let message = [Fp::rand(), Fp::rand()]; + let output = + poseidon::Hash::<_, OrchardNullifier, ConstantLength<2>, 3, 2>::init().hash(message); + + let k = 6; + let circuit = HashCircuit:: { + message: Some(message), + output: Some(output), + _spec: PhantomData, + }; + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())) + } + + #[test] + fn poseidon_hash_longer_input() { + let message = [Fp::rand(), Fp::rand(), Fp::rand()]; + let output = + poseidon::Hash::<_, OrchardNullifier, ConstantLength<3>, 3, 2>::init().hash(message); + + let k = 7; + let circuit = HashCircuit:: { + message: Some(message), + output: Some(output), + _spec: PhantomData, + }; + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())) + } + + #[test] + fn hash_test_vectors() { + for tv in crate::primitives::poseidon::test_vectors::fp::hash() { + let message = [ + pallas::Base::from_repr(tv.input[0]).unwrap(), + pallas::Base::from_repr(tv.input[1]).unwrap(), + ]; + let output = poseidon::Hash::<_, OrchardNullifier, ConstantLength<2>, 3, 2>::init() + .hash(message); + + let k = 6; + let circuit = HashCircuit:: { + message: Some(message), + output: Some(output), + _spec: PhantomData, + }; + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + } + + #[cfg(feature = "dev-graph")] + #[test] + fn print_poseidon_chip() { + use plotters::prelude::*; + + let root = BitMapBackend::new("poseidon-chip-layout.png", (1024, 768)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root + .titled("Poseidon Chip Layout", ("sans-serif", 60)) + .unwrap(); + + let circuit = HashCircuit:: { + message: None, + output: None, + _spec: PhantomData, + }; + halo2::dev::CircuitLayout::default() + .render(6, &circuit, &root) + .unwrap(); + } +} diff --git a/src/circuit/gadget/poseidon/pow5t3.rs b/src/circuit/gadget/poseidon/pow5t3.rs deleted file mode 100644 index e07504a3..00000000 --- a/src/circuit/gadget/poseidon/pow5t3.rs +++ /dev/null @@ -1,872 +0,0 @@ -use std::iter; - -use halo2::{ - arithmetic::FieldExt, - circuit::{Cell, Chip, Layouter, Region}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector}, - poly::Rotation, -}; - -use super::{PoseidonDuplexInstructions, PoseidonInstructions}; -use crate::circuit::gadget::utilities::{CellValue, Var}; -use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State}; - -const WIDTH: usize = 3; - -/// Configuration for an [`Pow5T3Chip`]. -#[derive(Clone, Debug)] -pub struct Pow5T3Config { - pub(in crate::circuit) state: [Column; WIDTH], - partial_sbox: Column, - rc_a: [Column; WIDTH], - rc_b: [Column; WIDTH], - s_full: Selector, - s_partial: Selector, - s_pad_and_add: Selector, - - half_full_rounds: usize, - half_partial_rounds: usize, - alpha: [u64; 4], - round_constants: Vec<[F; WIDTH]>, - m_reg: Mds, - m_inv: Mds, -} - -/// A Poseidon chip using an $x^5$ S-Box, with a width of 3, suitable for a 2:1 reduction. -#[derive(Debug)] -pub struct Pow5T3Chip { - config: Pow5T3Config, -} - -impl Pow5T3Chip { - /// Configures this chip for use in a circuit. - /// - /// # Side-effects - /// - /// All columns in `state` will be equality-enabled. - // - // TODO: Does the rate need to be hard-coded here, or only the width? It probably - // needs to be known wherever we implement the hashing gadget, but it isn't strictly - // necessary for the permutation. - pub fn configure>( - meta: &mut ConstraintSystem, - spec: S, - state: [Column; WIDTH], - partial_sbox: Column, - rc_a: [Column; WIDTH], - rc_b: [Column; WIDTH], - ) -> Pow5T3Config { - // Generate constants for the Poseidon permutation. - // This gadget requires R_F and R_P to be even. - assert!(S::full_rounds() & 1 == 0); - assert!(S::partial_rounds() & 1 == 0); - let half_full_rounds = S::full_rounds() / 2; - let half_partial_rounds = S::partial_rounds() / 2; - let (round_constants, m_reg, m_inv) = spec.constants(); - - // This allows state words to be initialized (by constraining them equal to fixed - // values), and used in a permutation from an arbitrary region. rc_a is used in - // every permutation round, while rc_b is empty in the initial and final full - // rounds, so we use rc_b as "scratch space" for fixed values (enabling potential - // layouter optimisations). - for column in iter::empty() - .chain(state.iter().cloned().map(|c| c.into())) - .chain(rc_b.iter().cloned().map(|c| c.into())) - { - meta.enable_equality(column); - } - - let s_full = meta.selector(); - let s_partial = meta.selector(); - let s_pad_and_add = meta.selector(); - - let alpha = [5, 0, 0, 0]; - let pow_5 = |v: Expression| { - let v2 = v.clone() * v.clone(); - v2.clone() * v2 * v - }; - - meta.create_gate("full round", |meta| { - let cur_0 = meta.query_advice(state[0], Rotation::cur()); - let cur_1 = meta.query_advice(state[1], Rotation::cur()); - let cur_2 = meta.query_advice(state[2], Rotation::cur()); - let next = [ - meta.query_advice(state[0], Rotation::next()), - meta.query_advice(state[1], Rotation::next()), - meta.query_advice(state[2], Rotation::next()), - ]; - - let rc_0 = meta.query_fixed(rc_a[0], Rotation::cur()); - let rc_1 = meta.query_fixed(rc_a[1], Rotation::cur()); - let rc_2 = meta.query_fixed(rc_a[2], Rotation::cur()); - - let s_full = meta.query_selector(s_full); - - let full_round = |next_idx: usize| { - s_full.clone() - * (pow_5(cur_0.clone() + rc_0.clone()) * m_reg[next_idx][0] - + pow_5(cur_1.clone() + rc_1.clone()) * m_reg[next_idx][1] - + pow_5(cur_2.clone() + rc_2.clone()) * m_reg[next_idx][2] - - next[next_idx].clone()) - }; - - vec![ - ("state[0]", full_round(0)), - ("state[1]", full_round(1)), - ("state[2]", full_round(2)), - ] - }); - - meta.create_gate("partial rounds", |meta| { - let cur_0 = meta.query_advice(state[0], Rotation::cur()); - let cur_1 = meta.query_advice(state[1], Rotation::cur()); - let cur_2 = meta.query_advice(state[2], Rotation::cur()); - let mid_0 = meta.query_advice(partial_sbox, Rotation::cur()); - let next_0 = meta.query_advice(state[0], Rotation::next()); - let next_1 = meta.query_advice(state[1], Rotation::next()); - let next_2 = meta.query_advice(state[2], Rotation::next()); - - let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur()); - let rc_a1 = meta.query_fixed(rc_a[1], Rotation::cur()); - let rc_a2 = meta.query_fixed(rc_a[2], Rotation::cur()); - let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur()); - let rc_b1 = meta.query_fixed(rc_b[1], Rotation::cur()); - let rc_b2 = meta.query_fixed(rc_b[2], Rotation::cur()); - - let s_partial = meta.query_selector(s_partial); - - let partial_round_linear = |idx: usize, rc_b: Expression| { - s_partial.clone() - * (mid_0.clone() * m_reg[idx][0] - + (cur_1.clone() + rc_a1.clone()) * m_reg[idx][1] - + (cur_2.clone() + rc_a2.clone()) * m_reg[idx][2] - + rc_b - - (next_0.clone() * m_inv[idx][0] - + next_1.clone() * m_inv[idx][1] - + next_2.clone() * m_inv[idx][2])) - }; - - vec![ - ( - "state[0] round a", - s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()), - ), - ( - "state[0] round b", - s_partial.clone() - * (pow_5( - mid_0.clone() * m_reg[0][0] - + (cur_1.clone() + rc_a1.clone()) * m_reg[0][1] - + (cur_2.clone() + rc_a2.clone()) * m_reg[0][2] - + rc_b0, - ) - (next_0.clone() * m_inv[0][0] - + next_1.clone() * m_inv[0][1] - + next_2.clone() * m_inv[0][2])), - ), - ("state[1]", partial_round_linear(1, rc_b1)), - ("state[2]", partial_round_linear(2, rc_b2)), - ] - }); - - meta.create_gate("pad-and-add", |meta| { - let initial_state_0 = meta.query_advice(state[0], Rotation::prev()); - let initial_state_1 = meta.query_advice(state[1], Rotation::prev()); - let initial_state_2 = meta.query_advice(state[2], Rotation::prev()); - let input_0 = meta.query_advice(state[0], Rotation::cur()); - let input_1 = meta.query_advice(state[1], Rotation::cur()); - let output_state_0 = meta.query_advice(state[0], Rotation::next()); - let output_state_1 = meta.query_advice(state[1], Rotation::next()); - let output_state_2 = meta.query_advice(state[2], Rotation::next()); - - let s_pad_and_add = meta.query_selector(s_pad_and_add); - - let pad_and_add = |initial_state, input, output_state| { - // We pad the input by storing the required padding in fixed columns and - // then constraining the corresponding input columns to be equal to it. - s_pad_and_add.clone() * (initial_state + input - output_state) - }; - - vec![ - ( - "state[0]", - pad_and_add(initial_state_0, input_0, output_state_0), - ), - ( - "state[1]", - pad_and_add(initial_state_1, input_1, output_state_1), - ), - // The capacity element is never altered by the input. - ( - "state[2]", - s_pad_and_add * (initial_state_2 - output_state_2), - ), - ] - }); - - Pow5T3Config { - state, - partial_sbox, - rc_a, - rc_b, - s_full, - s_partial, - s_pad_and_add, - half_full_rounds, - half_partial_rounds, - alpha, - round_constants, - m_reg, - m_inv, - } - } - - pub fn construct(config: Pow5T3Config) -> Self { - Pow5T3Chip { config } - } -} - -impl Chip for Pow5T3Chip { - type Config = Pow5T3Config; - type Loaded = (); - - fn config(&self) -> &Self::Config { - &self.config - } - - fn loaded(&self) -> &Self::Loaded { - &() - } -} - -impl> PoseidonInstructions for Pow5T3Chip { - type Word = StateWord; - - fn permute( - &self, - layouter: &mut impl Layouter, - initial_state: &State, - ) -> Result, Error> { - let config = self.config(); - - layouter.assign_region( - || "permute state", - |mut region| { - // Load the initial state into this region. - let state = Pow5T3State::load(&mut region, config, initial_state)?; - - let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| { - res.and_then(|state| state.full_round(&mut region, config, r, r)) - })?; - - let state = (0..config.half_partial_rounds).fold(Ok(state), |res, r| { - res.and_then(|state| { - state.partial_round( - &mut region, - config, - config.half_full_rounds + 2 * r, - config.half_full_rounds + r, - ) - }) - })?; - - let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| { - res.and_then(|state| { - state.full_round( - &mut region, - config, - config.half_full_rounds + 2 * config.half_partial_rounds + r, - config.half_full_rounds + config.half_partial_rounds + r, - ) - }) - })?; - - Ok(state.0) - }, - ) - } -} - -impl> PoseidonDuplexInstructions - for Pow5T3Chip -{ - fn initial_state( - &self, - layouter: &mut impl Layouter, - domain: &impl Domain, - ) -> Result, Error> { - let config = self.config(); - layouter.assign_region( - || format!("initial state for domain {:?}", domain), - |mut region| { - let mut load_state_word = |i: usize, value: F| { - let var = region.assign_advice_from_constant( - || format!("state_{}", i), - config.state[i], - 0, - value, - )?; - Ok(StateWord { - var, - value: Some(value), - }) - }; - - Ok([ - load_state_word(0, F::zero())?, - load_state_word(1, F::zero())?, - load_state_word(2, domain.initial_capacity_element())?, - ]) - }, - ) - } - - fn pad_and_add( - &self, - layouter: &mut impl Layouter, - domain: &impl Domain, - initial_state: &State, - input: &SpongeState, - ) -> Result, Error> { - let config = self.config(); - layouter.assign_region( - || format!("pad-and-add for domain {:?}", domain), - |mut region| { - config.s_pad_and_add.enable(&mut region, 1)?; - - // Load the initial state into this region. - let mut load_state_word = |i: usize| { - let value = initial_state[i].value; - let var = region.assign_advice( - || format!("load state_{}", i), - config.state[i], - 0, - || value.ok_or(Error::SynthesisError), - )?; - region.constrain_equal(initial_state[i].var, var)?; - Ok(StateWord { var, value }) - }; - let initial_state = [ - load_state_word(0)?, - load_state_word(1)?, - load_state_word(2)?, - ]; - - let padding_values = domain.padding(); - - // Load the input and padding into this region. - let mut load_input_word = |i: usize| { - let (constraint_var, value) = match (input[i], padding_values[i]) { - (Some(word), None) => (word.var, word.value), - (None, Some(padding_value)) => { - let padding_var = region.assign_fixed( - || format!("load pad_{}", i), - config.rc_b[i], - 1, - || Ok(padding_value), - )?; - (padding_var, Some(padding_value)) - } - _ => panic!("Input and padding don't match"), - }; - let var = region.assign_advice( - || format!("load input_{}", i), - config.state[i], - 1, - || value.ok_or(Error::SynthesisError), - )?; - region.constrain_equal(constraint_var, var)?; - - Ok(StateWord { var, value }) - }; - let input = [load_input_word(0)?, load_input_word(1)?]; - - // Constrain the output. - let mut constrain_output_word = |i: usize| { - let value = initial_state[i].value.and_then(|initial_word| { - input - .get(i) - .map(|word| word.value) - // The capacity element is never altered by the input. - .unwrap_or_else(|| Some(F::zero())) - .map(|input_word| initial_word + input_word) - }); - let var = region.assign_advice( - || format!("load output_{}", i), - config.state[i], - 2, - || value.ok_or(Error::SynthesisError), - )?; - Ok(StateWord { var, value }) - }; - - Ok([ - constrain_output_word(0)?, - constrain_output_word(1)?, - constrain_output_word(2)?, - ]) - }, - ) - } - - fn get_output(state: &State) -> SpongeState { - [Some(state[0]), Some(state[1])] - } -} - -#[derive(Clone, Copy, Debug)] -pub struct StateWord { - var: Cell, - value: Option, -} - -impl StateWord { - pub fn new(var: Cell, value: Option) -> Self { - Self { var, value } - } -} - -impl From> for CellValue { - fn from(state_word: StateWord) -> CellValue { - CellValue::new(state_word.var, state_word.value) - } -} - -#[derive(Debug)] -struct Pow5T3State([StateWord; WIDTH]); - -impl Pow5T3State { - fn full_round( - self, - region: &mut Region, - config: &Pow5T3Config, - round: usize, - offset: usize, - ) -> Result { - Self::round(region, config, round, offset, config.s_full, |_| { - let q_0 = self.0[0] - .value - .map(|v| v + config.round_constants[round][0]); - let q_1 = self.0[1] - .value - .map(|v| v + config.round_constants[round][1]); - let q_2 = self.0[2] - .value - .map(|v| v + config.round_constants[round][2]); - - let r_0 = q_0.map(|v| v.pow(&config.alpha)); - let r_1 = q_1.map(|v| v.pow(&config.alpha)); - let r_2 = q_2.map(|v| v.pow(&config.alpha)); - - let m = &config.m_reg; - let r = r_0.and_then(|r_0| r_1.and_then(|r_1| r_2.map(|r_2| [r_0, r_1, r_2]))); - - Ok(( - round + 1, - [ - r.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]), - r.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]), - r.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]), - ], - )) - }) - } - - fn partial_round( - self, - region: &mut Region, - config: &Pow5T3Config, - round: usize, - offset: usize, - ) -> Result { - Self::round(region, config, round, offset, config.s_partial, |region| { - let m = &config.m_reg; - - let p = self.0[0].value.and_then(|p_0| { - self.0[1] - .value - .and_then(|p_1| self.0[2].value.map(|p_2| [p_0, p_1, p_2])) - }); - - let r = p.map(|p| { - [ - (p[0] + config.round_constants[round][0]).pow(&config.alpha), - p[1] + config.round_constants[round][1], - p[2] + config.round_constants[round][2], - ] - }); - - region.assign_advice( - || format!("round_{} partial_sbox", round), - config.partial_sbox, - offset, - || r.map(|r| r[0]).ok_or(Error::SynthesisError), - )?; - - let p_mid = r.map(|r| { - [ - m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2], - m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2], - m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2], - ] - }); - - // Load the second round constants. - let mut load_round_constant = |i: usize| { - region.assign_fixed( - || format!("round_{} rc_{}", round + 1, i), - config.rc_b[i], - offset, - || Ok(config.round_constants[round + 1][i]), - ) - }; - for i in 0..WIDTH { - load_round_constant(i)?; - } - - let r_mid = p_mid.map(|p| { - [ - (p[0] + config.round_constants[round + 1][0]).pow(&config.alpha), - p[1] + config.round_constants[round + 1][1], - p[2] + config.round_constants[round + 1][2], - ] - }); - - Ok(( - round + 2, - [ - r_mid.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]), - r_mid.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]), - r_mid.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]), - ], - )) - }) - } - - fn load( - region: &mut Region, - config: &Pow5T3Config, - initial_state: &State, WIDTH>, - ) -> Result { - let mut load_state_word = |i: usize| { - let value = initial_state[i].value; - let var = region.assign_advice( - || format!("load state_{}", i), - config.state[i], - 0, - || value.ok_or(Error::SynthesisError), - )?; - region.constrain_equal(initial_state[i].var, var)?; - Ok(StateWord { var, value }) - }; - - Ok(Pow5T3State([ - load_state_word(0)?, - load_state_word(1)?, - load_state_word(2)?, - ])) - } - - fn round( - region: &mut Region, - config: &Pow5T3Config, - round: usize, - offset: usize, - round_gate: Selector, - round_fn: impl FnOnce(&mut Region) -> Result<(usize, [Option; WIDTH]), Error>, - ) -> Result { - // Enable the required gate. - round_gate.enable(region, offset)?; - - // Load the round constants. - let mut load_round_constant = |i: usize| { - region.assign_fixed( - || format!("round_{} rc_{}", round, i), - config.rc_a[i], - offset, - || Ok(config.round_constants[round][i]), - ) - }; - for i in 0..WIDTH { - load_round_constant(i)?; - } - - // Compute the next round's state. - let (next_round, next_state) = round_fn(region)?; - - let mut next_state_word = |i: usize| { - let value = next_state[i]; - let var = region.assign_advice( - || format!("round_{} state_{}", next_round, i), - config.state[i], - offset + 1, - || value.ok_or(Error::SynthesisError), - )?; - Ok(StateWord { var, value }) - }; - - Ok(Pow5T3State([ - next_state_word(0)?, - next_state_word(1)?, - next_state_word(2)?, - ])) - } -} - -#[cfg(test)] -mod tests { - use ff::PrimeField; - use halo2::{ - arithmetic::FieldExt, - circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, - pasta::Fp, - plonk::{Circuit, ConstraintSystem, Error}, - }; - use pasta_curves::pallas; - - use super::{PoseidonInstructions, Pow5T3Chip, Pow5T3Config, StateWord, WIDTH}; - use crate::{ - circuit::gadget::poseidon::{Hash, Word}, - primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec}, - }; - - struct PermuteCircuit {} - - impl Circuit for PermuteCircuit { - type Config = Pow5T3Config; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - PermuteCircuit {} - } - - fn configure(meta: &mut ConstraintSystem) -> Pow5T3Config { - let state = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; - let partial_sbox = meta.advice_column(); - - let rc_a = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - let rc_b = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - - Pow5T3Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b) - } - - fn synthesize( - &self, - config: Pow5T3Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let initial_state = layouter.assign_region( - || "prepare initial state", - |mut region| { - let mut state_word = |i: usize| { - let value = Some(Fp::from(i as u64)); - let var = region.assign_advice( - || format!("load state_{}", i), - config.state[i], - 0, - || value.ok_or(Error::SynthesisError), - )?; - Ok(StateWord { var, value }) - }; - - Ok([state_word(0)?, state_word(1)?, state_word(2)?]) - }, - )?; - - let chip = Pow5T3Chip::construct(config.clone()); - let final_state = as PoseidonInstructions< - Fp, - OrchardNullifier, - WIDTH, - 2, - >>::permute(&chip, &mut layouter, &initial_state)?; - - // For the purpose of this test, compute the real final state inline. - let mut expected_final_state = [Fp::zero(), Fp::one(), Fp::from_u64(2)]; - let (round_constants, mds, _) = OrchardNullifier.constants(); - poseidon::permute::<_, OrchardNullifier, WIDTH, 2>( - &mut expected_final_state, - &mds, - &round_constants, - ); - - layouter.assign_region( - || "constrain final state", - |mut region| { - let mut final_state_word = |i: usize| { - let var = region.assign_advice( - || format!("load final_state_{}", i), - config.state[i], - 0, - || Ok(expected_final_state[i]), - )?; - region.constrain_equal(final_state[i].var, var) - }; - - final_state_word(0)?; - final_state_word(1)?; - final_state_word(2) - }, - ) - } - } - - #[test] - fn poseidon_permute() { - let k = 6; - let circuit = PermuteCircuit {}; - let prover = MockProver::run(k, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())) - } - - #[derive(Default)] - struct HashCircuit { - message: Option<[Fp; 2]>, - // For the purpose of this test, witness the result. - // TODO: Move this into an instance column. - output: Option, - } - - impl Circuit for HashCircuit { - type Config = Pow5T3Config; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Pow5T3Config { - let state = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; - let partial_sbox = meta.advice_column(); - - let rc_a = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - let rc_b = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - - meta.enable_constant(rc_b[0]); - - Pow5T3Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b) - } - - fn synthesize( - &self, - config: Pow5T3Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let chip = Pow5T3Chip::construct(config.clone()); - - let message = layouter.assign_region( - || "load message", - |mut region| { - let mut message_word = |i: usize| { - let value = self.message.map(|message_vals| message_vals[i]); - let var = region.assign_advice( - || format!("load message_{}", i), - config.state[i], - 0, - || value.ok_or(Error::SynthesisError), - )?; - Ok(Word::<_, _, OrchardNullifier, WIDTH, 2> { - inner: StateWord { var, value }, - }) - }; - - Ok([message_word(0)?, message_word(1)?]) - }, - )?; - - let hasher = Hash::init(chip, layouter.namespace(|| "init"), ConstantLength::<2>)?; - let output = hasher.hash(layouter.namespace(|| "hash"), message)?; - - layouter.assign_region( - || "constrain output", - |mut region| { - let expected_var = region.assign_advice( - || "load output", - config.state[0], - 0, - || self.output.ok_or(Error::SynthesisError), - )?; - let word: StateWord<_> = output.inner; - region.constrain_equal(word.var, expected_var) - }, - ) - } - } - - #[test] - fn poseidon_hash() { - let message = [Fp::rand(), Fp::rand()]; - let output = poseidon::Hash::init(OrchardNullifier, ConstantLength::<2>).hash(message); - - let k = 6; - let circuit = HashCircuit { - message: Some(message), - output: Some(output), - }; - let prover = MockProver::run(k, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())) - } - - #[test] - fn hash_test_vectors() { - for tv in crate::primitives::poseidon::test_vectors::fp::hash() { - let message = [ - pallas::Base::from_repr(tv.input[0]).unwrap(), - pallas::Base::from_repr(tv.input[1]).unwrap(), - ]; - let output = poseidon::Hash::init(OrchardNullifier, ConstantLength).hash(message); - - let k = 6; - let circuit = HashCircuit { - message: Some(message), - output: Some(output), - }; - let prover = MockProver::run(k, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - } - - #[cfg(feature = "dev-graph")] - #[test] - fn print_poseidon_chip() { - use plotters::prelude::*; - - let root = BitMapBackend::new("poseidon-chip-layout.png", (1024, 768)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root - .titled("Poseidon Chip Layout", ("sans-serif", 60)) - .unwrap(); - - let circuit = HashCircuit { - message: None, - output: None, - }; - halo2::dev::CircuitLayout::default() - .render(6, &circuit, &root) - .unwrap(); - } -} diff --git a/src/circuit/gadget/sinsemilla.rs b/src/circuit/gadget/sinsemilla.rs index de562d8d..6f58f95f 100644 --- a/src/circuit/gadget/sinsemilla.rs +++ b/src/circuit/gadget/sinsemilla.rs @@ -3,10 +3,10 @@ use crate::circuit::gadget::{ ecc::{self, EccInstructions}, utilities::Var, }; -use ff::PrimeField; +use group::ff::{Field, PrimeField}; use halo2::{circuit::Layouter, plonk::Error}; -use pasta_curves::arithmetic::{CurveAffine, FieldExt}; -use std::{convert::TryInto, fmt::Debug}; +use pasta_curves::arithmetic::CurveAffine; +use std::fmt::Debug; pub mod chip; pub mod commit_ivk; @@ -28,11 +28,11 @@ pub trait SinsemillaInstructions, _> = bitstring .chunks(piece_num_words * K) .enumerate() @@ -170,7 +170,7 @@ where SinsemillaChip: SinsemillaInstructions + Clone + Debug + Eq, { fn inner(&self) -> SinsemillaChip::MessagePiece { - self.inner + self.inner.clone() } } @@ -188,26 +188,23 @@ where assert_eq!(bitstring.len() % K, 0); let num_words = bitstring.len() / K; - // Message piece must be at most `ceil(C::Base::NUM_BITS / K)` bits - let piece_max_num_words = C::Base::NUM_BITS as usize / K; + // Each message piece must have at most `floor(C::Base::CAPACITY / K)` words. + // This ensures that the all-ones bitstring is canonical in the field. + let piece_max_num_words = C::Base::CAPACITY as usize / K; assert!(num_words <= piece_max_num_words as usize); // Closure to parse a bitstring (little-endian) into a base field element. let to_base_field = |bits: &[Option]| -> Option { - assert!(bits.len() <= C::Base::NUM_BITS as usize); - let bits: Option> = bits.iter().cloned().collect(); - let bytes: Option> = bits.map(|bits| { - // Pad bits to 256 bits - let pad_len = 256 - bits.len(); - let mut bits = bits; - bits.extend_from_slice(&vec![false; pad_len]); - - bits.chunks_exact(8) - .map(|byte| byte.iter().rev().fold(0u8, |acc, bit| acc * 2 + *bit as u8)) - .collect() - }); - bytes.map(|bytes| C::Base::from_bytes(&bytes.try_into().unwrap()).unwrap()) + bits.map(|bits| { + bits.into_iter().rev().fold(C::Base::zero(), |acc, bit| { + if bit { + acc.double() + C::Base::one() + } else { + acc.double() + } + }) + }) }; let piece_value = to_base_field(bitstring); @@ -487,8 +484,7 @@ mod tests { let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx); - let ecc_config = - EccChip::configure(meta, advices, lagrange_coeffs, range_check.clone()); + let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check); let config1 = SinsemillaChip::configure( meta, @@ -496,7 +492,7 @@ mod tests { advices[2], lagrange_coeffs[0], lookup, - range_check.clone(), + range_check, ); let config2 = SinsemillaChip::configure( meta, diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index 8cac3fea..f2a7ff1e 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -4,8 +4,7 @@ use super::{ }; use crate::{ circuit::gadget::{ - ecc::chip::NonIdentityEccPoint, - utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, Var}, + ecc::chip::NonIdentityEccPoint, utilities::lookup_range_check::LookupRangeCheckConfig, }, constants::OrchardFixedBasesFull, primitives::sinsemilla::{ @@ -13,9 +12,10 @@ use crate::{ }, }; +use group::ff::PrimeField; use halo2::{ - arithmetic::{CurveAffine, FieldExt}, - circuit::{Chip, Layouter}, + arithmetic::CurveAffine, + circuit::{AssignedCell, Chip, Layouter}, plonk::{ Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, TableColumn, VirtualCells, @@ -146,7 +146,7 @@ impl SinsemillaChip { // Set up lookup argument GeneratorTableConfig::configure(meta, config.clone()); - let two = pallas::Base::from_u64(2); + let two = pallas::Base::from(2); // Closures for expressions that are derived multiple times // x_r = lambda_1^2 - x_a - x_p @@ -211,7 +211,7 @@ impl SinsemillaChip { // - rhs = (2 * Y_A_cur + (2 - q_s3) * Y_A_next + 2 * q_s3 * y_a_final) let y_check = { // lhs = 4 * lambda_2_cur * (x_a_cur - x_a_next) - let lhs = lambda_2_cur * pallas::Base::from_u64(4) * (x_a_cur - x_a_next); + let lhs = lambda_2_cur * pallas::Base::from(4) * (x_a_cur - x_a_next); // rhs = 2 * Y_A_cur + (2 - q_s3) * Y_A_next + 2 * q_s3 * y_a_final let rhs = { @@ -239,14 +239,14 @@ impl SinsemillaChip { impl SinsemillaInstructions for SinsemillaChip { - type CellValue = CellValue; + type CellValue = AssignedCell; type Message = Message; type MessagePiece = MessagePiece; type RunningSum = Vec; - type X = CellValue; + type X = AssignedCell; type NonIdentityPoint = NonIdentityEccPoint; type FixedPoints = OrchardFixedBasesFull; @@ -268,11 +268,11 @@ impl SinsemillaInstructions for SinsemillaHashDomains { fn Q(&self) -> pallas::Affine { match self { SinsemillaHashDomains::CommitIvk => pallas::Affine::from_xy( - pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap(), + pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap(), + pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap(), ) .unwrap(), SinsemillaHashDomains::NoteCommit => pallas::Affine::from_xy( - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(), + pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap(), + pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(), ) .unwrap(), SinsemillaHashDomains::MerkleCrh => pallas::Affine::from_xy( - pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap(), - pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap(), + pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap(), + pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap(), ) .unwrap(), } diff --git a/src/circuit/gadget/sinsemilla/chip/generator_table.rs b/src/circuit/gadget/sinsemilla/chip/generator_table.rs index 5c0624ac..71039d28 100644 --- a/src/circuit/gadget/sinsemilla/chip/generator_table.rs +++ b/src/circuit/gadget/sinsemilla/chip/generator_table.rs @@ -42,7 +42,7 @@ impl GeneratorTableConfig { let word = { let z_cur = meta.query_advice(config.bits, Rotation::cur()); let z_next = meta.query_advice(config.bits, Rotation::next()); - z_cur - ((q_s2 - q_s3) * z_next * pallas::Base::from_u64(1 << sinsemilla::K)) + z_cur - ((q_s2 - q_s3) * z_next * pallas::Base::from(1 << sinsemilla::K)) }; let x_p = meta.query_advice(config.x_p, Rotation::cur()); @@ -84,7 +84,7 @@ impl GeneratorTableConfig { || "table_idx", self.table_idx, index, - || Ok(pallas::Base::from_u64(index as u64)), + || Ok(pallas::Base::from(index as u64)), )?; table.assign_cell(|| "table_x", self.table_x, index, || Ok(*x))?; table.assign_cell(|| "table_y", self.table_y, index, || Ok(*y))?; diff --git a/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs b/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs index 0ec00e29..517e2202 100644 --- a/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs +++ b/src/circuit/gadget/sinsemilla/chip/hash_to_point.rs @@ -1,12 +1,13 @@ use super::super::SinsemillaInstructions; -use super::{CellValue, NonIdentityEccPoint, SinsemillaChip, Var}; +use super::{NonIdentityEccPoint, SinsemillaChip}; use crate::primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K, SINSEMILLA_S}; +use halo2::circuit::AssignedCell; use halo2::{ circuit::{Chip, Region}, plonk::Error, }; -use ff::{Field, PrimeFieldBits}; +use group::ff::{Field, PrimeField, PrimeFieldBits}; use pasta_curves::{ arithmetic::{CurveAffine, FieldExt}, pallas, @@ -26,7 +27,13 @@ impl SinsemillaChip { { sinsemilla::K }, { sinsemilla::C }, >>::Message, - ) -> Result<(NonIdentityEccPoint, Vec>>), Error> { + ) -> Result< + ( + NonIdentityEccPoint, + Vec>>, + ), + Error, + > { let config = self.config().clone(); let mut offset = 0; @@ -46,16 +53,13 @@ impl SinsemillaChip { // Constrain the initial x_q to equal the x-coordinate of the domain's `Q`. let mut x_a: X = { - let x_a = { - let cell = - region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?; - CellValue::new(cell, Some(x_q)) - }; + let x_a = + region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?; x_a.into() }; - let mut zs_sum: Vec>> = Vec::new(); + let mut zs_sum: Vec>> = Vec::new(); // Hash each piece in the message. for (idx, piece) in message.iter().enumerate() { @@ -81,7 +85,7 @@ impl SinsemillaChip { || "y_a", config.lambda_1, offset, - || y_a.ok_or(Error::SynthesisError), + || y_a.ok_or(Error::Synthesis), )?; // Assign lambda_2 and x_p zero values since they are queried @@ -102,7 +106,7 @@ impl SinsemillaChip { )?; } - CellValue::new(y_a_cell, y_a.0) + y_a_cell }; #[cfg(test)] @@ -142,15 +146,15 @@ impl SinsemillaChip { .chunks(K) .fold(Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc); let actual_point = - pallas::Affine::from_xy(x_a.value().unwrap(), y_a.value().unwrap()).unwrap(); + pallas::Affine::from_xy(*x_a.value().unwrap(), *y_a.value().unwrap()).unwrap(); assert_eq!(expected_point.to_affine(), actual_point); } } if let Some(x_a) = x_a.value() { if let Some(y_a) = y_a.value() { - if x_a == pallas::Base::zero() || y_a == pallas::Base::zero() { - return Err(Error::SynthesisError); + if x_a.is_zero_vartime() || y_a.is_zero_vartime() { + return Err(Error::Synthesis); } } } @@ -183,7 +187,7 @@ impl SinsemillaChip { ( X, Y, - Vec>, + Vec>, ), Error, > { @@ -220,7 +224,7 @@ impl SinsemillaChip { offset + piece.num_words() - 1, || { Ok(if final_piece { - pallas::Base::from_u64(2) + pallas::Base::from(2) } else { pallas::Base::zero() }) @@ -264,14 +268,13 @@ impl SinsemillaChip { let mut zs = Vec::with_capacity(piece.num_words() + 1); // Copy message and initialize running sum `z` to decompose message in-circuit - let cell = region.assign_advice( + let initial_z = piece.cell_value().copy_advice( || "z_0 (copy of message piece)", + region, config.bits, offset, - || piece.field_elem().ok_or(Error::SynthesisError), )?; - region.constrain_equal(piece.cell(), cell)?; - zs.push(CellValue::new(cell, piece.field_elem())); + zs.push(initial_z); // Assign cumulative sum such that for 0 <= i < n, // z_i = 2^K * z_{i + 1} + m_{i + 1} @@ -281,21 +284,21 @@ impl SinsemillaChip { // We end up with z_n = 0. (z_n is not directly encoded as a cell value; // it is implicitly taken as 0 by adjusting the definition of m_{i+1}.) let mut z = piece.field_elem(); - let inv_2_k = pallas::Base::from_bytes(&INV_TWO_POW_K).unwrap(); + let inv_2_k = pallas::Base::from_repr(INV_TWO_POW_K).unwrap(); // We do not assign the final z_n as it is constrained to be zero. for (idx, word) in words[0..(words.len() - 1)].iter().enumerate() { // z_{i + 1} = (z_i - m_{i + 1}) / 2^K z = z .zip(*word) - .map(|(z, word)| (z - pallas::Base::from_u64(word as u64)) * inv_2_k); + .map(|(z, word)| (z - pallas::Base::from(word as u64)) * inv_2_k); let cell = region.assign_advice( || format!("z_{:?}", idx + 1), config.bits, offset + idx + 1, - || z.ok_or(Error::SynthesisError), + || z.ok_or(Error::Synthesis), )?; - zs.push(CellValue::new(cell, z)) + zs.push(cell) } zs @@ -320,7 +323,7 @@ impl SinsemillaChip { || "x_p", config.x_p, offset + row, - || x_p.ok_or(Error::SynthesisError), + || x_p.ok_or(Error::Synthesis), )?; // Compute and assign `lambda_1` @@ -337,7 +340,7 @@ impl SinsemillaChip { || "lambda_1", config.lambda_1, offset + row, - || lambda_1.ok_or(Error::SynthesisError), + || lambda_1.ok_or(Error::Synthesis), )?; lambda_1 @@ -353,7 +356,7 @@ impl SinsemillaChip { let lambda_2 = { let lambda_2 = x_a.value().zip(y_a.0).zip(x_r).zip(lambda_1).map( |(((x_a, y_a), x_r), lambda_1)| { - pallas::Base::from_u64(2) * y_a * (x_a - x_r).invert().unwrap() - lambda_1 + pallas::Base::from(2) * y_a * (x_a - x_r).invert().unwrap() - lambda_1 }, ); @@ -361,7 +364,7 @@ impl SinsemillaChip { || "lambda_2", config.lambda_2, offset + row, - || lambda_2.ok_or(Error::SynthesisError), + || lambda_2.ok_or(Error::Synthesis), )?; lambda_2 @@ -378,10 +381,10 @@ impl SinsemillaChip { || "x_a", config.x_a, offset + row + 1, - || x_a_new.ok_or(Error::SynthesisError), + || x_a_new.ok_or(Error::Synthesis), )?; - CellValue::new(x_a_cell, x_a_new).into() + x_a_cell.into() }; // Compute y_a for the next row. @@ -402,18 +405,18 @@ impl SinsemillaChip { } /// The x-coordinate of the accumulator in a Sinsemilla hash instance. -struct X(CellValue); +struct X(AssignedCell); -impl From> for X { - fn from(cell_value: CellValue) -> Self { +impl From> for X { + fn from(cell_value: AssignedCell) -> Self { X(cell_value) } } impl Deref for X { - type Target = CellValue; + type Target = AssignedCell; - fn deref(&self) -> &CellValue { + fn deref(&self) -> &AssignedCell { &self.0 } } diff --git a/src/circuit/gadget/sinsemilla/commit_ivk.rs b/src/circuit/gadget/sinsemilla/commit_ivk.rs index 476691e9..16a8c905 100644 --- a/src/circuit/gadget/sinsemilla/commit_ivk.rs +++ b/src/circuit/gadget/sinsemilla/commit_ivk.rs @@ -1,5 +1,5 @@ use halo2::{ - circuit::Layouter, + circuit::{AssignedCell, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, poly::Rotation, }; @@ -8,7 +8,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas}; use crate::{ circuit::gadget::{ ecc::{chip::EccChip, X}, - utilities::{bitrange_subset, bool_check, copy, CellValue, Var}, + utilities::{bitrange_subset, bool_check}, }, constants::T_P, }; @@ -67,8 +67,8 @@ impl CommitIvkConfig { let q_commit_ivk = meta.query_selector(config.q_commit_ivk); // Useful constants - let two_pow_4 = pallas::Base::from_u64(1 << 4); - let two_pow_5 = pallas::Base::from_u64(1 << 5); + let two_pow_4 = pallas::Base::from(1 << 4); + let two_pow_5 = pallas::Base::from(1 << 5); let two_pow_9 = two_pow_4 * two_pow_5; let two_pow_250 = pallas::Base::from_u128(1 << 125).square(); let two_pow_254 = two_pow_250 * two_pow_4; @@ -119,7 +119,7 @@ impl CommitIvkConfig { // Check that nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit) let nk_decomposition_check = { - let two_pow_245 = pallas::Base::from_u64(1 << 49).pow(&[5, 0, 0, 0]); + let two_pow_245 = pallas::Base::from(1 << 49).pow(&[5, 0, 0, 0]); b_2.clone() + c.clone() * two_pow_5 @@ -181,7 +181,7 @@ impl CommitIvkConfig { // Check that b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P. // This is checked regardless of the value of d_1. let b2_c_prime_check = { - let two_pow_5 = pallas::Base::from_u64(1 << 5); + let two_pow_5 = pallas::Base::from(1 << 5); let two_pow_140 = Expression::Constant(pallas::Base::from_u128(1 << 70).square()); let t_p = Expression::Constant(pallas::Base::from_u128(T_P)); @@ -225,8 +225,8 @@ impl CommitIvkConfig { sinsemilla_chip: SinsemillaChip, ecc_chip: EccChip, mut layouter: impl Layouter, - ak: CellValue, - nk: CellValue, + ak: AssignedCell, + nk: AssignedCell, rivk: Option, ) -> Result, Error> { // @@ -257,8 +257,8 @@ impl CommitIvkConfig { let b_2 = nk.value().map(|value| bitrange_subset(value, 0..5)); let b = b_0.zip(b_1).zip(b_2).map(|((b_0, b_1), b_2)| { - let b1_shifted = b_1 * pallas::Base::from_u64(1 << 4); - let b2_shifted = b_2 * pallas::Base::from_u64(1 << 5); + let b1_shifted = b_1 * pallas::Base::from(1 << 4); + let b2_shifted = b_2 * pallas::Base::from(1 << 5); b_0 + b1_shifted + b2_shifted }); @@ -304,7 +304,7 @@ impl CommitIvkConfig { let d = d_0 .zip(d_1) - .map(|(d_0, d_1)| d_0 + d_1 * pallas::Base::from_u64(1 << 9)); + .map(|(d_0, d_1)| d_0 + d_1 * pallas::Base::from(1 << 9)); // Constrain d_0 to be 9 bits. let d_0 = self.sinsemilla_config.lookup_config.witness_short_check( @@ -337,8 +337,8 @@ impl CommitIvkConfig { domain.short_commit(layouter.namespace(|| "Hash ak||nk"), message, rivk)? }; - let z13_a = zs[0][13]; - let z13_c = zs[2][13]; + let z13_a = zs[0][13].clone(); + let z13_c = zs[2][13].clone(); let (a_prime, z13_a_prime) = self.ak_canonicity( layouter.namespace(|| "ak canonicity"), @@ -347,7 +347,7 @@ impl CommitIvkConfig { let (b2_c_prime, z14_b2_c_prime) = self.nk_canonicity( layouter.namespace(|| "nk canonicity"), - b_2, + b_2.clone(), c.inner().cell_value(), )?; @@ -384,8 +384,14 @@ impl CommitIvkConfig { fn ak_canonicity( &self, mut layouter: impl Layouter, - a: CellValue, - ) -> Result<(CellValue, CellValue), Error> { + a: AssignedCell, + ) -> Result< + ( + AssignedCell, + AssignedCell, + ), + Error, + > { // `ak` = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)` // - b_1 = 1 => b_0 = 0 // - b_1 = 1 => a < t_P @@ -406,10 +412,10 @@ impl CommitIvkConfig { 13, false, )?; - let a_prime = zs[0]; + let a_prime = zs[0].clone(); assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z13_a] - Ok((a_prime, zs[13])) + Ok((a_prime, zs[13].clone())) } #[allow(clippy::type_complexity)] @@ -417,9 +423,15 @@ impl CommitIvkConfig { fn nk_canonicity( &self, mut layouter: impl Layouter, - b_2: CellValue, - c: CellValue, - ) -> Result<(CellValue, CellValue), Error> { + b_2: AssignedCell, + c: AssignedCell, + ) -> Result< + ( + AssignedCell, + AssignedCell, + ), + Error, + > { // `nk` = `b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit) // - d_1 = 1 => d_0 = 0 // - d_1 = 1 => b_2 + c * 2^5 < t_P @@ -432,7 +444,7 @@ impl CommitIvkConfig { // Decompose the low 140 bits of b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P, and output // the running sum at the end of it. If b2_c_prime < 2^140, the running sum will be 0. let b2_c_prime = b_2.value().zip(c.value()).map(|(b_2, c)| { - let two_pow_5 = pallas::Base::from_u64(1 << 5); + let two_pow_5 = pallas::Base::from(1 << 5); let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square(); let t_p = pallas::Base::from_u128(T_P); b_2 + c * two_pow_5 + two_pow_140 - t_p @@ -443,10 +455,10 @@ impl CommitIvkConfig { 14, false, )?; - let b2_c_prime = zs[0]; + let b2_c_prime = zs[0].clone(); assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z14] - Ok((b2_c_prime, zs[14])) + Ok((b2_c_prime, zs[14].clone())) } // Assign cells for the canonicity gate. @@ -474,71 +486,60 @@ impl CommitIvkConfig { { let offset = 0; // Copy in `ak` - copy( - &mut region, - || "ak", - self.advices[0], - offset, - &gate_cells.ak, - )?; + gate_cells + .ak + .copy_advice(|| "ak", &mut region, self.advices[0], offset)?; // Copy in `a` - copy(&mut region, || "a", self.advices[1], offset, &gate_cells.a)?; + gate_cells + .a + .copy_advice(|| "a", &mut region, self.advices[1], offset)?; // Copy in `b` - copy(&mut region, || "b", self.advices[2], offset, &gate_cells.b)?; + gate_cells + .b + .copy_advice(|| "b", &mut region, self.advices[2], offset)?; // Copy in `b_0` - copy( - &mut region, - || "b_0", - self.advices[3], - offset, - &gate_cells.b_0, - )?; + gate_cells + .b_0 + .copy_advice(|| "b_0", &mut region, self.advices[3], offset)?; // Witness `b_1` region.assign_advice( || "Witness b_1", self.advices[4], offset, - || gate_cells.b_1.ok_or(Error::SynthesisError), + || gate_cells.b_1.ok_or(Error::Synthesis), )?; // Copy in `b_2` - copy( - &mut region, - || "b_2", - self.advices[5], - offset, - &gate_cells.b_2, - )?; + gate_cells + .b_2 + .copy_advice(|| "b_2", &mut region, self.advices[5], offset)?; // Copy in z13_a - copy( - &mut region, + gate_cells.z13_a.copy_advice( || "z13_a", + &mut region, self.advices[6], offset, - &gate_cells.z13_a, )?; // Copy in a_prime - copy( - &mut region, + gate_cells.a_prime.copy_advice( || "a_prime", + &mut region, self.advices[7], offset, - &gate_cells.a_prime, )?; // Copy in z13_a_prime - copy( - &mut region, + gate_cells.z13_a_prime.copy_advice( || "z13_a_prime", + &mut region, self.advices[8], offset, - &gate_cells.z13_a_prime, )?; } @@ -547,62 +548,55 @@ impl CommitIvkConfig { let offset = 1; // Copy in `nk` - copy( - &mut region, - || "nk", - self.advices[0], - offset, - &gate_cells.nk, - )?; + gate_cells + .nk + .copy_advice(|| "nk", &mut region, self.advices[0], offset)?; // Copy in `c` - copy(&mut region, || "c", self.advices[1], offset, &gate_cells.c)?; + gate_cells + .c + .copy_advice(|| "c", &mut region, self.advices[1], offset)?; // Copy in `d` - copy(&mut region, || "d", self.advices[2], offset, &gate_cells.d)?; + gate_cells + .d + .copy_advice(|| "d", &mut region, self.advices[2], offset)?; // Copy in `d_0` - copy( - &mut region, - || "d_0", - self.advices[3], - offset, - &gate_cells.d_0, - )?; + gate_cells + .d_0 + .copy_advice(|| "d_0", &mut region, self.advices[3], offset)?; // Witness `d_1` region.assign_advice( || "Witness d_1", self.advices[4], offset, - || gate_cells.d_1.ok_or(Error::SynthesisError), + || gate_cells.d_1.ok_or(Error::Synthesis), )?; // Copy in z13_c - copy( - &mut region, + gate_cells.z13_c.copy_advice( || "z13_c", + &mut region, self.advices[6], offset, - &gate_cells.z13_c, )?; // Copy in b2_c_prime - copy( - &mut region, + gate_cells.b2_c_prime.copy_advice( || "b2_c_prime", + &mut region, self.advices[7], offset, - &gate_cells.b2_c_prime, )?; // Copy in z14_b2_c_prime - copy( - &mut region, + gate_cells.z14_b2_c_prime.copy_advice( || "z14_b2_c_prime", + &mut region, self.advices[8], offset, - &gate_cells.z14_b2_c_prime, )?; } @@ -614,23 +608,23 @@ impl CommitIvkConfig { // Cells used in the canonicity gate. struct GateCells { - a: CellValue, - b: CellValue, - c: CellValue, - d: CellValue, - ak: CellValue, - nk: CellValue, - b_0: CellValue, + a: AssignedCell, + b: AssignedCell, + c: AssignedCell, + d: AssignedCell, + ak: AssignedCell, + nk: AssignedCell, + b_0: AssignedCell, b_1: Option, - b_2: CellValue, - d_0: CellValue, + b_2: AssignedCell, + d_0: AssignedCell, d_1: Option, - z13_a: CellValue, - a_prime: CellValue, - z13_a_prime: CellValue, - z13_c: CellValue, - b2_c_prime: CellValue, - z14_b2_c_prime: CellValue, + z13_a: AssignedCell, + a_prime: AssignedCell, + z13_a_prime: AssignedCell, + z13_c: AssignedCell, + b2_c_prime: AssignedCell, + z14_b2_c_prime: AssignedCell, } #[cfg(test)] @@ -640,16 +634,14 @@ mod tests { circuit::gadget::{ ecc::chip::{EccChip, EccConfig}, sinsemilla::chip::SinsemillaChip, - utilities::{ - lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var, - }, + utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions}, }, constants::{COMMIT_IVK_PERSONALIZATION, L_ORCHARD_BASE, T_Q}, primitives::sinsemilla::CommitDomain, }; use ff::PrimeFieldBits; use halo2::{ - circuit::{Layouter, SimpleFloorPlanner}, + circuit::{AssignedCell, Layouter, SimpleFloorPlanner}, dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; @@ -666,7 +658,7 @@ mod tests { } impl UtilitiesInstructions for MyCircuit { - type Var = CellValue; + type Var = AssignedCell; } impl Circuit for MyCircuit { @@ -722,7 +714,7 @@ mod tests { advices[2], lagrange_coeffs[0], lookup, - range_check.clone(), + range_check, ); let commit_ivk_config = @@ -803,7 +795,7 @@ mod tests { .unwrap() }; - assert_eq!(expected_ivk, ivk.inner().value().unwrap()); + assert_eq!(&expected_ivk, ivk.inner().value().unwrap()); Ok(()) } diff --git a/src/circuit/gadget/sinsemilla/merkle.rs b/src/circuit/gadget/sinsemilla/merkle.rs index d9be7163..d83484d7 100644 --- a/src/circuit/gadget/sinsemilla/merkle.rs +++ b/src/circuit/gadget/sinsemilla/merkle.rs @@ -139,13 +139,14 @@ pub mod tests { use crate::{ circuit::gadget::{ sinsemilla::chip::{SinsemillaChip, SinsemillaHashDomains}, - utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions, Var}, + utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions}, }, constants::MERKLE_DEPTH_ORCHARD, note::commitment::ExtractedNoteCommitment, tree, }; + use group::ff::PrimeField; use halo2::{ arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner}, @@ -210,7 +211,7 @@ pub mod tests { advices[7], fixed_y_q_1, lookup, - range_check.clone(), + range_check, ); let config1 = MerkleChip::configure(meta, sinsemilla_config_1); @@ -260,13 +261,13 @@ pub mod tests { // The expected final root let final_root = { let path = tree::MerklePath::new(leaf_pos, self.merkle_path.unwrap()); - let leaf = ExtractedNoteCommitment::from_bytes(&self.leaf.unwrap().to_bytes()) - .unwrap(); + let leaf = + ExtractedNoteCommitment::from_bytes(&self.leaf.unwrap().to_repr()).unwrap(); path.root(leaf) }; // Check the computed final root against the expected final root. - assert_eq!(computed_final_root.value().unwrap(), final_root.inner()); + assert_eq!(computed_final_root.value().unwrap(), &final_root.inner()); } Ok(()) diff --git a/src/circuit/gadget/sinsemilla/merkle/chip.rs b/src/circuit/gadget/sinsemilla/merkle/chip.rs index ce25531e..a4230668 100644 --- a/src/circuit/gadget/sinsemilla/merkle/chip.rs +++ b/src/circuit/gadget/sinsemilla/merkle/chip.rs @@ -1,5 +1,5 @@ use halo2::{ - circuit::{Chip, Layouter}, + circuit::{AssignedCell, Chip, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Selector}, poly::Rotation, }; @@ -15,7 +15,7 @@ use crate::{ circuit::gadget::utilities::{ bitrange_subset, cond_swap::{CondSwapChip, CondSwapConfig, CondSwapInstructions}, - copy, CellValue, UtilitiesInstructions, Var, + UtilitiesInstructions, }, constants::{L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD}, primitives::sinsemilla, @@ -82,7 +82,7 @@ impl MerkleChip { let q_decompose = meta.query_selector(q_decompose); let l_whole = meta.query_advice(advices[4], Rotation::next()); - let two_pow_5 = pallas::Base::from_u64(1 << 5); + let two_pow_5 = pallas::Base::from(1 << 5); let two_pow_10 = two_pow_5.square(); // a_whole is constrained by Sinsemilla to be 250 bits. @@ -101,7 +101,7 @@ impl MerkleChip { let z1_a = meta.query_advice(advices[0], Rotation::next()); let a_1 = z1_a; // a_0 = a - (a_1 * 2^10) - let a_0 = a_whole - a_1.clone() * pallas::Base::from_u64(1 << 10); + let a_0 = a_whole - a_1.clone() * pallas::Base::from(1 << 10); let l_check = a_0 - l_whole; // b = b_0||b_1||b_2 @@ -185,12 +185,12 @@ impl MerkleInstructions(l as u64); @@ -363,7 +347,7 @@ impl MerkleInstructions for MerkleChip { - type Var = CellValue; + type Var = AssignedCell; } impl CondSwapInstructions for MerkleChip { diff --git a/src/circuit/gadget/sinsemilla/message.rs b/src/circuit/gadget/sinsemilla/message.rs index ac2926d9..2581453a 100644 --- a/src/circuit/gadget/sinsemilla/message.rs +++ b/src/circuit/gadget/sinsemilla/message.rs @@ -1,7 +1,9 @@ //! Gadget and chips for the Sinsemilla hash function. -use crate::circuit::gadget::utilities::{CellValue, Var}; use ff::PrimeFieldBits; -use halo2::{arithmetic::FieldExt, circuit::Cell}; +use halo2::{ + arithmetic::FieldExt, + circuit::{AssignedCell, Cell}, +}; use std::fmt::Debug; /// A [`Message`] composed of several [`MessagePiece`]s. @@ -32,17 +34,16 @@ impl std:: /// /// The piece must fit within a base field element, which means its length /// cannot exceed the base field's `NUM_BITS`. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct MessagePiece { - cell_value: CellValue, + cell_value: AssignedCell, /// The number of K-bit words in this message piece. num_words: usize, } impl MessagePiece { - pub fn new(cell: Cell, field_elem: Option, num_words: usize) -> Self { + pub fn new(cell_value: AssignedCell, num_words: usize) -> Self { assert!(num_words * K < F::NUM_BITS as usize); - let cell_value = CellValue::new(cell, field_elem); Self { cell_value, num_words, @@ -58,10 +59,10 @@ impl MessagePiece { } pub fn field_elem(&self) -> Option { - self.cell_value.value() + self.cell_value.value().cloned() } - pub fn cell_value(&self) -> CellValue { - self.cell_value + pub fn cell_value(&self) -> AssignedCell { + self.cell_value.clone() } } diff --git a/src/circuit/gadget/sinsemilla/note_commit.rs b/src/circuit/gadget/sinsemilla/note_commit.rs index 89174f4d..9efbd4cc 100644 --- a/src/circuit/gadget/sinsemilla/note_commit.rs +++ b/src/circuit/gadget/sinsemilla/note_commit.rs @@ -1,5 +1,5 @@ use halo2::{ - circuit::Layouter, + circuit::{AssignedCell, Layouter}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, poly::Rotation, }; @@ -11,7 +11,7 @@ use crate::{ chip::{EccChip, NonIdentityEccPoint}, Point, }, - utilities::{bitrange_subset, bool_check, copy, CellValue, Var}, + utilities::{bitrange_subset, bool_check}, }, constants::T_P, }; @@ -21,6 +21,13 @@ use super::{ CommitDomain, Message, MessagePiece, }; +/// The values of the running sum at the start and end of the range being used for a +/// canonicity check. +type CanonicityBounds = ( + AssignedCell, + AssignedCell, +); + /* We need to hash g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi, @@ -89,15 +96,15 @@ impl NoteCommitConfig { }; // Useful constants - let two = pallas::Base::from_u64(2); - let two_pow_2 = pallas::Base::from_u64(1 << 2); + let two = pallas::Base::from(2); + let two_pow_2 = pallas::Base::from(1 << 2); let two_pow_4 = two_pow_2.square(); let two_pow_5 = two_pow_4 * two; let two_pow_6 = two_pow_5 * two; let two_pow_8 = two_pow_4.square(); let two_pow_9 = two_pow_8 * two; let two_pow_10 = two_pow_9 * two; - let two_pow_58 = pallas::Base::from_u64(1 << 58); + let two_pow_58 = pallas::Base::from(1 << 58); let two_pow_130 = Expression::Constant(pallas::Base::from_u128(1 << 65).square()); let two_pow_140 = Expression::Constant(pallas::Base::from_u128(1 << 70).square()); let two_pow_249 = pallas::Base::from_u128(1 << 124).square() * two; @@ -525,13 +532,16 @@ impl NoteCommitConfig { ecc_chip: EccChip, g_d: &NonIdentityEccPoint, pk_d: &NonIdentityEccPoint, - value: CellValue, - rho: CellValue, - psi: CellValue, + // TODO: Set V to Orchard value type + value: AssignedCell, + rho: AssignedCell, + psi: AssignedCell, rcm: Option, ) -> Result, Error> { - let (gd_x, gd_y) = (g_d.x().value(), g_d.y().value()); - let (pkd_x, pkd_y) = (pk_d.x().value(), pk_d.y().value()); + let (gd_x, gd_y) = (g_d.x(), g_d.y()); + let (pkd_x, pkd_y) = (pk_d.x(), pk_d.y()); + let (gd_x, gd_y) = (gd_x.value(), gd_y.value()); + let (pkd_x, pkd_y) = (pkd_x.value(), pkd_y.value()); let value_val = value.value(); let rho_val = rho.value(); let psi_val = psi.value(); @@ -569,9 +579,9 @@ impl NoteCommitConfig { let b = b_0.value().zip(b_1).zip(b_2).zip(b_3.value()).map( |(((b_0, b_1), b_2), b_3)| { - let b1_shifted = b_1 * pallas::Base::from_u64(1 << 4); - let b2_shifted = b_2 * pallas::Base::from_u64(1 << 5); - let b3_shifted = b_3 * pallas::Base::from_u64(1 << 6); + let b1_shifted = b_1 * pallas::Base::from(1 << 4); + let b2_shifted = b_2 * pallas::Base::from(1 << 5); + let b3_shifted = b_3 * pallas::Base::from(1 << 6); b_0 + b1_shifted + b2_shifted + b3_shifted }, ); @@ -611,9 +621,9 @@ impl NoteCommitConfig { .zip(d_2.value()) .zip(d_3) .map(|(((d_0, d_1), d_2), d_3)| { - let d1_shifted = d_1 * pallas::Base::from_u64(2); - let d2_shifted = d_2 * pallas::Base::from_u64(1 << 2); - let d3_shifted = d_3 * pallas::Base::from_u64(1 << 10); + let d1_shifted = d_1 * pallas::Base::from(2); + let d2_shifted = d_2 * pallas::Base::from(1 << 2); + let d3_shifted = d_3 * pallas::Base::from(1 << 10); d_0 + d1_shifted + d2_shifted + d3_shifted }); @@ -644,7 +654,7 @@ impl NoteCommitConfig { let e = e_0 .value() .zip(e_1.value()) - .map(|(e_0, e_1)| e_0 + e_1 * pallas::Base::from_u64(1 << 6)); + .map(|(e_0, e_1)| e_0 + e_1 * pallas::Base::from(1 << 6)); let e = MessagePiece::from_field_elem(chip.clone(), layouter.namespace(|| "e"), e, 1)?; (e_0, e_1, e) @@ -674,8 +684,8 @@ impl NoteCommitConfig { // g_2 = z1_g from the SinsemillaHash(g) running sum output. let g = g_0.zip(g_1.value()).zip(g_2).map(|((g_0, g_1), g_2)| { - let g1_shifted = g_1 * pallas::Base::from_u64(2); - let g2_shifted = g_2 * pallas::Base::from_u64(1 << 10); + let g1_shifted = g_1 * pallas::Base::from(2); + let g2_shifted = g_2 * pallas::Base::from(1 << 10); g_0 + g1_shifted + g2_shifted }); let g = MessagePiece::from_field_elem(chip.clone(), layouter.namespace(|| "g"), g, 25)?; @@ -701,7 +711,7 @@ impl NoteCommitConfig { let h = h_0 .value() .zip(h_1) - .map(|(h_0, h_1)| h_0 + h_1 * pallas::Base::from_u64(1 << 5)); + .map(|(h_0, h_1)| h_0 + h_1 * pallas::Base::from(1 << 5)); let h = MessagePiece::from_field_elem(chip.clone(), layouter.namespace(|| "h"), h, 1)?; (h_0, h_1, h) @@ -738,13 +748,13 @@ impl NoteCommitConfig { )? }; - let z13_a = zs[0][13]; - let z13_c = zs[2][13]; - let z1_d = zs[3][1]; - let z13_f = zs[5][13]; - let z1_g = zs[6][1]; - let g_2 = z1_g; - let z13_g = zs[6][13]; + let z13_a = zs[0][13].clone(); + let z13_c = zs[2][13].clone(); + let z1_d = zs[3][1].clone(); + let z13_f = zs[5][13].clone(); + let z1_g = zs[6][1].clone(); + let g_2 = z1_g.clone(); + let z13_g = zs[6][13].clone(); let (a_prime, z13_a_prime) = self.canon_bitshift_130( layouter.namespace(|| "x(g_d) canonicity"), @@ -753,18 +763,18 @@ impl NoteCommitConfig { let (b3_c_prime, z14_b3_c_prime) = self.pkd_x_canonicity( layouter.namespace(|| "x(pk_d) canonicity"), - b_3, + b_3.clone(), c.inner().cell_value(), )?; let (e1_f_prime, z14_e1_f_prime) = self.rho_canonicity( layouter.namespace(|| "rho canonicity"), - e_1, + e_1.clone(), f.inner().cell_value(), )?; let (g1_g2_prime, z13_g1_g2_prime) = - self.psi_canonicity(layouter.namespace(|| "psi canonicity"), g_1, g_2)?; + self.psi_canonicity(layouter.namespace(|| "psi canonicity"), g_1.clone(), g_2)?; let gate_cells = GateCells { a: a.inner().cell_value(), @@ -814,13 +824,12 @@ impl NoteCommitConfig { Ok(cm) } - #[allow(clippy::type_complexity)] // A canonicity check helper used in checking x(g_d), y(g_d), and y(pk_d). fn canon_bitshift_130( &self, mut layouter: impl Layouter, - a: CellValue, - ) -> Result<(CellValue, CellValue), Error> { + a: AssignedCell, + ) -> Result { // element = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)` // - b_1 = 1 => b_0 = 0 // - b_1 = 1 => a < t_P @@ -841,19 +850,19 @@ impl NoteCommitConfig { 13, false, )?; - let a_prime = zs[0]; + let a_prime = zs[0].clone(); assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13] - Ok((a_prime, zs[13])) + Ok((a_prime, zs[13].clone())) } // Check canonicity of `x(pk_d)` encoding fn pkd_x_canonicity( &self, mut layouter: impl Layouter, - b_3: CellValue, - c: CellValue, - ) -> Result<(CellValue, CellValue), Error> { + b_3: AssignedCell, + c: AssignedCell, + ) -> Result { // `x(pk_d)` = `b_3 (4 bits) || c (250 bits) || d_0 (1 bit)` // - d_0 = 1 => b_3 + 2^4 c < t_P // - 0 ≤ b_3 + 2^4 c < 2^134 @@ -868,7 +877,7 @@ impl NoteCommitConfig { // and output the running sum at the end of it. // If b3_c_prime < 2^140, the running sum will be 0. let b3_c_prime = b_3.value().zip(c.value()).map(|(b_3, c)| { - let two_pow_4 = pallas::Base::from_u64(1u64 << 4); + let two_pow_4 = pallas::Base::from(1u64 << 4); let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square(); let t_p = pallas::Base::from_u128(T_P); b_3 + (two_pow_4 * c) + two_pow_140 - t_p @@ -880,20 +889,19 @@ impl NoteCommitConfig { 14, false, )?; - let b3_c_prime = zs[0]; + let b3_c_prime = zs[0].clone(); assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] - Ok((b3_c_prime, zs[14])) + Ok((b3_c_prime, zs[14].clone())) } - #[allow(clippy::type_complexity)] // Check canonicity of `rho` encoding fn rho_canonicity( &self, mut layouter: impl Layouter, - e_1: CellValue, - f: CellValue, - ) -> Result<(CellValue, CellValue), Error> { + e_1: AssignedCell, + f: AssignedCell, + ) -> Result { // `rho` = `e_1 (4 bits) || f (250 bits) || g_0 (1 bit)` // - g_0 = 1 => e_1 + 2^4 f < t_P // - 0 ≤ e_1 + 2^4 f < 2^134 @@ -905,7 +913,7 @@ impl NoteCommitConfig { // - 0 ≤ e_1 + 2^4 f + 2^140 - t_P < 2^140 (14 ten-bit lookups) let e1_f_prime = e_1.value().zip(f.value()).map(|(e_1, f)| { - let two_pow_4 = pallas::Base::from_u64(1u64 << 4); + let two_pow_4 = pallas::Base::from(1u64 << 4); let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square(); let t_p = pallas::Base::from_u128(T_P); e_1 + (two_pow_4 * f) + two_pow_140 - t_p @@ -920,19 +928,19 @@ impl NoteCommitConfig { 14, false, )?; - let e1_f_prime = zs[0]; + let e1_f_prime = zs[0].clone(); assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] - Ok((e1_f_prime, zs[14])) + Ok((e1_f_prime, zs[14].clone())) } // Check canonicity of `psi` encoding fn psi_canonicity( &self, mut layouter: impl Layouter, - g_1: CellValue, - g_2: CellValue, - ) -> Result<(CellValue, CellValue), Error> { + g_1: AssignedCell, + g_2: AssignedCell, + ) -> Result { // `psi` = `g_1 (9 bits) || g_2 (240 bits) || h_0 (5 bits) || h_1 (1 bit)` // - h_1 = 1 => (h_0 = 0) ∧ (g_1 + 2^9 g_2 < t_P) // - 0 ≤ g_1 + 2^9 g_2 < 2^130 @@ -945,7 +953,7 @@ impl NoteCommitConfig { // and output the running sum at the end of it. // If g1_g2_prime < 2^130, the running sum will be 0. let g1_g2_prime = g_1.value().zip(g_2.value()).map(|(g_1, g_2)| { - let two_pow_9 = pallas::Base::from_u64(1u64 << 9); + let two_pow_9 = pallas::Base::from(1u64 << 9); let two_pow_130 = pallas::Base::from_u128(1u128 << 65).square(); let t_p = pallas::Base::from_u128(T_P); g_1 + (two_pow_9 * g_2) + two_pow_130 - t_p @@ -957,10 +965,10 @@ impl NoteCommitConfig { 13, false, )?; - let g1_g2_prime = zs[0]; + let g1_g2_prime = zs[0].clone(); assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13] - Ok((g1_g2_prime, zs[13])) + Ok((g1_g2_prime, zs[13].clone())) } // Check canonicity of y-coordinate given its LSB as a value. @@ -968,9 +976,9 @@ impl NoteCommitConfig { fn y_canonicity( &self, mut layouter: impl Layouter, - y: CellValue, + y: AssignedCell, lsb: Option, - ) -> Result, Error> { + ) -> Result, Error> { // Decompose the field element // y = LSB || k_0 || k_1 || k_2 || k_3 // = (bit 0) || (bits 1..=9) || (bits 10..=249) || (bits 250..=253) || (bit 254) @@ -1000,8 +1008,8 @@ impl NoteCommitConfig { // Decompose j = LSB + (2)k_0 + (2^10)k_1 using 25 ten-bit lookups. let (j, z1_j, z13_j) = { let j = lsb.zip(k_0.value()).zip(k_1).map(|((lsb, k_0), k_1)| { - let two = pallas::Base::from_u64(2); - let two_pow_10 = pallas::Base::from_u64(1 << 10); + let two = pallas::Base::from(2); + 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( @@ -1010,13 +1018,15 @@ impl NoteCommitConfig { 25, true, )?; - (zs[0], zs[1], zs[13]) + (zs[0].clone(), zs[1].clone(), zs[13].clone()) }; // 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(layouter.namespace(|| "j_prime = j + 2^130 - t_P"), j)?; + let (j_prime, z13_j_prime) = self.canon_bitshift_130( + layouter.namespace(|| "j_prime = j + 2^130 - t_P"), + j.clone(), + )?; /* @@ -1037,27 +1047,24 @@ impl NoteCommitConfig { let offset = 0; // Copy y. - copy(&mut region, || "copy y", self.advices[5], offset, &y)?; + y.copy_advice(|| "copy y", &mut region, self.advices[5], offset)?; // Witness LSB. - let lsb = { - let cell = region.assign_advice( - || "witness LSB", - self.advices[6], - offset, - || lsb.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, lsb) - }; + let lsb = region.assign_advice( + || "witness LSB", + self.advices[6], + offset, + || lsb.ok_or(Error::Synthesis), + )?; // Witness k_0. - copy(&mut region, || "copy k_0", self.advices[7], offset, &k_0)?; + k_0.copy_advice(|| "copy k_0", &mut region, self.advices[7], offset)?; // Copy k_2. - copy(&mut region, || "copy k_2", self.advices[8], offset, &k_2)?; + k_2.copy_advice(|| "copy k_2", &mut region, self.advices[8], offset)?; // Witness k_3. region.assign_advice( || "witness k_3", self.advices[9], offset, - || k_3.ok_or(Error::SynthesisError), + || k_3.ok_or(Error::Synthesis), )?; lsb @@ -1068,32 +1075,19 @@ impl NoteCommitConfig { let offset = 1; // Copy j. - copy(&mut region, || "copy j", self.advices[5], offset, &j)?; + j.copy_advice(|| "copy j", &mut region, self.advices[5], offset)?; // Copy z1_j. - copy(&mut region, || "copy z1_j", self.advices[6], offset, &z1_j)?; + z1_j.copy_advice(|| "copy z1_j", &mut region, self.advices[6], offset)?; // Copy z13_j. - copy( - &mut region, - || "copy z13_j", - self.advices[7], - offset, - &z13_j, - )?; + z13_j.copy_advice(|| "copy z13_j", &mut region, self.advices[7], offset)?; // Copy j_prime. - copy( - &mut region, - || "copy j_prime", - self.advices[8], - offset, - &j_prime, - )?; + j_prime.copy_advice(|| "copy j_prime", &mut region, self.advices[8], offset)?; // Copy z13_j_prime. - copy( - &mut region, + z13_j_prime.copy_advice( || "copy z13_j_prime", + &mut region, self.advices[9], offset, - &z13_j_prime, )?; } @@ -1122,20 +1116,23 @@ impl NoteCommitConfig { |mut region| { self.q_notecommit_b.enable(&mut region, 0)?; - copy(&mut region, || "b", col_l, 0, &gate_cells.b)?; - copy(&mut region, || "b_0", col_m, 0, &gate_cells.b_0)?; - let b_1 = { - let cell = region.assign_advice( - || "b_1", - col_r, - 0, - || gate_cells.b_1.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, gate_cells.b_1) - }; + gate_cells.b.copy_advice(|| "b", &mut region, col_l, 0)?; + gate_cells + .b_0 + .copy_advice(|| "b_0", &mut region, col_m, 0)?; + let b_1 = region.assign_advice( + || "b_1", + col_r, + 0, + || gate_cells.b_1.ok_or(Error::Synthesis), + )?; - copy(&mut region, || "b_2", col_m, 1, &gate_cells.b_2)?; - copy(&mut region, || "b_3", col_r, 1, &gate_cells.b_3)?; + gate_cells + .b_2 + .copy_advice(|| "b_2", &mut region, col_m, 1)?; + gate_cells + .b_3 + .copy_advice(|| "b_3", &mut region, col_r, 1)?; Ok(b_1) }, @@ -1150,20 +1147,23 @@ impl NoteCommitConfig { |mut region| { self.q_notecommit_d.enable(&mut region, 0)?; - copy(&mut region, || "d", col_l, 0, &gate_cells.d)?; - let d_0 = { - let cell = region.assign_advice( - || "d_0", - col_m, - 0, - || gate_cells.d_0.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, gate_cells.d_0) - }; - copy(&mut region, || "d_1", col_r, 0, &gate_cells.d_1)?; + gate_cells.d.copy_advice(|| "d", &mut region, col_l, 0)?; + let d_0 = region.assign_advice( + || "d_0", + col_m, + 0, + || gate_cells.d_0.ok_or(Error::Synthesis), + )?; + gate_cells + .d_1 + .copy_advice(|| "d_1", &mut region, col_r, 0)?; - copy(&mut region, || "d_2", col_m, 1, &gate_cells.d_2)?; - copy(&mut region, || "d_3 = z1_d", col_r, 1, &gate_cells.z1_d)?; + gate_cells + .d_2 + .copy_advice(|| "d_2", &mut region, col_m, 1)?; + gate_cells + .z1_d + .copy_advice(|| "d_3 = z1_d", &mut region, col_r, 1)?; Ok(d_0) }, @@ -1177,9 +1177,13 @@ impl NoteCommitConfig { |mut region| { self.q_notecommit_e.enable(&mut region, 0)?; - copy(&mut region, || "e", col_l, 0, &gate_cells.e)?; - copy(&mut region, || "e_0", col_m, 0, &gate_cells.e_0)?; - copy(&mut region, || "e_1", col_r, 0, &gate_cells.e_1)?; + gate_cells.e.copy_advice(|| "e", &mut region, col_l, 0)?; + gate_cells + .e_0 + .copy_advice(|| "e_0", &mut region, col_m, 0)?; + gate_cells + .e_1 + .copy_advice(|| "e_1", &mut region, col_r, 0)?; Ok(()) }, @@ -1194,19 +1198,20 @@ impl NoteCommitConfig { |mut region| { self.q_notecommit_g.enable(&mut region, 0)?; - copy(&mut region, || "g", col_l, 0, &gate_cells.g)?; - let g_0 = { - let cell = region.assign_advice( - || "g_0", - col_m, - 0, - || gate_cells.g_0.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, gate_cells.g_0) - }; + gate_cells.g.copy_advice(|| "g", &mut region, col_l, 0)?; + let g_0 = region.assign_advice( + || "g_0", + col_m, + 0, + || gate_cells.g_0.ok_or(Error::Synthesis), + )?; - copy(&mut region, || "g_1", col_l, 1, &gate_cells.g_1)?; - copy(&mut region, || "g_2 = z1_g", col_m, 1, &gate_cells.z1_g)?; + gate_cells + .g_1 + .copy_advice(|| "g_1", &mut region, col_l, 1)?; + gate_cells + .z1_g + .copy_advice(|| "g_2 = z1_g", &mut region, col_m, 1)?; Ok(g_0) }, @@ -1220,17 +1225,16 @@ impl NoteCommitConfig { |mut region| { self.q_notecommit_h.enable(&mut region, 0)?; - copy(&mut region, || "h", col_l, 0, &gate_cells.h)?; - copy(&mut region, || "h_0", col_m, 0, &gate_cells.h_0)?; - let h_1 = { - let cell = region.assign_advice( - || "h_1", - col_r, - 0, - || gate_cells.h_1.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, gate_cells.h_1) - }; + gate_cells.h.copy_advice(|| "h", &mut region, col_l, 0)?; + gate_cells + .h_0 + .copy_advice(|| "h_0", &mut region, col_m, 0)?; + let h_1 = region.assign_advice( + || "h_1", + col_r, + 0, + || gate_cells.h_1.ok_or(Error::Synthesis), + )?; Ok(h_1) }, @@ -1243,22 +1247,26 @@ impl NoteCommitConfig { layouter.assign_region( || "NoteCommit input g_d", |mut region| { - copy(&mut region, || "gd_x", col_l, 0, &gate_cells.gd_x)?; + gate_cells + .gd_x + .copy_advice(|| "gd_x", &mut region, col_l, 0)?; - copy(&mut region, || "b_0", col_m, 0, &gate_cells.b_0)?; - copy(&mut region, || "b_1", col_m, 1, &b_1)?; + gate_cells + .b_0 + .copy_advice(|| "b_0", &mut region, col_m, 0)?; + b_1.copy_advice(|| "b_1", &mut region, col_m, 1)?; - copy(&mut region, || "a", col_r, 0, &gate_cells.a)?; - copy(&mut region, || "a_prime", col_r, 1, &gate_cells.a_prime)?; + gate_cells.a.copy_advice(|| "a", &mut region, col_r, 0)?; + gate_cells + .a_prime + .copy_advice(|| "a_prime", &mut region, col_r, 1)?; - copy(&mut region, || "z13_a", col_z, 0, &gate_cells.z13_a)?; - copy( - &mut region, - || "z13_a_prime", - col_z, - 1, - &gate_cells.z13_a_prime, - )?; + gate_cells + .z13_a + .copy_advice(|| "z13_a", &mut region, col_z, 0)?; + gate_cells + .z13_a_prime + .copy_advice(|| "z13_a_prime", &mut region, col_z, 1)?; self.q_notecommit_g_d.enable(&mut region, 0) }, @@ -1271,27 +1279,28 @@ impl NoteCommitConfig { layouter.assign_region( || "NoteCommit input pk_d", |mut region| { - copy(&mut region, || "pkd_x", col_l, 0, &gate_cells.pkd_x)?; + gate_cells + .pkd_x + .copy_advice(|| "pkd_x", &mut region, col_l, 0)?; - copy(&mut region, || "b_3", col_m, 0, &gate_cells.b_3)?; - copy(&mut region, || "d_0", col_m, 1, &d_0)?; + gate_cells + .b_3 + .copy_advice(|| "b_3", &mut region, col_m, 0)?; + d_0.copy_advice(|| "d_0", &mut region, col_m, 1)?; - copy(&mut region, || "c", col_r, 0, &gate_cells.c)?; - copy( - &mut region, - || "b3_c_prime", - col_r, - 1, - &gate_cells.b3_c_prime, - )?; + gate_cells.c.copy_advice(|| "c", &mut region, col_r, 0)?; + gate_cells + .b3_c_prime + .copy_advice(|| "b3_c_prime", &mut region, col_r, 1)?; - copy(&mut region, || "z13_c", col_z, 0, &gate_cells.z13_c)?; - copy( - &mut region, + gate_cells + .z13_c + .copy_advice(|| "z13_c", &mut region, col_z, 0)?; + gate_cells.z14_b3_c_prime.copy_advice( || "z14_b3_c_prime", + &mut region, col_z, 1, - &gate_cells.z14_b3_c_prime, )?; self.q_notecommit_pk_d.enable(&mut region, 0) @@ -1302,10 +1311,18 @@ impl NoteCommitConfig { layouter.assign_region( || "NoteCommit input value", |mut region| { - copy(&mut region, || "value", col_l, 0, &gate_cells.value)?; - copy(&mut region, || "d_2", col_m, 0, &gate_cells.d_2)?; - copy(&mut region, || "d3 = z1_d", col_r, 0, &gate_cells.z1_d)?; - copy(&mut region, || "e_0", col_z, 0, &gate_cells.e_0)?; + gate_cells + .value + .copy_advice(|| "value", &mut region, col_l, 0)?; + gate_cells + .d_2 + .copy_advice(|| "d_2", &mut region, col_m, 0)?; + gate_cells + .z1_d + .copy_advice(|| "d3 = z1_d", &mut region, col_r, 0)?; + gate_cells + .e_0 + .copy_advice(|| "e_0", &mut region, col_z, 0)?; self.q_notecommit_value.enable(&mut region, 0) }, @@ -1318,27 +1335,28 @@ impl NoteCommitConfig { layouter.assign_region( || "NoteCommit input rho", |mut region| { - copy(&mut region, || "rho", col_l, 0, &gate_cells.rho)?; + gate_cells + .rho + .copy_advice(|| "rho", &mut region, col_l, 0)?; - copy(&mut region, || "e_1", col_m, 0, &gate_cells.e_1)?; - copy(&mut region, || "g_0", col_m, 1, &g_0)?; + gate_cells + .e_1 + .copy_advice(|| "e_1", &mut region, col_m, 0)?; + g_0.copy_advice(|| "g_0", &mut region, col_m, 1)?; - copy(&mut region, || "f", col_r, 0, &gate_cells.f)?; - copy( - &mut region, - || "e1_f_prime", - col_r, - 1, - &gate_cells.e1_f_prime, - )?; + gate_cells.f.copy_advice(|| "f", &mut region, col_r, 0)?; + gate_cells + .e1_f_prime + .copy_advice(|| "e1_f_prime", &mut region, col_r, 1)?; - copy(&mut region, || "z13_f", col_z, 0, &gate_cells.z13_f)?; - copy( - &mut region, + gate_cells + .z13_f + .copy_advice(|| "z13_f", &mut region, col_z, 0)?; + gate_cells.z14_e1_f_prime.copy_advice( || "z14_e1_f_prime", + &mut region, col_z, 1, - &gate_cells.z14_e1_f_prime, )?; self.q_notecommit_rho.enable(&mut region, 0) @@ -1352,28 +1370,33 @@ impl NoteCommitConfig { layouter.assign_region( || "NoteCommit input psi", |mut region| { - copy(&mut region, || "psi", col_l, 0, &gate_cells.psi)?; - copy(&mut region, || "h_0", col_l, 1, &gate_cells.h_0)?; + gate_cells + .psi + .copy_advice(|| "psi", &mut region, col_l, 0)?; + gate_cells + .h_0 + .copy_advice(|| "h_0", &mut region, col_l, 1)?; - copy(&mut region, || "g_1", col_m, 0, &gate_cells.g_1)?; - copy(&mut region, || "h_1", col_m, 1, &h_1)?; + gate_cells + .g_1 + .copy_advice(|| "g_1", &mut region, col_m, 0)?; + h_1.copy_advice(|| "h_1", &mut region, col_m, 1)?; - copy(&mut region, || "g_2 = z1_g", col_r, 0, &gate_cells.z1_g)?; - copy( - &mut region, - || "g1_g2_prime", - col_r, - 1, - &gate_cells.g1_g2_prime, - )?; + gate_cells + .z1_g + .copy_advice(|| "g_2 = z1_g", &mut region, col_r, 0)?; + gate_cells + .g1_g2_prime + .copy_advice(|| "g1_g2_prime", &mut region, col_r, 1)?; - copy(&mut region, || "z13_g", col_z, 0, &gate_cells.z13_g)?; - copy( - &mut region, + gate_cells + .z13_g + .copy_advice(|| "z13_g", &mut region, col_z, 0)?; + gate_cells.z13_g1_g2_prime.copy_advice( || "z13_g1_g2_prime", + &mut region, col_z, 1, - &gate_cells.z13_g1_g2_prime, )?; self.q_notecommit_psi.enable(&mut region, 0) @@ -1383,46 +1406,46 @@ impl NoteCommitConfig { } struct GateCells { - a: CellValue, - b: CellValue, - b_0: CellValue, + a: AssignedCell, + b: AssignedCell, + b_0: AssignedCell, b_1: Option, - b_2: CellValue, - b_3: CellValue, - c: CellValue, - d: CellValue, + b_2: AssignedCell, + b_3: AssignedCell, + c: AssignedCell, + d: AssignedCell, d_0: Option, - d_1: CellValue, - d_2: CellValue, - z1_d: CellValue, - e: CellValue, - e_0: CellValue, - e_1: CellValue, - f: CellValue, - g: CellValue, + d_1: AssignedCell, + d_2: AssignedCell, + z1_d: AssignedCell, + e: AssignedCell, + e_0: AssignedCell, + e_1: AssignedCell, + f: AssignedCell, + g: AssignedCell, g_0: Option, - g_1: CellValue, - z1_g: CellValue, - h: CellValue, - h_0: CellValue, + g_1: AssignedCell, + z1_g: AssignedCell, + h: AssignedCell, + h_0: AssignedCell, h_1: Option, - gd_x: CellValue, - pkd_x: CellValue, - value: CellValue, - rho: CellValue, - psi: CellValue, - a_prime: CellValue, - b3_c_prime: CellValue, - e1_f_prime: CellValue, - g1_g2_prime: CellValue, - z13_a_prime: CellValue, - z14_b3_c_prime: CellValue, - z14_e1_f_prime: CellValue, - z13_g1_g2_prime: CellValue, - z13_a: CellValue, - z13_c: CellValue, - z13_f: CellValue, - z13_g: CellValue, + gd_x: AssignedCell, + pkd_x: AssignedCell, + value: AssignedCell, + rho: AssignedCell, + psi: AssignedCell, + a_prime: AssignedCell, + b3_c_prime: AssignedCell, + e1_f_prime: AssignedCell, + g1_g2_prime: AssignedCell, + z13_a_prime: AssignedCell, + z14_b3_c_prime: AssignedCell, + z14_e1_f_prime: AssignedCell, + z13_g1_g2_prime: AssignedCell, + z13_a: AssignedCell, + z13_c: AssignedCell, + z13_f: AssignedCell, + z13_g: AssignedCell, } #[cfg(test)] @@ -1435,9 +1458,7 @@ mod tests { NonIdentityPoint, }, sinsemilla::chip::SinsemillaChip, - utilities::{ - lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, - }, + utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions}, }, constants::{L_ORCHARD_BASE, L_VALUE, NOTE_COMMITMENT_PERSONALIZATION, T_Q}, primitives::sinsemilla::CommitDomain, @@ -1446,7 +1467,7 @@ mod tests { use ff::{Field, PrimeField, PrimeFieldBits}; use group::Curve; use halo2::{ - circuit::{Layouter, SimpleFloorPlanner}, + circuit::{AssignedCell, Layouter, SimpleFloorPlanner}, dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; @@ -1471,7 +1492,7 @@ mod tests { } impl UtilitiesInstructions for MyCircuit { - type Var = CellValue; + type Var = AssignedCell; } impl Circuit for MyCircuit { @@ -1528,7 +1549,7 @@ mod tests { advices[2], lagrange_coeffs[0], lookup, - range_check.clone(), + range_check, ); let note_commit_config = NoteCommitConfig::configure(meta, advices, sinsemilla_config); @@ -1595,7 +1616,7 @@ mod tests { // A note value cannot be negative. let value = { let mut rng = OsRng; - pallas::Base::from_u64(rng.next_u64()) + pallas::Base::from(rng.next_u64()) }; let value_var = { self.load_private( diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index eed3c61e..e84a808b 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -1,45 +1,42 @@ +//! Utility gadgets. + use ff::PrimeFieldBits; use halo2::{ - circuit::{Cell, Layouter, Region}, + circuit::{AssignedCell, Cell, Layouter}, plonk::{Advice, Column, Error, Expression}, }; use pasta_curves::arithmetic::FieldExt; -use std::{array, convert::TryInto, ops::Range}; +use std::{array, ops::Range}; pub(crate) mod cond_swap; pub(crate) mod decompose_running_sum; pub(crate) mod lookup_range_check; -/// A variable representing a field element. -#[derive(Copy, Clone, Debug)] -pub struct CellValue { - cell: Cell, - value: Option, -} - -pub trait Var: Copy + Clone + std::fmt::Debug { - fn new(cell: Cell, value: Option) -> Self; +/// Trait for a variable in the circuit. +pub trait Var: Clone + std::fmt::Debug + From> { + /// The cell at which this variable was allocated. fn cell(&self) -> Cell; + + /// The value allocated to this variable. fn value(&self) -> Option; } -impl Var for CellValue { - fn new(cell: Cell, value: Option) -> Self { - Self { cell, value } - } - +impl Var for AssignedCell { fn cell(&self) -> Cell { - self.cell + self.cell() } fn value(&self) -> Option { - self.value + self.value().cloned() } } +/// Trait for utilities used across circuits. pub trait UtilitiesInstructions { + /// Variable in the circuit. type Var: Var; + /// Load a variable. fn load_private( &self, mut layouter: impl Layouter, @@ -49,46 +46,20 @@ pub trait UtilitiesInstructions { layouter.assign_region( || "load private", |mut region| { - let cell = region.assign_advice( - || "load private", - column, - 0, - || value.ok_or(Error::SynthesisError), - )?; - Ok(Var::new(cell, value)) + region + .assign_advice( + || "load private", + column, + 0, + || value.ok_or(Error::Synthesis), + ) + .map(Self::Var::from) }, ) } } -/// Assigns a cell at a specific offset within the given region, constraining it -/// to the same value as another cell (which may be in any region). -/// -/// Returns an error if either `column` or `copy` is not in a column that was passed to -/// [`ConstraintSystem::enable_equality`] during circuit configuration. -/// -/// [`ConstraintSystem::enable_equality`]: halo2::plonk::ConstraintSystem::enable_equality -pub fn copy( - region: &mut Region<'_, F>, - annotation: A, - column: Column, - offset: usize, - copy: &CellValue, -) -> Result, Error> -where - A: Fn() -> AR, - AR: Into, -{ - let cell = region.assign_advice(annotation, column, offset, || { - copy.value.ok_or(Error::SynthesisError) - })?; - - region.constrain_equal(cell, copy.cell)?; - - Ok(CellValue::new(cell, copy.value)) -} - -pub fn transpose_option_array( +pub(crate) fn transpose_option_array( option_array: Option<[T; LEN]>, ) -> [Option; LEN] { let mut ret = [None; LEN]; @@ -102,36 +73,45 @@ pub fn transpose_option_array( /// Checks that an expresssion is either 1 or 0. pub fn bool_check(value: Expression) -> Expression { - value.clone() * (Expression::Constant(F::one()) - value) + range_check(value, 2) +} + +/// If `a` then `b`, else `c`. Returns (a * b) + (1 - a) * c. +/// +/// `a` must be a boolean-constrained expression. +pub fn ternary(a: Expression, b: Expression, c: Expression) -> Expression { + let one_minus_a = Expression::Constant(F::one()) - a.clone(); + a * b + one_minus_a * c } /// Takes a specified subsequence of the little-endian bit representation of a field element. /// The bits are numbered from 0 for the LSB. -pub fn bitrange_subset(field_elem: F, bitrange: Range) -> F { +pub fn bitrange_subset(field_elem: &F, bitrange: Range) -> F { + // We can allow a subsequence of length NUM_BITS, because + // field_elem.to_le_bits() returns canonical bitstrings. assert!(bitrange.end <= F::NUM_BITS as usize); - let bits: Vec = field_elem + field_elem .to_le_bits() .iter() .by_val() .skip(bitrange.start) .take(bitrange.end - bitrange.start) - .chain(std::iter::repeat(false)) - .take(256) - .collect(); - let bytearray: Vec = bits - .chunks_exact(8) - .map(|byte| byte.iter().rev().fold(0u8, |acc, bit| acc * 2 + *bit as u8)) - .collect(); - - F::from_bytes(&bytearray.try_into().unwrap()).unwrap() + .rev() + .fold(F::zero(), |acc, bit| { + if bit { + acc.double() + F::one() + } else { + acc.double() + } + }) } /// Check that an expression is in the small range [0..range), /// i.e. 0 ≤ word < range. pub fn range_check(word: Expression, range: usize) -> Expression { (1..range).fold(word.clone(), |acc, i| { - acc * (word.clone() - Expression::Constant(F::from_u64(i as u64))) + acc * (Expression::Constant(F::from(i as u64)) - word.clone()) }) } @@ -143,7 +123,7 @@ mod tests { use halo2::{ circuit::{Layouter, SimpleFloorPlanner}, dev::{MockProver, VerifyFailure}, - plonk::{Circuit, ConstraintSystem, Error, Selector}, + plonk::{Any, Circuit, ConstraintSystem, Error, Selector}, poly::Rotation, }; use pasta_curves::pallas; @@ -153,7 +133,7 @@ mod tests { struct MyCircuit(u8); impl UtilitiesInstructions for MyCircuit { - type Var = CellValue; + type Var = AssignedCell; } #[derive(Clone)] @@ -197,7 +177,7 @@ mod tests { || format!("witness {}", self.0), config.advice, 0, - || Ok(pallas::Base::from_u64(self.0.into())), + || Ok(pallas::Base::from(self.0 as u64)), )?; Ok(()) @@ -219,7 +199,8 @@ mod tests { prover.verify(), Err(vec![VerifyFailure::ConstraintNotSatisfied { constraint: ((0, "range check").into(), 0, "").into(), - row: 0 + row: 0, + cell_values: vec![(((Any::Advice, 0).into(), 0).into(), "0x8".to_string())], }]) ); } @@ -231,7 +212,7 @@ mod tests { { let field_elem = pallas::Base::rand(); let bitrange = 0..(pallas::Base::NUM_BITS as usize); - let subset = bitrange_subset(field_elem, bitrange); + let subset = bitrange_subset(&field_elem, bitrange); assert_eq!(field_elem, subset); } @@ -239,7 +220,7 @@ mod tests { { let field_elem = pallas::Base::rand(); let bitrange = 0..0; - let subset = bitrange_subset(field_elem, bitrange); + let subset = bitrange_subset(&field_elem, bitrange); assert_eq!(pallas::Base::zero(), subset); } @@ -266,7 +247,7 @@ mod tests { let subsets = ranges .iter() - .map(|range| bitrange_subset(field_elem, range.clone())) + .map(|range| bitrange_subset(&field_elem, range.clone())) .collect::>(); let mut sum = subsets[0]; @@ -281,7 +262,7 @@ mod tests { .to_little_endian(&mut range_shift); range_shift }; - sum += subset * pallas::Base::from_bytes(&range_shift).unwrap(); + sum += subset * pallas::Base::from_repr(range_shift).unwrap(); } assert_eq!(field_elem, sum); }; diff --git a/src/circuit/gadget/utilities/cond_swap.rs b/src/circuit/gadget/utilities/cond_swap.rs index 4d387cab..003d9ab8 100644 --- a/src/circuit/gadget/utilities/cond_swap.rs +++ b/src/circuit/gadget/utilities/cond_swap.rs @@ -1,7 +1,7 @@ -use super::{copy, CellValue, UtilitiesInstructions, Var}; +use super::{bool_check, ternary, UtilitiesInstructions}; use halo2::{ - circuit::{Chip, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, + circuit::{AssignedCell, Chip, Layouter}, + plonk::{Advice, Column, ConstraintSystem, Error, Selector}, poly::Rotation, }; use pasta_curves::arithmetic::FieldExt; @@ -53,7 +53,7 @@ pub struct CondSwapConfig { } impl UtilitiesInstructions for CondSwapChip { - type Var = CellValue; + type Var = AssignedCell; } impl CondSwapInstructions for CondSwapChip { @@ -73,64 +73,55 @@ impl CondSwapInstructions for CondSwapChip { config.q_swap.enable(&mut region, 0)?; // Copy in `a` value - let a = copy(&mut region, || "copy a", config.a, 0, &pair.0)?; + let a = pair.0.copy_advice(|| "copy a", &mut region, config.a, 0)?; // Witness `b` value - let b = { - let cell = region.assign_advice( - || "witness b", - config.b, - 0, - || pair.1.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, pair.1) - }; + let b = region.assign_advice( + || "witness b", + config.b, + 0, + || pair.1.ok_or(Error::Synthesis), + )?; // Witness `swap` value - let swap_val = swap.map(|swap| F::from_u64(swap as u64)); + let swap_val = swap.map(|swap| F::from(swap as u64)); region.assign_advice( || "swap", config.swap, 0, - || swap_val.ok_or(Error::SynthesisError), + || swap_val.ok_or(Error::Synthesis), )?; // Conditionally swap a let a_swapped = { let a_swapped = a - .value - .zip(b.value) + .value() + .zip(b.value()) .zip(swap) - .map(|((a, b), swap)| if swap { b } else { a }); - let a_swapped_cell = region.assign_advice( + .map(|((a, b), swap)| if swap { b } else { a }) + .cloned(); + region.assign_advice( || "a_swapped", config.a_swapped, 0, - || a_swapped.ok_or(Error::SynthesisError), - )?; - CellValue { - cell: a_swapped_cell, - value: a_swapped, - } + || a_swapped.ok_or(Error::Synthesis), + )? }; // Conditionally swap b let b_swapped = { let b_swapped = a - .value - .zip(b.value) + .value() + .zip(b.value()) .zip(swap) - .map(|((a, b), swap)| if swap { a } else { b }); - let b_swapped_cell = region.assign_advice( + .map(|((a, b), swap)| if swap { a } else { b }) + .cloned(); + region.assign_advice( || "b_swapped", config.b_swapped, 0, - || b_swapped.ok_or(Error::SynthesisError), - )?; - CellValue { - cell: b_swapped_cell, - value: b_swapped, - } + || b_swapped.ok_or(Error::Synthesis), + )? }; // Return swapped pair @@ -176,21 +167,16 @@ impl CondSwapChip { let b_swapped = meta.query_advice(config.b_swapped, Rotation::cur()); let swap = meta.query_advice(config.swap, Rotation::cur()); - let one = Expression::Constant(F::one()); - - // a_swapped - b ⋅ swap - a ⋅ (1-swap) = 0 - // This checks that `a_swapped` is equal to `y` when `swap` is set, + // This checks that `a_swapped` is equal to `b` when `swap` is set, // but remains as `a` when `swap` is not set. - let a_check = - a_swapped - b.clone() * swap.clone() - a.clone() * (one.clone() - swap.clone()); + let a_check = a_swapped - ternary(swap.clone(), b.clone(), a.clone()); - // b_swapped - a ⋅ swap - b ⋅ (1-swap) = 0 // This checks that `b_swapped` is equal to `a` when `swap` is set, // but remains as `b` when `swap` is not set. - let b_check = b_swapped - a * swap.clone() - b * (one.clone() - swap.clone()); + let b_check = b_swapped - ternary(swap.clone(), a, b); // Check `swap` is boolean. - let bool_check = swap.clone() * (one - swap); + let bool_check = bool_check(swap); array::IntoIter::new([a_check, b_check, bool_check]) .map(move |poly| q_swap.clone() * poly) @@ -257,18 +243,21 @@ mod tests { // Load the pair and the swap flag into the circuit. let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?; // Return the swapped pair. - let swapped_pair = - chip.swap(layouter.namespace(|| "swap"), (a, self.b), self.swap)?; + let swapped_pair = chip.swap( + layouter.namespace(|| "swap"), + (a.clone(), self.b), + self.swap, + )?; if let Some(swap) = self.swap { if swap { // Check that `a` and `b` have been swapped - assert_eq!(swapped_pair.0.value.unwrap(), self.b.unwrap()); - assert_eq!(swapped_pair.1.value.unwrap(), a.value.unwrap()); + assert_eq!(swapped_pair.0.value().unwrap(), &self.b.unwrap()); + assert_eq!(swapped_pair.1.value().unwrap(), a.value().unwrap()); } else { // Check that `a` and `b` have not been swapped - assert_eq!(swapped_pair.0.value.unwrap(), a.value.unwrap()); - assert_eq!(swapped_pair.1.value.unwrap(), self.b.unwrap()); + assert_eq!(swapped_pair.0.value().unwrap(), a.value().unwrap()); + assert_eq!(swapped_pair.1.value().unwrap(), &self.b.unwrap()); } } diff --git a/src/circuit/gadget/utilities/decompose_running_sum.rs b/src/circuit/gadget/utilities/decompose_running_sum.rs index fd8c3556..b9bc6f8a 100644 --- a/src/circuit/gadget/utilities/decompose_running_sum.rs +++ b/src/circuit/gadget/utilities/decompose_running_sum.rs @@ -24,29 +24,29 @@ use ff::PrimeFieldBits; use halo2::{ - circuit::Region, + circuit::{AssignedCell, Region}, plonk::{Advice, Column, ConstraintSystem, Error, Selector}, poly::Rotation, }; -use super::{copy, range_check, CellValue, Var}; +use super::range_check; use crate::constants::util::decompose_word; use pasta_curves::arithmetic::FieldExt; use std::marker::PhantomData; /// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$. -pub struct RunningSum(Vec>); +pub struct RunningSum(Vec>); impl std::ops::Deref for RunningSum { - type Target = Vec>; + type Target = Vec>; - fn deref(&self) -> &Vec> { + fn deref(&self) -> &Vec> { &self.0 } } -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct RunningSumConfig { - q_range_check: Selector, + pub q_range_check: Selector, pub z: Column, _marker: PhantomData, } @@ -84,7 +84,7 @@ impl let z_next = meta.query_advice(config.z, Rotation::next()); // z_i = 2^{K}⋅z_{i + 1} + k_i // => k_i = z_i - 2^{K}⋅z_{i + 1} - let word = z_cur - z_next * F::from_u64(1 << WINDOW_NUM_BITS); + let word = z_cur - z_next * F::from(1 << WINDOW_NUM_BITS); vec![q_range_check * range_check(word, 1 << WINDOW_NUM_BITS)] }); @@ -105,15 +105,12 @@ impl word_num_bits: usize, num_windows: usize, ) -> Result, Error> { - let z_0 = { - let cell = region.assign_advice( - || "z_0 = alpha", - self.z, - offset, - || alpha.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, alpha) - }; + let z_0 = region.assign_advice( + || "z_0 = alpha", + self.z, + offset, + || alpha.ok_or(Error::Synthesis), + )?; self.decompose(region, offset, z_0, strict, word_num_bits, num_windows) } @@ -125,12 +122,12 @@ impl &self, region: &mut Region<'_, F>, offset: usize, - alpha: CellValue, + alpha: AssignedCell, strict: bool, word_num_bits: usize, num_windows: usize, ) -> Result, Error> { - let z_0 = copy(region, || "copy z_0 = alpha", self.z, offset, &alpha)?; + let z_0 = alpha.copy_advice(|| "copy z_0 = alpha", region, self.z, offset)?; self.decompose(region, offset, z_0, strict, word_num_bits, num_windows) } @@ -143,7 +140,7 @@ impl &self, region: &mut Region<'_, F>, offset: usize, - z_0: CellValue, + z_0: AssignedCell, strict: bool, word_num_bits: usize, num_windows: usize, @@ -179,33 +176,32 @@ impl }; // Initialize empty vector to store running sum values [z_0, ..., z_W]. - let mut zs: Vec> = vec![z_0]; + let mut zs: Vec> = vec![z_0.clone()]; let mut z = z_0; // Assign running sum `z_{i+1}` = (z_i - k_i) / (2^K) for i = 0..=n-1. // Outside of this helper, z_0 = alpha must have already been loaded into the // `z` column at `offset`. - let two_pow_k_inv = F::from_u64(1 << WINDOW_NUM_BITS as u64).invert().unwrap(); + let two_pow_k_inv = F::from(1 << WINDOW_NUM_BITS as u64).invert().unwrap(); for (i, word) in words.iter().enumerate() { // z_next = (z_cur - word) / (2^K) let z_next = { - let word = word.map(|word| F::from_u64(word as u64)); + let word = word.map(|word| F::from(word as u64)); let z_next_val = z .value() .zip(word) - .map(|(z_cur_val, word)| (z_cur_val - word) * two_pow_k_inv); - let cell = region.assign_advice( + .map(|(z_cur_val, word)| (*z_cur_val - word) * two_pow_k_inv); + region.assign_advice( || format!("z_{:?}", i + 1), self.z, offset + i + 1, - || z_next_val.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, z_next_val) + || z_next_val.ok_or(Error::Synthesis), + )? }; // Update `z`. z = z_next; - zs.push(z); + zs.push(z.clone()); } assert_eq!(zs.len(), num_windows + 1); @@ -284,7 +280,7 @@ mod tests { WORD_NUM_BITS, NUM_WINDOWS, )?; - let alpha = zs[0]; + let alpha = zs[0].clone(); let offset = offset + NUM_WINDOWS + 1; @@ -323,7 +319,7 @@ mod tests { // Random 64-bit word { - let alpha = pallas::Base::from_u64(rand::random()); + let alpha = pallas::Base::from(rand::random::()); // Strict full decomposition should pass. let circuit: MyCircuit< diff --git a/src/circuit/gadget/utilities/lookup_range_check.rs b/src/circuit/gadget/utilities/lookup_range_check.rs index 1b7fd682..04596ff5 100644 --- a/src/circuit/gadget/utilities/lookup_range_check.rs +++ b/src/circuit/gadget/utilities/lookup_range_check.rs @@ -3,7 +3,7 @@ use crate::spec::lebs2ip; use halo2::{ - circuit::{Layouter, Region}, + circuit::{AssignedCell, Layouter, Region}, plonk::{Advice, Column, ConstraintSystem, Error, Selector, TableColumn}, poly::Rotation, }; @@ -14,16 +14,16 @@ use ff::PrimeFieldBits; use super::*; /// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$. -pub struct RunningSum(Vec>); +pub struct RunningSum(Vec>); impl std::ops::Deref for RunningSum { - type Target = Vec>; + type Target = Vec>; - fn deref(&self) -> &Vec> { + fn deref(&self) -> &Vec> { &self.0 } } -#[derive(Eq, PartialEq, Debug, Clone)] +#[derive(Eq, PartialEq, Debug, Clone, Copy)] pub struct LookupRangeCheckConfig { pub q_lookup: Selector, pub q_running: Selector, @@ -76,7 +76,7 @@ impl LookupRangeCheckConfig let running_sum_lookup = { let running_sum_word = { let z_next = meta.query_advice(config.running_sum, Rotation::next()); - z_cur.clone() - z_next * F::from_u64(1 << K) + z_cur.clone() - z_next * F::from(1 << K) }; q_running.clone() * running_sum_word @@ -104,7 +104,7 @@ impl LookupRangeCheckConfig let shifted_word = meta.query_advice(config.running_sum, Rotation::cur()); let inv_two_pow_s = meta.query_advice(config.running_sum, Rotation::next()); - let two_pow_k = F::from_u64(1 << K); + let two_pow_k = F::from(1 << K); // shifted_word = word * 2^{K-s} // = word * 2^K * inv_two_pow_s @@ -128,7 +128,7 @@ impl LookupRangeCheckConfig || "table_idx", self.table_idx, index, - || Ok(F::from_u64(index as u64)), + || Ok(F::from(index as u64)), )?; } Ok(()) @@ -143,7 +143,7 @@ impl LookupRangeCheckConfig pub fn copy_check( &self, mut layouter: impl Layouter, - element: CellValue, + element: AssignedCell, num_words: usize, strict: bool, ) -> Result, Error> { @@ -151,7 +151,7 @@ impl LookupRangeCheckConfig || format!("{:?} words range check", num_words), |mut region| { // 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)?; + let z_0 = element.copy_advice(|| "z_0", &mut region, self.running_sum, 0)?; self.range_check(&mut region, z_0, num_words, strict) }, ) @@ -168,15 +168,12 @@ impl LookupRangeCheckConfig layouter.assign_region( || "Witness element", |mut region| { - let z_0 = { - let cell = region.assign_advice( - || "Witness element", - self.running_sum, - 0, - || value.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, value) - }; + let z_0 = region.assign_advice( + || "Witness element", + self.running_sum, + 0, + || value.ok_or(Error::Synthesis), + )?; self.range_check(&mut region, z_0, num_words, strict) }, ) @@ -192,7 +189,7 @@ impl LookupRangeCheckConfig fn range_check( &self, region: &mut Region<'_, F>, - element: CellValue, + element: AssignedCell, num_words: usize, strict: bool, ) -> Result, Error> { @@ -213,7 +210,7 @@ impl LookupRangeCheckConfig let words: Option> = bits.map(|bits| { bits.chunks_exact(K) - .map(|word| F::from_u64(lebs2ip::(&(word.try_into().unwrap())))) + .map(|word| F::from(lebs2ip::(&(word.try_into().unwrap())))) .collect::>() }); if let Some(words) = words { @@ -223,7 +220,7 @@ impl LookupRangeCheckConfig } }; - let mut zs = vec![element]; + let mut zs = vec![element.clone()]; // Assign cumulative sum such that // z_i = 2^{K}⋅z_{i + 1} + a_i @@ -232,7 +229,7 @@ impl LookupRangeCheckConfig // For `element` = a_0 + 2^10 a_1 + ... + 2^{120} a_{12}}, initialize z_0 = `element`. // If `element` fits in 130 bits, we end up with z_{13} = 0. let mut z = element; - let inv_two_pow_k = F::from_u64(1u64 << K).invert().unwrap(); + let inv_two_pow_k = F::from(1u64 << K).invert().unwrap(); for (idx, word) in words.iter().enumerate() { // Enable q_lookup on this row self.q_lookup.enable(region, idx)?; @@ -244,19 +241,17 @@ impl LookupRangeCheckConfig let z_val = z .value() .zip(*word) - .map(|(z, word)| (z - word) * inv_two_pow_k); + .map(|(z, word)| (*z - word) * inv_two_pow_k); // Assign z_next - let z_cell = region.assign_advice( + region.assign_advice( || format!("z_{:?}", idx + 1), self.running_sum, idx + 1, - || z_val.ok_or(Error::SynthesisError), - )?; - - CellValue::new(z_cell, z_val) + || z_val.ok_or(Error::Synthesis), + )? }; - zs.push(z); + zs.push(z.clone()); } if strict { @@ -275,7 +270,7 @@ impl LookupRangeCheckConfig pub fn copy_short_check( &self, mut layouter: impl Layouter, - element: CellValue, + element: AssignedCell, num_bits: usize, ) -> Result<(), Error> { assert!(num_bits < K); @@ -283,7 +278,8 @@ impl LookupRangeCheckConfig || format!("Range check {:?} bits", num_bits), |mut region| { // Copy `element` to use in the k-bit lookup. - let element = copy(&mut region, || "element", self.running_sum, 0, &element)?; + let element = + element.copy_advice(|| "element", &mut region, self.running_sum, 0)?; self.short_range_check(&mut region, element, num_bits) }, @@ -300,23 +296,20 @@ impl LookupRangeCheckConfig mut layouter: impl Layouter, element: Option, num_bits: usize, - ) -> Result, Error> { + ) -> Result, Error> { assert!(num_bits <= K); layouter.assign_region( || format!("Range check {:?} bits", num_bits), |mut region| { // Witness `element` to use in the k-bit lookup. - let element = { - let cell = region.assign_advice( - || "Witness element", - self.running_sum, - 0, - || element.ok_or(Error::SynthesisError), - )?; - CellValue::new(cell, element) - }; + let element = region.assign_advice( + || "Witness element", + self.running_sum, + 0, + || element.ok_or(Error::Synthesis), + )?; - self.short_range_check(&mut region, element, num_bits)?; + self.short_range_check(&mut region, element.clone(), num_bits)?; Ok(element) }, @@ -329,7 +322,7 @@ impl LookupRangeCheckConfig fn short_range_check( &self, region: &mut Region<'_, F>, - element: CellValue, + element: AssignedCell, num_bits: usize, ) -> Result<(), Error> { // Enable lookup for `element`, to constrain it to 10 bits. @@ -343,19 +336,19 @@ impl LookupRangeCheckConfig // Assign shifted `element * 2^{K - num_bits}` let shifted = element.value().map(|element| { - let shift = F::from_u64(1 << (K - num_bits)); - element * shift + let shift = F::from(1 << (K - num_bits)); + *element * shift }); region.assign_advice( || format!("element * 2^({}-{})", K, num_bits), self.running_sum, 1, - || shifted.ok_or(Error::SynthesisError), + || shifted.ok_or(Error::Synthesis), )?; // Assign 2^{-num_bits} from a fixed column. - let inv_two_pow_s = F::from_u64(1 << num_bits).invert().unwrap(); + let inv_two_pow_s = F::from(1 << num_bits).invert().unwrap(); region.assign_advice_from_constant( || format!("2^(-{})", num_bits), self.running_sum, @@ -369,10 +362,9 @@ impl LookupRangeCheckConfig #[cfg(test)] mod tests { - use super::super::Var; use super::LookupRangeCheckConfig; - use crate::primitives::sinsemilla::{INV_TWO_POW_K, K}; + use crate::primitives::sinsemilla::K; use crate::spec::lebs2ip; use ff::{Field, PrimeFieldBits}; use halo2::{ @@ -419,12 +411,8 @@ mod tests { // Lookup constraining element to be no longer than num_words * K bits. let elements_and_expected_final_zs = [ - ( - F::from_u64((1 << (self.num_words * K)) - 1), - F::zero(), - true, - ), // a word that is within self.num_words * K bits long - (F::from_u64(1 << (self.num_words * K)), F::one(), false), // a word that is just over self.num_words * K bits long + (F::from((1 << (self.num_words * K)) - 1), F::zero(), true), // a word that is within self.num_words * K bits long + (F::from(1 << (self.num_words * K)), F::one(), false), // a word that is just over self.num_words * K bits long ]; fn expected_zs( @@ -439,11 +427,11 @@ mod tests { .take(num_words * K) .collect::>() .chunks_exact(K) - .map(|chunk| F::from_u64(lebs2ip::(chunk.try_into().unwrap()))) + .map(|chunk| F::from(lebs2ip::(chunk.try_into().unwrap()))) .collect::>() }; let expected_zs = { - let inv_two_pow_k = F::from_bytes(&INV_TWO_POW_K).unwrap(); + let inv_two_pow_k = F::from(1 << K).invert().unwrap(); chunks.iter().fold(vec![element], |mut zs, a_i| { // z_{i + 1} = (z_i - a_i) / 2^{K} let z = (zs[zs.len() - 1] - a_i) * inv_two_pow_k; @@ -468,7 +456,7 @@ mod tests { for (expected_z, z) in expected_zs.into_iter().zip(zs.iter()) { if let Some(z) = z.value() { - assert_eq!(expected_z, z); + assert_eq!(&expected_z, z); } } } @@ -546,7 +534,7 @@ mod tests { // Edge case: K bits { let circuit: MyCircuit = MyCircuit { - element: Some(pallas::Base::from_u64((1 << K) - 1)), + element: Some(pallas::Base::from((1 << K) - 1)), num_bits: K, }; let prover = MockProver::::run(11, &circuit, vec![]).unwrap(); @@ -556,7 +544,7 @@ mod tests { // Element within `num_bits` { let circuit: MyCircuit = MyCircuit { - element: Some(pallas::Base::from_u64((1 << 6) - 1)), + element: Some(pallas::Base::from((1 << 6) - 1)), num_bits: 6, }; let prover = MockProver::::run(11, &circuit, vec![]).unwrap(); @@ -566,7 +554,7 @@ mod tests { // Element larger than `num_bits` but within K bits { let circuit: MyCircuit = MyCircuit { - element: Some(pallas::Base::from_u64(1 << 6)), + element: Some(pallas::Base::from(1 << 6)), num_bits: 6, }; let prover = MockProver::::run(11, &circuit, vec![]).unwrap(); @@ -582,7 +570,7 @@ mod tests { // Element larger than K bits { let circuit: MyCircuit = MyCircuit { - element: Some(pallas::Base::from_u64(1 << K)), + element: Some(pallas::Base::from(1 << K)), num_bits: 6, }; let prover = MockProver::::run(11, &circuit, vec![]).unwrap(); @@ -605,11 +593,11 @@ mod tests { // num_bits { let num_bits = 6; - let shifted = pallas::Base::from_u64((1 << num_bits) - 1); + let shifted = pallas::Base::from((1 << num_bits) - 1); // Recall that shifted = element * 2^{K-s} // => element = shifted * 2^{s-K} let element = shifted - * pallas::Base::from_u64(1 << (K as u64 - num_bits)) + * pallas::Base::from(1 << (K as u64 - num_bits)) .invert() .unwrap(); let circuit: MyCircuit = MyCircuit { diff --git a/src/circuit_description b/src/circuit_description index 509f45df..9ff4e74f 100644 --- a/src/circuit_description +++ b/src/circuit_description @@ -37,11 +37,15 @@ PinnedVerificationKey { column_type: Fixed, }, Column { - index: 20, + index: 19, column_type: Fixed, }, Column { - index: 20, + index: 19, + column_type: Fixed, + }, + Column { + index: 19, column_type: Fixed, }, Column { @@ -65,15 +69,15 @@ PinnedVerificationKey { column_type: Fixed, }, Column { - index: 21, + index: 20, column_type: Fixed, }, Column { - index: 21, + index: 20, column_type: Fixed, }, Column { - index: 21, + index: 20, column_type: Fixed, }, Column { @@ -84,24 +88,20 @@ PinnedVerificationKey { index: 22, column_type: Fixed, }, + Column { + index: 23, + column_type: Fixed, + }, Column { index: 21, column_type: Fixed, }, Column { - index: 23, + index: 21, column_type: Fixed, }, Column { - index: 23, - column_type: Fixed, - }, - Column { - index: 23, - column_type: Fixed, - }, - Column { - index: 23, + index: 21, column_type: Fixed, }, Column { @@ -940,266 +940,26 @@ PinnedVerificationKey { ), ), ), - Product( - Fixed { - query_index: 19, - column_index: 19, - rotation: Rotation( - 0, - ), - }, - Product( - Product( - Product( - Product( - Product( - Product( - Product( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, - ), - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, - ), - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000006, - ), - ), - ), - ), - Sum( - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Scaled( - Advice { - query_index: 12, - column_index: 4, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000007, - ), - ), - ), - ), - ), Product( Product( Product( Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1213,8 +973,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1228,8 +988,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1304,20 +1064,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1331,8 +1091,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1346,8 +1106,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1421,8 +1181,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1433,8 +1193,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1444,12 +1204,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1463,8 +1223,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1530,20 +1290,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1553,12 +1313,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1572,8 +1332,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1587,7 +1347,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -1694,20 +1454,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1717,12 +1477,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1736,8 +1496,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1749,7 +1509,7 @@ PinnedVerificationKey { Product( Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -1812,7 +1572,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -1829,8 +1589,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1841,8 +1601,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1852,12 +1612,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1867,12 +1627,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1955,8 +1715,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1967,8 +1727,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1978,12 +1738,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -1993,12 +1753,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2094,8 +1854,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2106,8 +1866,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2117,12 +1877,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2132,12 +1892,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2223,7 +1983,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -2238,8 +1998,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2250,8 +2010,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2261,12 +2021,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2276,12 +2036,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2346,7 +2106,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -2367,7 +2127,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -2382,8 +2142,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2394,8 +2154,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2405,12 +2165,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2420,12 +2180,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2509,7 +2269,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -2524,8 +2284,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2536,8 +2296,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2547,12 +2307,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2562,12 +2322,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2630,7 +2390,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -2651,7 +2411,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -2666,8 +2426,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2678,8 +2438,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2689,12 +2449,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2704,12 +2464,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2743,7 +2503,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -2766,8 +2526,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2778,8 +2538,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2789,12 +2549,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2804,12 +2564,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2843,7 +2603,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -2866,8 +2626,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2878,8 +2638,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2889,12 +2649,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2904,12 +2664,112 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -2944,106 +2804,6 @@ PinnedVerificationKey { Sum( Advice { query_index: 13, - column_index: 2, - rotation: Rotation( - 1, - ), - }, - Negated( - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - ), - Product( - Product( - Product( - Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - Negated( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, - ), - Negated( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Product( - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Product( - Advice { - query_index: 2, - column_index: 2, - rotation: Rotation( - 0, - ), - }, - Advice { - query_index: 7, - column_index: 7, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Advice { - query_index: 14, column_index: 3, rotation: Rotation( 1, @@ -3066,8 +2826,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -3078,8 +2838,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -3089,12 +2849,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -3104,12 +2864,141 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 20, - column_index: 20, + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Product( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, rotation: Rotation( 0, ), @@ -3183,135 +3072,6 @@ PinnedVerificationKey { ), Advice { query_index: 13, - column_index: 2, - rotation: Rotation( - 1, - ), - }, - ), - ), - Product( - Product( - Product( - Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - Negated( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, - ), - Negated( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Product( - Sum( - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Product( - Sum( - Advice { - query_index: 2, - column_index: 2, - rotation: Rotation( - 0, - ), - }, - Negated( - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - ), - ), - Advice { - query_index: 5, - column_index: 5, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Negated( - Product( - Sum( - Advice { - query_index: 3, - column_index: 3, - rotation: Rotation( - 0, - ), - }, - Advice { - query_index: 1, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - ), - Advice { - query_index: 8, - column_index: 8, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Advice { - query_index: 14, column_index: 3, rotation: Rotation( 1, @@ -3319,434 +3079,6 @@ PinnedVerificationKey { }, ), ), - Product( - Product( - Product( - Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Product( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - ), - ), - ), - ), - Product( - Product( - Product( - Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Product( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - ), - Product( - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - ), - ), - Sum( - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - Negated( - Advice { - query_index: 15, - column_index: 0, - rotation: Rotation( - 1, - ), - }, - ), - ), - ), - ), - ), - Product( - Product( - Product( - Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Product( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - Advice { - query_index: 1, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - ), - Product( - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - ), - ), - Sum( - Advice { - query_index: 1, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - Advice { - query_index: 16, - column_index: 1, - rotation: Rotation( - 1, - ), - }, - ), - ), - ), - ), Product( Product( Product( @@ -3848,14 +3180,14 @@ PinnedVerificationKey { Product( Sum( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, ), }, Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -3864,7 +3196,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -3875,14 +3207,14 @@ PinnedVerificationKey { Sum( Product( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, ), }, Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -3891,7 +3223,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -3901,7 +3233,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -4015,7 +3347,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -4122,7 +3454,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -4612,7 +3944,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -4780,7 +4112,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -4867,14 +4199,14 @@ PinnedVerificationKey { Product( Sum( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, ), }, Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -4883,7 +4215,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -4894,14 +4226,14 @@ PinnedVerificationKey { Sum( Product( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, ), }, Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -4910,7 +4242,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -4920,7 +4252,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -5417,7 +4749,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -5585,7 +4917,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -5669,7 +5001,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -5682,16 +5014,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -5706,7 +5054,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -5721,7 +5069,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -5754,7 +5102,7 @@ PinnedVerificationKey { ), }, Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -5800,7 +5148,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -5820,16 +5168,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -5844,7 +5208,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -5859,7 +5223,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -5882,7 +5246,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -5895,16 +5259,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -5919,7 +5299,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -5934,7 +5314,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -5957,7 +5337,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -5970,16 +5350,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -5994,7 +5390,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -6009,7 +5405,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -6078,16 +5474,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -6102,7 +5514,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -6117,7 +5529,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -6278,16 +5690,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -6302,7 +5730,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -6317,7 +5745,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -6415,16 +5843,32 @@ PinnedVerificationKey { Product( Product( Product( - Fixed { - query_index: 20, - column_index: 20, - rotation: Rotation( - 0, + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), ), - }, + ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { @@ -6439,7 +5883,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { @@ -6454,7 +5898,7 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000005, ), Negated( Fixed { @@ -6581,7 +6025,7 @@ PinnedVerificationKey { ), }, Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -6627,7 +6071,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -6649,20 +6093,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6672,12 +6116,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6691,8 +6135,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6706,8 +6150,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6773,20 +6217,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6796,12 +6240,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6815,8 +6259,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6830,8 +6274,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -6989,20 +6433,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7012,12 +6456,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7031,8 +6475,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7046,8 +6490,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7142,20 +6586,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7165,12 +6609,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7184,8 +6628,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7199,8 +6643,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7327,8 +6771,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7339,8 +6783,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7350,12 +6794,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7365,12 +6809,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7384,8 +6828,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7417,113 +6861,11 @@ PinnedVerificationKey { ), ), ), - Sum( - Sum( - Advice { - query_index: 10, - column_index: 9, - rotation: Rotation( - 1, - ), - }, - Negated( - Product( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - Advice { - query_index: 11, - column_index: 9, - rotation: Rotation( - -1, - ), - }, - ), - ), - ), - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - ), - ), - ), - ), - Product( - Product( - Product( - Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), - ), - ), - Sum( - Product( - Sum( Sum( Advice { query_index: 10, @@ -7547,29 +6889,83 @@ PinnedVerificationKey { ), ), ), - Negated( + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, ), ), ), Sum( - Advice { - query_index: 9, - column_index: 9, + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), }, - Advice { - query_index: 21, - column_index: 1, - rotation: Rotation( - -1, - ), - }, ), ), + ), + Sum( Product( Sum( Advice { @@ -7613,6 +7009,54 @@ PinnedVerificationKey { ), ), ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 21, + column_index: 1, + rotation: Rotation( + -1, + ), + }, + ), + ), ), ), Product( @@ -7621,8 +7065,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7633,8 +7077,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7648,8 +7092,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7659,12 +7103,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7674,12 +7118,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7731,8 +7175,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7743,8 +7187,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7758,8 +7202,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7769,12 +7213,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7784,12 +7228,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7829,8 +7273,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7841,8 +7285,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7856,8 +7300,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7867,12 +7311,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7882,12 +7326,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7925,8 +7369,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7937,8 +7381,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7952,8 +7396,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7963,12 +7407,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -7978,12 +7422,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -8014,8 +7458,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -8026,8 +7470,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -8041,8 +7485,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -8052,12 +7496,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, + 0x0000000000000000000000000000000000000000000000000000000000000003, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -8067,12 +7511,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, + 0x0000000000000000000000000000000000000000000000000000000000000004, ), Negated( Fixed { - query_index: 21, - column_index: 21, + query_index: 20, + column_index: 20, rotation: Rotation( 0, ), @@ -8129,10 +7573,630 @@ PinnedVerificationKey { }, ), ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 17, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), Product( Fixed { - query_index: 19, - column_index: 19, + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + ), + Product( + Fixed { + query_index: 22, + column_index: 22, rotation: Rotation( 0, ), @@ -8178,7 +8242,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8215,7 +8279,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8237,7 +8301,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8275,7 +8339,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8297,7 +8361,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8319,7 +8383,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8358,7 +8422,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8380,7 +8444,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8402,7 +8466,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8424,7 +8488,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8464,7 +8528,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8486,7 +8550,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8508,7 +8572,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8530,7 +8594,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8552,7 +8616,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8593,7 +8657,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8615,7 +8679,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8637,7 +8701,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8659,7 +8723,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8681,7 +8745,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8703,7 +8767,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8745,7 +8809,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8767,7 +8831,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8789,7 +8853,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8811,7 +8875,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8833,7 +8897,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8855,7 +8919,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8877,7 +8941,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -8910,8 +8974,8 @@ PinnedVerificationKey { ), Product( Fixed { - query_index: 19, - column_index: 19, + query_index: 22, + column_index: 22, rotation: Rotation( 0, ), @@ -8957,8 +9021,8 @@ PinnedVerificationKey { ), Product( Fixed { - query_index: 19, - column_index: 19, + query_index: 22, + column_index: 22, rotation: Rotation( 0, ), @@ -9018,8 +9082,8 @@ PinnedVerificationKey { ), Product( Fixed { - query_index: 22, - column_index: 22, + query_index: 23, + column_index: 23, rotation: Rotation( 0, ), @@ -9405,8 +9469,8 @@ PinnedVerificationKey { ), Product( Fixed { - query_index: 22, - column_index: 22, + query_index: 23, + column_index: 23, rotation: Rotation( 0, ), @@ -9452,8 +9516,8 @@ PinnedVerificationKey { ), Product( Fixed { - query_index: 22, - column_index: 22, + query_index: 23, + column_index: 23, rotation: Rotation( 0, ), @@ -9513,8 +9577,8 @@ PinnedVerificationKey { ), Product( Fixed { - query_index: 22, - column_index: 22, + query_index: 23, + column_index: 23, rotation: Rotation( 0, ), @@ -9534,6 +9598,25 @@ PinnedVerificationKey { ), }, Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( Advice { query_index: 4, column_index: 4, @@ -9541,14 +9624,14 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - ), ), ), - Sum( + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( Advice { query_index: 4, column_index: 4, @@ -9556,14 +9639,14 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), ), ), - Sum( + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( Advice { query_index: 4, column_index: 4, @@ -9571,14 +9654,14 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), - ), ), ), - Sum( + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( Advice { query_index: 4, column_index: 4, @@ -9586,14 +9669,14 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000004, - ), - ), ), ), - Sum( + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( Advice { query_index: 4, column_index: 4, @@ -9601,14 +9684,14 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000005, - ), - ), ), ), - Sum( + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( Advice { query_index: 4, column_index: 4, @@ -9616,25 +9699,6 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000006, - ), - ), - ), - ), - Sum( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000007, - ), ), ), ), @@ -9643,32 +9707,16 @@ PinnedVerificationKey { Product( Product( Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, ), - ), + }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { @@ -9739,32 +9787,16 @@ PinnedVerificationKey { Product( Product( Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, ), - ), + }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { @@ -9835,32 +9867,16 @@ PinnedVerificationKey { Product( Product( Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, ), - ), + }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { @@ -9944,32 +9960,16 @@ PinnedVerificationKey { Product( Product( Product( - Product( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Fixed { - query_index: 21, - column_index: 21, - rotation: Rotation( - 0, - ), - }, - ), + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, ), - ), + }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { @@ -10045,20 +10045,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10068,12 +10068,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10087,8 +10087,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10118,20 +10118,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10141,12 +10141,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10160,8 +10160,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10207,20 +10207,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10230,12 +10230,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10249,8 +10249,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10289,30 +10289,30 @@ PinnedVerificationKey { ), ), Sum( - Sum( - Advice { - query_index: 19, - column_index: 8, - rotation: Rotation( - 1, - ), - }, - Negated( - Scaled( - Advice { - query_index: 18, - column_index: 7, - rotation: Rotation( - 1, - ), - }, - 0x0000000000000000000000000000000000000000000000000000000000000008, - ), - ), + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, + Sum( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), ), ), ), @@ -10324,20 +10324,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10347,12 +10347,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10366,8 +10366,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10397,20 +10397,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10420,12 +10420,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10439,8 +10439,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10459,6 +10459,25 @@ PinnedVerificationKey { ), }, Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( Advice { query_index: 7, column_index: 7, @@ -10466,14 +10485,14 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - ), ), ), - Sum( + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( Advice { query_index: 7, column_index: 7, @@ -10481,25 +10500,6 @@ PinnedVerificationKey { 0, ), }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, - ), - ), - ), - ), - Sum( - Advice { - query_index: 7, - column_index: 7, - rotation: Rotation( - 0, - ), - }, - Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, - ), ), ), ), @@ -10509,20 +10509,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10532,12 +10532,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10551,8 +10551,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10569,17 +10569,17 @@ PinnedVerificationKey { ), }, Sum( - Advice { - query_index: 8, - column_index: 8, - rotation: Rotation( - 0, - ), - }, + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), Negated( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, ), ), ), @@ -10589,20 +10589,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10612,12 +10612,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10631,8 +10631,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10676,20 +10676,20 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), }, Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000002, + 0x0000000000000000000000000000000000000000000000000000000000000001, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10699,12 +10699,12 @@ PinnedVerificationKey { ), Sum( Constant( - 0x0000000000000000000000000000000000000000000000000000000000000003, + 0x0000000000000000000000000000000000000000000000000000000000000002, ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10718,8 +10718,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10777,8 +10777,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10789,8 +10789,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10804,8 +10804,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -10819,8 +10819,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11122,8 +11122,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11134,8 +11134,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11149,8 +11149,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11164,8 +11164,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11467,8 +11467,8 @@ PinnedVerificationKey { Product( Product( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11479,8 +11479,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11494,8 +11494,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -11509,8 +11509,8 @@ PinnedVerificationKey { ), Negated( Fixed { - query_index: 23, - column_index: 23, + query_index: 21, + column_index: 21, rotation: Rotation( 0, ), @@ -13093,7 +13093,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -13179,7 +13179,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -13295,14 +13295,14 @@ PinnedVerificationKey { Product( Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, ), }, Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -13311,7 +13311,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -13322,14 +13322,14 @@ PinnedVerificationKey { Sum( Product( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, ), }, Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -13338,7 +13338,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -13348,7 +13348,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -13389,7 +13389,7 @@ PinnedVerificationKey { 0x0000000000000000000000000000000000000000000000000000000000000002, ), Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -13457,23 +13457,16 @@ PinnedVerificationKey { ), ), Sum( - Sum( - Advice { - query_index: 2, - column_index: 2, - rotation: Rotation( - 0, - ), - }, - Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( Product( - Advice { - query_index: 1, - column_index: 1, - rotation: Rotation( - 0, - ), - }, Advice { query_index: 4, column_index: 4, @@ -13481,31 +13474,36 @@ PinnedVerificationKey { 0, ), }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, ), - ), - ), - Negated( - Product( - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, ), ), ), @@ -13568,23 +13566,16 @@ PinnedVerificationKey { ), ), Sum( - Sum( - Advice { - query_index: 3, - column_index: 3, - rotation: Rotation( - 0, - ), - }, - Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( Product( - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, Advice { query_index: 4, column_index: 4, @@ -13592,31 +13583,36 @@ PinnedVerificationKey { 0, ), }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, ), - ), - ), - Negated( - Product( - Advice { - query_index: 1, - column_index: 1, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Advice { - query_index: 4, - column_index: 4, - rotation: Rotation( - 0, - ), - }, + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, ), ), ), @@ -13802,7 +13798,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -13814,7 +13810,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -13914,7 +13910,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -13933,7 +13929,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -13945,7 +13941,7 @@ PinnedVerificationKey { ), Scaled( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -14059,7 +14055,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -14177,7 +14173,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -14186,7 +14182,7 @@ PinnedVerificationKey { Negated( Sum( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -14194,7 +14190,7 @@ PinnedVerificationKey { }, Scaled( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -14405,7 +14401,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -14491,7 +14487,7 @@ PinnedVerificationKey { }, Negated( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -14623,7 +14619,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -14650,7 +14646,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -14817,23 +14813,16 @@ PinnedVerificationKey { ), ), Sum( - Sum( - Advice { - query_index: 7, - column_index: 7, - rotation: Rotation( - 0, - ), - }, - Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( Product( - Advice { - query_index: 6, - column_index: 6, - rotation: Rotation( - 0, - ), - }, Advice { query_index: 9, column_index: 9, @@ -14841,31 +14830,36 @@ PinnedVerificationKey { 0, ), }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, ), - ), - ), - Negated( - Product( - Advice { - query_index: 5, - column_index: 5, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, ), ), ), @@ -14976,23 +14970,16 @@ PinnedVerificationKey { ), ), Sum( - Sum( - Advice { - query_index: 8, - column_index: 8, - rotation: Rotation( - 0, - ), - }, - Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( Product( - Advice { - query_index: 5, - column_index: 5, - rotation: Rotation( - 0, - ), - }, Advice { query_index: 9, column_index: 9, @@ -15000,31 +14987,36 @@ PinnedVerificationKey { 0, ), }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, ), - ), - ), - Negated( - Product( - Advice { - query_index: 6, - column_index: 6, - rotation: Rotation( - 0, - ), - }, - Sum( - Constant( - 0x0000000000000000000000000000000000000000000000000000000000000001, - ), - Negated( - Advice { - query_index: 9, - column_index: 9, - rotation: Rotation( - 0, - ), - }, + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), ), + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, ), ), ), @@ -15274,7 +15266,7 @@ PinnedVerificationKey { Negated( Scaled( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -15402,7 +15394,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -15928,7 +15920,7 @@ PinnedVerificationKey { ), Product( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -15940,7 +15932,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -16171,7 +16163,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -16180,7 +16172,7 @@ PinnedVerificationKey { Negated( Sum( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -16188,7 +16180,7 @@ PinnedVerificationKey { }, Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -16432,7 +16424,7 @@ PinnedVerificationKey { }, Scaled( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -16443,7 +16435,7 @@ PinnedVerificationKey { ), Scaled( Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -16454,7 +16446,7 @@ PinnedVerificationKey { ), Scaled( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -16465,7 +16457,7 @@ PinnedVerificationKey { ), Negated( Advice { - query_index: 15, + query_index: 16, column_index: 0, rotation: Rotation( 1, @@ -16998,14 +16990,14 @@ PinnedVerificationKey { ), Product( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, ), }, Advice { - query_index: 14, + query_index: 13, column_index: 3, rotation: Rotation( 1, @@ -17103,7 +17095,7 @@ PinnedVerificationKey { ), Product( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -17219,7 +17211,7 @@ PinnedVerificationKey { }, Scaled( Advice { - query_index: 16, + query_index: 17, column_index: 1, rotation: Rotation( 1, @@ -17339,7 +17331,7 @@ PinnedVerificationKey { ), Product( Advice { - query_index: 12, + query_index: 14, column_index: 4, rotation: Rotation( 1, @@ -21512,7 +21504,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -21669,7 +21661,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -21808,7 +21800,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -26468,7 +26460,7 @@ PinnedVerificationKey { ), Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -26625,7 +26617,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -26764,7 +26756,7 @@ PinnedVerificationKey { Sum( Sum( Advice { - query_index: 17, + query_index: 15, column_index: 5, rotation: Rotation( 1, @@ -27264,15 +27256,6 @@ PinnedVerificationKey { -1, ), ), - ( - Column { - index: 4, - column_type: Advice, - }, - Rotation( - 1, - ), - ), ( Column { index: 2, @@ -27291,6 +27274,24 @@ PinnedVerificationKey { 1, ), ), + ( + Column { + index: 4, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 5, + column_type: Advice, + }, + Rotation( + 1, + ), + ), ( Column { index: 0, @@ -27309,15 +27310,6 @@ PinnedVerificationKey { 1, ), ), - ( - Column { - index: 5, - column_type: Advice, - }, - Rotation( - 1, - ), - ), ( Column { index: 7, @@ -27854,7 +27846,7 @@ PinnedVerificationKey { ), ), Advice { - query_index: 13, + query_index: 12, column_index: 2, rotation: Rotation( 1, @@ -28325,15 +28317,15 @@ PinnedVerificationKey { (0x05f5862cad2888855bc3c1843a9eff57b11b592d9eb0e13354256661387f5231, 0x32236b14df85bf5f532a930232cb23a5c56ef7d67aaeed8bcb8fc10ea132cbd6), (0x3e727e8554679f98120355d39d958dbd8755d5a7f8b42ea87f258064c4b3eb57, 0x0c0d5c23f3ee62ac1b2d986dd3033bbcab260d590e1393de3a14a4b31ae091bb), (0x3748680dd6a91c5dec668b30fb6bf9a450aeb884b81cbc344914f265760e7131, 0x18530eaa5c58b61bd3fc38220ea484c0922524a815a8f752be955c229f9c4164), - (0x0ba01714529dc85dd2460cdbf014b71e7638d18a35eacf06868020c27c28232d, 0x2dcbb2c3191f582b6882cc9c5e6cac1a0361d0534f8f4c814e33a272b8e8da8d), - (0x0f8fb02c096c0957230639133087e8ae516d04d1678ace6ab0495a8e8576c0db, 0x2842b28258744140b09e0d27559d84e1d8f1d55350b0af37237879d14f8eb2a1), - (0x36f2f0410d5ab00d5296bf101cbd91008551d4afe2b5ac45fc437aa157c731aa, 0x19e8c688f0c92a3db3e74b66075d2e0771d5c744e77090c487ffca245a7f7f96), - (0x09ce7fd65bf8af2b554230465003160d3d0b7bfc495f6d33f5f0704cb57d6320, 0x38d3478df8841a4f7a74a3c1fe7788e6e1cbb1076d5f7358be729fa2572c9530), - (0x2abaec8c54bde721f7f2aea685764c80b0b935447c00173ff34209ac8d96b633, 0x36957cf784345bfbdb97f0fad0b74d3e5d683fcc80ee407d6a6bedfe097c0011), - (0x3f1845e758196892c66d920980dc29cc2e1844fa03575668bf84349071094207, 0x1873355bc49aeed1faee56e6d21907a5277fcd0283a3f51fface877e1c59f046), - (0x04e5834f610cf806911a2e9c14519132a49ac55f40095c3156e3b20d09a2f250, 0x2ea666b99d148e1090b42fd2f67fd34c7e94ca505447c1431485eca09ed1fe0b), - (0x35de09079a5b05d25f756479ea044c654c97043bc4a79b34f860faa9b049337a, 0x06c4cc500093cfdc9bd284f3c44a2a8856cfc8ce70786dd65787397292e4fe53), - (0x235b63a940346f07c5a669831067ba59acb3b9936db7d66c4a4cf6746af5f97e, 0x0d838f859fb1386034263d43ace744961a310f548f7274586f133aa4ddc779a9), + (0x18cd12d5f4e12bd7247a8fd7cc93ded7a8a9b60935b319b2db578a8fceef9559, 0x16b15af4fcfb9ed75533e7a4c1966cae5621f10dc1dfbba39a491ec37c911b5e), + (0x37e70146801841d95832259e4d8f39aeee8a364ea5007f82aa84a0e387231315, 0x20ef65833381c985722a95e65125a1123cab3357741168a5ed7e92e972dbd30d), + (0x12e3af8e507a2ca30e544568cf319701ecbc29dc5919f0198d541938edecc8f3, 0x3fb1bb2804faaa4c215e93694d5d1e6f87874cb8c34cb9206ff958de14e44275), + (0x334d99f316343f01c8eb67e327c76f743f8de7f957c893c6f30ccd87e5d1af3a, 0x1da64caf127c8eb513653791147a85ed1edcca12935c95b7d615a9377c9406d8), + (0x1430dca15080286939046d3cb9fb7ace4ed1da2fbe7a362e8f3575489bc6e3e1, 0x1ef5f4aff2902f3bc8acb2cc884a2d44334f910a68b2701f1c37943652b46d8f), + (0x02ebdeac7e91b3d15e4b7c0533e42678672ec384d51e51e22342be7edeeb8074, 0x03638952e0489de03ff03236433f5d2617cb4ddd0a72637ed31095deca85a2a6), + (0x31179b7f5b01ad2a55cf9a66f57b696e9b9cb4919cca426d469b270827f3018b, 0x2b231a9a71b674cc546ba9b36916a13411648903cfdcb808926fc47ee745586c), + (0x0673497660cec8a8c391cfb16f5ffdcb710f9e9e194a1a85935cf4bc45b01359, 0x15ce8801c51811f0256a43f762e497fe38c88866c09bb256289d93e0393dc225), + (0x098d2a0cfedae91fe29e21a01b5e43045b9cc8d9a334f1aee3e075f36a16e323, 0x0be47cc41643c9e6d95012428d5b46ae5a44e765392e05152a0181e30689d8b6), (0x02adb7cbc9ebbbd87d7d6a41fc40cb4cf57585c6243aa204f757c9026ef20fd3, 0x327fc06fee179c6a57ed95336f9fb7854420d80dd191251a40935664ff6c8067), (0x2d00d4ec8aa5e4b3d035131f559e4a97f896e8dbc39badb92b58a8d46b5e91df, 0x37046fb32ed8eb4ba0b4da8e1c9b56cd3832fa2ed4788f7faf4fee1f76a94c32), (0x212f5afd70e787e2fd951e0ddc5430d1fa78f988c30740384d18cf9ff276b43b, 0x20c5a150e200caddf9a35a993668fc4742be5d924d1086f05c74ef6a78b5feb2), @@ -28341,31 +28333,31 @@ PinnedVerificationKey { (0x1f777f0e4263ec4a19f30813739c640335ffa951cc3cc586b6c4095e737f95be, 0x061c07fb12cb19582eefd858a08e689acd970c8cb9ed8f7b928d88e691a2f586), (0x13d0bd76da4ace22c0e90b098d6073551322b8c734bf37eeca67fbf19687f550, 0x3d996cc9da5a31cefb453390b906eabbcc75797bc6a6b9c9e3af2fe7b6b8beed), (0x04cad7405b492a30db0a710c842cecc97d02059acf4c02aa79626dce68ac4837, 0x3d6d7b6698b258e61ebe0b25d9cbcd4a016cb0a2ae8d92752532d98cfb27720d), + (0x1b6f5383c5a0ae5bf457e1c8e17a933e892464d33fef9e9262411e01c117d87e, 0x0c552b960e8ce365b5f2302bcc0b7ce5cdf964e6036602cfc859c8769184f619), + (0x3fa4b5cc33e30de3ac7342c84f47f4fffe7ae77dda1689c2f08050d0ab743cb1, 0x327663e39b128ec28c94486b1e5d4429000626f65230ed572c66f80406a42176), + (0x2184a7d65b5000cc5c5f178c2f4ab5b11a67fdc626771c29ade508020c8da032, 0x34c98ee1f6dfa6c1e8cd915d1efeb484206e13e5e32e13118925913568e620b7), (0x0974ad1a3c0eb4c8d2c59cd820a82b7f28ea2f7a245008d403815131ff30879e, 0x00bb593cdf920cef4965f788d65eba3c3aa07d9718dfb62e3e385849a0d692a8), - (0x3806a39c587dc730cc767713a1ed65cb3171b2b731553d39b84d0896ed46bad9, 0x2b187668497f037b5a72aeeca3b642efc01f536ec6c368c5eef2fe9d54c6cfbb), - (0x171ba8f97a0e12f975056bfb652b5667bc6e2759c4d4fb4de17064ecb60beeaf, 0x055bd6ea2501053cb07723245da65bd46a5bccd52d12073d76ce92e46ab29686), (0x1e355d783cffccafc120f462461fb312773442762383ac444009653f3d8d4be6, 0x3c60e17b18492aa2c41798b409d2bcc1857ca57ee9d2fb0001584cedc8e141d6), - (0x32059fe4e96eb002f24f6e6090014f7b3baf23946cc31a11341543a5a184903c, 0x3793fd512a65c7aa7c17a42e855eb8be26aa644165a9bc963c368baf0e5cce9d), (0x0a6fe1cc1ce659681079768ca8ff94d82c7d51ef39cd99b738b144de3a3027f6, 0x30cfc2f4e0ec95f623199970d8b762647ad2d7c3591a20781ee8187702babe5f), - (0x29085e497c6097147a72a256459a7237a9bc8cb8e66b56a60f4839468f50ab7b, 0x3a8f21bdd0be7b7d0f641745f5008eedf16f02f04df65691045ef7b21d91443c), - (0x1fcae59b79411349d85836f3dfcba5ab523c2aa1426f62ddf80b7f85e41dfce5, 0x3d0eeb91ef1e68f59a6aead5c4e536f7d6a78721666002d0a92b0b923def40c7), - (0x161c09589b8e0e1d1712b98a4b5cea260d48365ab3f2bba6faec85a2b1966fe6, 0x29e201d0935308e8b1b04ef13c0b9b6891948f69da260bc5c8b12b62aa21155d), - (0x0e6d6b798948be5e9577257fcac9226c659ba7d6040b3b5d0c2df7bf541f18b7, 0x169ba3030178091cce287c13af9b83b376988cc95bc0aa51ea387be0e74964dd), + (0x00d87a2c430f1db50a63f18f8cf8807f4f70d3acb940d4130ba6811f8ba2d479, 0x13d5742320e1b2cecda6073b7f2bf5816b9067453deeaa829f356a65ef5621b2), + (0x3118979ade023f3977d034f86eed6506d7e0586ead81f80bc5ca01a7660ee0c9, 0x30f6731193d5c786cf61b05523c05e2664a066c2d39a685588f02883057320ad), + (0x0df51bd411d5f95da66fcc57f5e4d8dbcca3d82b08ceb61e9ff1befa074e08d3, 0x11c9092b6d02c46f173b0108854499ca4922afaf58e0734e77a6088715e84b64), + (0x24289014ede2672df8e8e32eb4e0d71709846041319fb85b1328cdb3b8764565, 0x0833de9c0b76ae816df0e41ae33daece27c63a41f2ba9abbbc7c08724211b50c), ], permutation: VerifyingKey { commitments: [ - (0x0fbd38d3edc5a2548fee21b4aaab8548eb717b2866c3e7905def92ab41123bd3, 0x1f6d299f0181a6cae84d84c093e68e459271fabef938368eb71c928f351e275d), - (0x29f078d82ad78281cabc78f49c808c4f86b22bd4029b3c03c14e2d5d105eeff0, 0x28f20d695afaed04cda27e52460e71c7ddac016f56fba5730c780bb79304b26e), - (0x3b499a325a876c16aaa0a7621b772c600fd310ef3b690a247fbd4cb270a9a127, 0x1c080b1d2561f1283dab49893a96084b421c501f3b52f28a4afb86a90dcad65e), - (0x255ee788dfb0159fd0b0f5f09d1c69e4ec67337a48a75636f52e8bf6efe8994c, 0x09093cdab6ef83ac64bde2b2252a91944801c8d3e0877c526e592f3d6aca168d), - (0x1f86b1c66c980456a5f80fad06bb3ba739590e53951e2ca40aef5157cb637639, 0x20b35b7cd45a239671f7ccf917411f4f703630b4dc9d41192b44f0d84eac59c0), - (0x046fee5aff0ed40810369bbc1d76f9832fc38ae5ea6d5804e5908e162cbb44ae, 0x07ece7ed5973a1c46b3302ea4cca43b9a7f55f2f5d75cab755860ad65928de17), - (0x02f0c3fbef4a3d9ca3a5a8367285040bc7e2e3837adf0decc601f5ca52986667, 0x1b287e8f1d2812e2051d31e0b8f5b44ad8a42c28b3f23b39f9dab54040197c10), - (0x0a25c502dd5fbc50d0b9bc0c4b73ebdb68212cb3d8919d304366a95cca9273cf, 0x10d56a7902ae750304bb5b397a442e7bb20de731ef393bd5a4bde4b821676f43), - (0x2ce5cc97761c46326b70b86b5c7f5a2b6835ecfc5b19735bc6d69be9d53236c4, 0x182442dbc817aa8926dc78761be286048ac57fd15d5c9e72210013dd77d68a79), - (0x0babcc3c20e0909cfd33f33b2afd5ef7928e935655614a7783a41d54783b8641, 0x1203a94185e9c222b2965adf896f2c7093f151f73f141cb0b6275a9428d56046), - (0x24836d91a72ea59da98dbecda06b12fdfe73bf870cba478b5da6e5a0214c6d42, 0x1d3848af33d96a5fce26243cf19c147260ab903a275bf9a03ff3db91f6a22971), - (0x319f25dcc4b43eaf7767eb2330cb1f31d8790418bd073cb0c03d6c0dcfbdf3c8, 0x1de3e74aee1b1d661c73d0a86c5bf718a76d55ab446973fd2a8038bced640a3e), + (0x2ad778f0e75a3dcad7c0cc2215e554f3d6fe41eabd612c487ea2708d59fb2e7e, 0x0561e9268230b53ec9cac0fd7654b3edaa3851f624c62bdae39519ae17526c06), + (0x358a21858e7f0da213959badd192b12e7bd40f6b18f5617a7fbad1f142b53c41, 0x1cc665c7a95332ea3ecb79bc35c4d672837809470691ad6a96f2eca081ca9c98), + (0x28d6468db328f1d201b3b7ca36f3affddee9dd0043d8868e34f1839742ac3190, 0x2f38eba3a82748cc28e46c1e20b7d343fdac7ef726ed6de89f6484c6070204f1), + (0x21f27b52cd9a76e1dbbf572fbfc0262007015777b68bda954f3c9db60ebb27f9, 0x0dbbf8f04e8476544a853e54093689d59935de9194eef24a0ee04d6faef9423f), + (0x0253a69e81add6bc1c2fe14bd90dab3e3c2603747dd3760c9dd1e341d96a79ed, 0x15cbe812a45a46512cc8ed493250f75a9dcaaee4be0d3bdaee8b43d74c50481f), + (0x19eb8760e7d0e6ae6d28d65627d958661cdde4e58a0aeb55a6b7017bcf723002, 0x064575794bf9bfdbc50e94b8afbbd2851ae4e12ff2809777d28fe71c235727d9), + (0x0e5c408b5224841cb4f75aec5cdb7bc95c150bbe858dbde8dbac9f72e5392462, 0x01030c69ac5fc7dd63e4e0bb1718d26f51b79dccc81e0b172e98c26e59145414), + (0x12437cb05ecff24131b52b5a55f6f143d8437c28c9d7c31eb06cfa093972a64b, 0x06e1a5e39566b4ce097a6c7dace6dcb827e54dac7d64fa79d994fb1557717614), + (0x34636ff9de412da15f41a8a006abbe8f43a5cffc94e6c3deb02f98af4fb2b8c7, 0x0270f0c3fa8cc7338f20fbcd5ec4e62799e051e0c5938d9e8c41581de8da6165), + (0x218e047b1c0a3b92c59539b3f6d9c23d34ebeeb65ca0be98f5e0e9642bdf1085, 0x20c1117f40b375688a94ff5c5c0b70004e43c7c7cd492fe8055fea081ea5ca78), + (0x2478c8226d4ede1c203fa7455b5fe28f99d5a0cb8ccdb5be4b54d5edcce974c4, 0x1ce69b76f05daeae57cd3d452370439237da89f2ddc84f7b2e35703acbf99655), + (0x08383138ecc6f2fb5459043c7666ae3df7802f1f02392af44db6ba25cd7d2c56, 0x20957d7a3f00a8589f627c5f5e471f45a84fbcbdcde00dfc97b9a97f3f723202), (0x21d210b41675a1eae44cbd0f3fd27d69e30716c71873f6089cee61acacd403ab, 0x2275e97c7e84f68bfaa528a9d8be4e059f7abefd80d03fbfca774e8414a9b7c1), (0x0f9e7de28e0f650d99d99d95c0fcd39c9dac9db5aa1973319f66922d6eb9f7d5, 0x1ba644ecc18ad711ddd33af7f695f6834e9f35c93d47a6a5273dabbe800fc7e6), (0x0aab3ab73afac76277cd94a891de15e42ceb09f3a9865dab5c814bebfbb4453f, 0x27119fec3736d99abeeef1ad7b857db7e754e0c158780ed3dd0cdd4dc2453e10), diff --git a/src/constants.rs b/src/constants.rs index c95c27e8..52df6cd5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -100,8 +100,8 @@ fn compute_window_table(base: C, num_windows: usize) -> Vec<[C; (0..H) .map(|k| { // scalar = (k+2)*(8^w) - let scalar = C::ScalarExt::from_u64(k as u64 + 2) - * C::ScalarExt::from_u64(H as u64).pow(&[w as u64, 0, 0, 0]); + let scalar = C::Scalar::from(k as u64 + 2) + * C::Scalar::from(H as u64).pow(&[w as u64, 0, 0, 0]); (base * scalar).to_affine() }) .collect::>() @@ -114,19 +114,14 @@ fn compute_window_table(base: C, num_windows: usize) -> Vec<[C; // For the last window, we compute [k * (2^3)^w - sum]B, where sum is defined // as sum = \sum_{j = 0}^{`num_windows - 2`} 2^{3j+1} let sum = (0..(num_windows - 1)).fold(C::ScalarExt::zero(), |acc, j| { - acc + C::ScalarExt::from_u64(2).pow(&[ - FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1, - 0, - 0, - 0, - ]) + acc + C::Scalar::from(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1, 0, 0, 0]) }); window_table.push( (0..H) .map(|k| { // scalar = k * (2^3)^w - sum, where w = `num_windows - 1` - let scalar = C::ScalarExt::from_u64(k as u64) - * C::ScalarExt::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) + let scalar = C::Scalar::from(k as u64) + * C::Scalar::from(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) - sum; (base * scalar).to_affine() }) @@ -142,7 +137,7 @@ fn compute_window_table(base: C, num_windows: usize) -> Vec<[C; /// Here, we pre-compute and store the coefficients of the interpolation polynomial. fn compute_lagrange_coeffs(base: C, num_windows: usize) -> Vec<[C::Base; H]> { // We are interpolating over the 3-bit window, k \in [0..8) - let points: Vec<_> = (0..H).map(|i| C::Base::from_u64(i as u64)).collect(); + let points: Vec<_> = (0..H).map(|i| C::Base::from(i as u64)).collect(); let window_table = compute_window_table(base, num_windows); @@ -183,8 +178,8 @@ fn find_zs_and_us(base: C, num_windows: usize) -> Option(base: C, num_windows: usize) { // Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B. let point = base - * C::Scalar::from_u64(bits as u64 + 2) - * C::Scalar::from_u64(H as u64).pow(&[idx as u64, 0, 0, 0]); + * C::Scalar::from(bits as u64 + 2) + * C::Scalar::from(H as u64).pow(&[idx as u64, 0, 0, 0]); let x = *point.to_affine().coordinates().unwrap().x(); // Check that the interpolated x-coordinate matches the actual one. @@ -235,15 +230,10 @@ fn test_lagrange_coeffs(base: C, num_windows: usize) { // Compute the actual x-coordinate of the multiple [k * (8^84) - offset]B, // where offset = \sum_{j = 0}^{83} 2^{3j+1} let offset = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, w| { - acc + C::Scalar::from_u64(2).pow(&[ - FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, - 0, - 0, - 0, - ]) + acc + C::Scalar::from(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 0, 0, 0]) }); - let scalar = C::Scalar::from_u64(bits as u64) - * C::Scalar::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) + let scalar = C::Scalar::from(bits as u64) + * C::Scalar::from(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) - offset; let point = base * scalar; let x = *point.to_affine().coordinates().unwrap().x(); @@ -258,15 +248,15 @@ fn test_lagrange_coeffs(base: C, num_windows: usize) { // 1. z + y = u^2, // 2. z - y is not a square // for the y-coordinate of each fixed-base multiple in each window. -fn test_zs_and_us(base: C, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) { +fn test_zs_and_us(base: pallas::Affine, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) { let window_table = compute_window_table(base, num_windows); for ((u, z), window_points) in u.iter().zip(z.iter()).zip(window_table) { for (u, point) in u.iter().zip(window_points.iter()) { let y = *point.coordinates().unwrap().y(); - let u = C::Base::from_bytes(u).unwrap(); - assert_eq!(C::Base::from_u64(*z) + y, u * u); // allow either square root - assert!(bool::from((C::Base::from_u64(*z) - y).sqrt().is_none())); + let u = pallas::Base::from_repr(*u).unwrap(); + assert_eq!(pallas::Base::from(*z) + y, u * u); // allow either square root + assert!(bool::from((pallas::Base::from(*z) - y).sqrt().is_none())); } } } diff --git a/src/constants/commit_ivk_r.rs b/src/constants/commit_ivk_r.rs index c0e0b508..976ed22e 100644 --- a/src/constants/commit_ivk_r.rs +++ b/src/constants/commit_ivk_r.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// Generator used in SinsemillaCommit randomness for IVK commitment pub const GENERATOR: ([u8; 32], [u8; 32]) = ( @@ -2922,8 +2920,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2936,10 +2934,7 @@ mod tests { use super::*; use crate::primitives::sinsemilla::CommitDomain; use group::Curve; - use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, - }; + use pasta_curves::{arithmetic::CurveAffine, pallas}; #[test] fn generator() { @@ -2947,8 +2942,8 @@ mod tests { let point = domain.R(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/constants/load.rs b/src/constants/load.rs index e2743f26..5368a962 100644 --- a/src/constants/load.rs +++ b/src/constants/load.rs @@ -1,7 +1,8 @@ use std::convert::TryInto; use crate::constants::{self, compute_lagrange_coeffs, H, NUM_WINDOWS, NUM_WINDOWS_SHORT}; -use pasta_curves::{arithmetic::FieldExt, pallas}; +use group::ff::PrimeField; +use pasta_curves::pallas; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum OrchardFixedBasesFull { @@ -177,7 +178,7 @@ impl From<[u64; NUM_WINDOWS]> for Z { fn from(zs: [u64; NUM_WINDOWS]) -> Self { Self( zs.iter() - .map(|z| pallas::Base::from_u64(*z)) + .map(|z| pallas::Base::from(*z)) .collect::>() .into_boxed_slice() .try_into() @@ -194,7 +195,7 @@ impl From<[u64; NUM_WINDOWS_SHORT]> for ZShort { fn from(zs: [u64; NUM_WINDOWS_SHORT]) -> Self { Self( zs.iter() - .map(|z| pallas::Base::from_u64(*z)) + .map(|z| pallas::Base::from(*z)) .collect::>() .into_boxed_slice() .try_into() @@ -212,7 +213,7 @@ impl From<&[[u8; 32]; H]> for WindowUs { Self( window_us .iter() - .map(|u| pallas::Base::from_bytes(u).unwrap()) + .map(|u| pallas::Base::from_repr(*u).unwrap()) .collect::>() .into_boxed_slice() .try_into() diff --git a/src/constants/note_commit_r.rs b/src/constants/note_commit_r.rs index 4a12734d..d2dde504 100644 --- a/src/constants/note_commit_r.rs +++ b/src/constants/note_commit_r.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// Generator used in SinsemillaCommit randomness for note commitment pub const GENERATOR: ([u8; 32], [u8; 32]) = ( @@ -2922,8 +2920,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2936,10 +2934,7 @@ mod tests { use super::*; use crate::primitives::sinsemilla::CommitDomain; use group::Curve; - use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, - }; + use pasta_curves::{arithmetic::CurveAffine, pallas}; #[test] fn generator() { @@ -2947,8 +2942,8 @@ mod tests { let point = domain.R(); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/constants/nullifier_k.rs b/src/constants/nullifier_k.rs index 37db6fda..47f69068 100644 --- a/src/constants/nullifier_k.rs +++ b/src/constants/nullifier_k.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ @@ -2921,8 +2919,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2934,10 +2932,7 @@ mod tests { }; use super::*; use group::Curve; - use pasta_curves::{ - arithmetic::{CurveExt, FieldExt}, - pallas, - }; + use pasta_curves::{arithmetic::CurveExt, pallas}; #[test] fn generator() { @@ -2945,8 +2940,8 @@ mod tests { let point = hasher(b"K"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/constants/spend_auth_g.rs b/src/constants/spend_auth_g.rs index 41b99c90..6deba8f6 100644 --- a/src/constants/spend_auth_g.rs +++ b/src/constants/spend_auth_g.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// The value commitment is used to check balance between inputs and outputs. The value is /// placed over this generator. @@ -2923,8 +2921,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2937,7 +2935,7 @@ mod tests { use super::*; use group::Curve; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; @@ -2947,8 +2945,8 @@ mod tests { let point = hasher(b"G"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/constants/util.rs b/src/constants/util.rs index bdb84901..b68404e9 100644 --- a/src/constants/util.rs +++ b/src/constants/util.rs @@ -1,5 +1,5 @@ use ff::{Field, PrimeFieldBits}; -use halo2::arithmetic::{CurveAffine, FieldExt}; +use halo2::arithmetic::CurveAffine; /// Decompose a word `alpha` into `window_num_bits` bits (little-endian) /// For a window size of `w`, this returns [k_0, ..., k_n] where each `k_i` @@ -10,7 +10,7 @@ use halo2::arithmetic::{CurveAffine, FieldExt}; /// We are returning a `Vec` which means the window size is limited to /// <= 8 bits. pub fn decompose_word( - word: F, + word: &F, word_num_bits: usize, window_num_bits: usize, ) -> Vec { @@ -33,7 +33,7 @@ pub fn decompose_word( /// Evaluate y = f(x) given the coefficients of f(x) pub fn evaluate(x: u8, coeffs: &[C::Base]) -> C::Base { - let x = C::Base::from_u64(x as u64); + let x = C::Base::from(x as u64); coeffs .iter() .rev() @@ -86,7 +86,7 @@ mod tests { window_num_bits in 1u8..9 ) { // Get decomposition into `window_num_bits` bits - let decomposed = decompose_word(scalar, pallas::Scalar::NUM_BITS as usize, window_num_bits as usize); + let decomposed = decompose_word(&scalar, pallas::Scalar::NUM_BITS as usize, window_num_bits as usize); // Flatten bits let bits = decomposed @@ -102,7 +102,7 @@ mod tests { let bytes: Vec = bits.chunks_exact(8).map(|chunk| chunk.iter().rev().fold(0, |acc, b| (acc << 1) + (*b as u8))).collect(); // Check that original scalar is recovered from decomposition - assert_eq!(scalar, pallas::Scalar::from_bytes(&bytes.try_into().unwrap()).unwrap()); + assert_eq!(scalar, pallas::Scalar::from_repr(bytes.try_into().unwrap()).unwrap()); } } } diff --git a/src/constants/value_commit_r.rs b/src/constants/value_commit_r.rs index 402df337..b12030f0 100644 --- a/src/constants/value_commit_r.rs +++ b/src/constants/value_commit_r.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// The value commitment is used to check balance between inputs and outputs. The value is /// placed over this generator. @@ -2923,8 +2921,8 @@ pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -2937,7 +2935,7 @@ mod tests { use super::*; use group::Curve; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; @@ -2947,8 +2945,8 @@ mod tests { let point = hasher(b"r"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/constants/value_commit_v.rs b/src/constants/value_commit_v.rs index 7ffc9899..c17bdaed 100644 --- a/src/constants/value_commit_v.rs +++ b/src/constants/value_commit_v.rs @@ -1,7 +1,5 @@ -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, -}; +use group::ff::PrimeField; +use pasta_curves::{arithmetic::CurveAffine, pallas}; /// The value commitment is used to check balance between inputs and outputs. The value is /// placed over this generator. @@ -776,8 +774,8 @@ pub const U_SHORT: [[[u8; 32]; super::H]; super::NUM_WINDOWS_SHORT] = [ pub fn generator() -> pallas::Affine { pallas::Affine::from_xy( - pallas::Base::from_bytes(&GENERATOR.0).unwrap(), - pallas::Base::from_bytes(&GENERATOR.1).unwrap(), + pallas::Base::from_repr(GENERATOR.0).unwrap(), + pallas::Base::from_repr(GENERATOR.1).unwrap(), ) .unwrap() } @@ -790,7 +788,7 @@ mod tests { use super::*; use group::Curve; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; @@ -800,8 +798,8 @@ mod tests { let point = hasher(b"v"); let coords = point.to_affine().coordinates().unwrap(); - assert_eq!(*coords.x(), pallas::Base::from_bytes(&GENERATOR.0).unwrap()); - assert_eq!(*coords.y(), pallas::Base::from_bytes(&GENERATOR.1).unwrap()); + assert_eq!(*coords.x(), pallas::Base::from_repr(GENERATOR.0).unwrap()); + assert_eq!(*coords.y(), pallas::Base::from_repr(GENERATOR.1).unwrap()); } #[test] diff --git a/src/keys.rs b/src/keys.rs index e317f0ca..ad218b15 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -7,8 +7,11 @@ use std::mem; use aes::Aes256; use blake2b_simd::{Hash as Blake2bHash, Params}; use fpe::ff1::{BinaryNumeralString, FF1}; -use group::{ff::Field, prime::PrimeCurveAffine, Curve, GroupEncoding}; -use halo2::arithmetic::FieldExt; +use group::{ + ff::{Field, PrimeField}, + prime::PrimeCurveAffine, + Curve, GroupEncoding, +}; use pasta_curves::pallas; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -124,10 +127,10 @@ impl From<&SpendingKey> for SpendAuthorizingKey { // SpendingKey cannot be constructed such that this assertion would fail. assert!(!bool::from(ask.is_zero())); // TODO: Add TryFrom for SpendAuthorizingKey. - let ret = SpendAuthorizingKey(ask.to_bytes().try_into().unwrap()); + let ret = SpendAuthorizingKey(ask.to_repr().try_into().unwrap()); // If the last bit of repr_P(ak) is 1, negate ask. if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 { - SpendAuthorizingKey((-ask).to_bytes().try_into().unwrap()) + SpendAuthorizingKey((-ask).to_repr().try_into().unwrap()) } else { ret } @@ -230,7 +233,7 @@ impl NullifierDerivingKey { pub(crate) fn from_bytes(bytes: &[u8]) -> Option { let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?; - let nk = pallas::Base::from_bytes(&nk_bytes).map(NullifierDerivingKey); + let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey); if nk.is_some().into() { Some(nk.unwrap()) } else { @@ -265,7 +268,7 @@ impl CommitIvkRandomness { pub(crate) fn from_bytes(bytes: &[u8]) -> Option { let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?; - let rivk = pallas::Scalar::from_bytes(&rivk_bytes).map(CommitIvkRandomness); + let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness); if rivk.is_some().into() { Some(rivk.unwrap()) } else { @@ -324,8 +327,8 @@ impl FullViewingKey { /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) { - let k = self.rivk.0.to_bytes(); - let b = [(&self.ak.0).into(), self.nk.0.to_bytes()]; + let k = self.rivk.0.to_repr(); + let b = [(&self.ak.0).into(), self.nk.0.to_repr()]; let r = PrfExpand::OrchardDkOvk.with_ad_slices(&k, &[&b[0][..], &b[1][..]]); ( DiversifierKey(r[..32].try_into().unwrap()), @@ -355,8 +358,8 @@ impl FullViewingKey { pub fn write(&self, mut writer: W) -> io::Result<()> { let ak_raw: [u8; 32] = self.ak.0.clone().into(); writer.write_all(&ak_raw)?; - writer.write_all(&self.nk.0.to_bytes())?; - writer.write_all(&self.rivk.0.to_bytes())?; + writer.write_all(&self.nk.0.to_repr())?; + writer.write_all(&self.rivk.0.to_repr())?; Ok(()) } @@ -560,7 +563,7 @@ impl IncomingViewingKey { pub fn to_bytes(&self) -> [u8; 64] { let mut result = [0u8; 64]; result[..32].copy_from_slice(self.dk.to_bytes()); - result[32..].copy_from_slice(&self.ivk.0.to_bytes()); + result[32..].copy_from_slice(&self.ivk.0.to_repr()); result } diff --git a/src/note/commitment.rs b/src/note/commitment.rs index d3a7dff8..a8109efb 100644 --- a/src/note/commitment.rs +++ b/src/note/commitment.rs @@ -1,8 +1,8 @@ use std::iter; use bitvec::{array::BitArray, order::Lsb0}; -use ff::PrimeFieldBits; -use pasta_curves::{arithmetic::FieldExt, pallas}; +use group::ff::{PrimeField, PrimeFieldBits}; +use pasta_curves::pallas; use subtle::{ConstantTimeEq, CtOption}; use crate::{ @@ -72,12 +72,12 @@ impl ExtractedNoteCommitment { /// /// [cmxcanon]: https://zips.z.cash/protocol/protocol.pdf#actionencodingandconsensus pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - pallas::Base::from_bytes(bytes).map(ExtractedNoteCommitment) + pallas::Base::from_repr(*bytes).map(ExtractedNoteCommitment) } /// Serialize the value commitment to its canonical byte representation. pub fn to_bytes(self) -> [u8; 32] { - self.0.to_bytes() + self.0.to_repr() } } diff --git a/src/note/nullifier.rs b/src/note/nullifier.rs index a0ede6fb..f1b8e4fe 100644 --- a/src/note/nullifier.rs +++ b/src/note/nullifier.rs @@ -1,6 +1,6 @@ -use group::Group; +use group::{ff::PrimeField, Group}; use halo2::arithmetic::CurveExt; -use pasta_curves::{arithmetic::FieldExt, pallas}; +use pasta_curves::pallas; use rand::RngCore; use subtle::CtOption; @@ -33,12 +33,12 @@ impl Nullifier { /// Deserialize the nullifier from a byte array. pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - pallas::Base::from_bytes(bytes).map(Nullifier) + pallas::Base::from_repr(*bytes).map(Nullifier) } /// Serialize the nullifier to its canonical byte representation. pub fn to_bytes(self) -> [u8; 32] { - self.0.to_bytes() + self.0.to_repr() } /// $DeriveNullifier$. diff --git a/src/note_encryption.rs b/src/note_encryption.rs index c0aabfd3..2ae8f21f 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -3,7 +3,7 @@ use std::{convert::TryInto, fmt}; use blake2b_simd::{Hash, Params}; -use halo2::arithmetic::FieldExt; +use group::ff::PrimeField; use zcash_note_encryption::{ BatchDomain, Domain, EphemeralKeyBytes, NotePlaintextBytes, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, @@ -171,7 +171,7 @@ impl Domain for OrchardDomain { ) -> OutPlaintextBytes { let mut op = [0; OUT_PLAINTEXT_SIZE]; op[..32].copy_from_slice(¬e.recipient().pk_d().to_bytes()); - op[32..].copy_from_slice(&esk.0.to_bytes()); + op[32..].copy_from_slice(&esk.0.to_repr()); OutPlaintextBytes(op) } diff --git a/src/primitives/poseidon.rs b/src/primitives/poseidon.rs index f2892d1b..1f3922c7 100644 --- a/src/primitives/poseidon.rs +++ b/src/primitives/poseidon.rs @@ -1,6 +1,7 @@ //! The Poseidon algebraic hash function. use std::array; +use std::convert::TryInto; use std::fmt; use std::iter; use std::marker::PhantomData; @@ -23,14 +24,14 @@ use grain::SboxType; /// The type used to hold permutation state. pub(crate) type State = [F; T]; -/// The type used to hold duplex sponge state. -pub(crate) type SpongeState = [Option; RATE]; +/// The type used to hold sponge rate. +pub(crate) type SpongeRate = [Option; RATE]; /// The type used to hold the MDS matrix and its inverse. pub(crate) type Mds = [[F; T]; T]; /// A specification for a Poseidon permutation. -pub trait Spec { +pub trait Spec: fmt::Debug { /// The number of full rounds for this specification. /// /// This must be an even number. @@ -47,10 +48,10 @@ pub trait Spec { /// /// This is used by the default implementation of [`Spec::constants`]. If you are /// hard-coding the constants, you may leave this unimplemented. - fn secure_mds(&self) -> usize; + fn secure_mds() -> usize; /// Generates `(round_constants, mds, mds^-1)` corresponding to this specification. - fn constants(&self) -> (Vec<[F; T]>, Mds, Mds) { + fn constants() -> (Vec<[F; T]>, Mds, Mds) { let r_f = Self::full_rounds(); let r_p = Self::partial_rounds(); @@ -69,7 +70,7 @@ pub trait Spec { }) .collect(); - let (mds, mds_inv) = mds::generate_mds::(&mut grain, self.secure_mds()); + let (mds, mds_inv) = mds::generate_mds::(&mut grain, Self::secure_mds()); (round_constants, mds, mds_inv) } @@ -123,14 +124,19 @@ pub(crate) fn permute, const T: usize, const RA }); } -fn poseidon_duplex, const T: usize, const RATE: usize>( +fn poseidon_sponge, const T: usize, const RATE: usize>( state: &mut State, - input: &SpongeState, - pad_and_add: &dyn Fn(&mut State, &SpongeState), + input: Option<&Absorbing>, mds_matrix: &Mds, round_constants: &[[F; T]], -) -> SpongeState { - pad_and_add(state, input); +) -> Squeezing { + if let Some(Absorbing(input)) = input { + // `Iterator::zip` short-circuits when one iterator completes, so this will only + // mutate the rate portion of the state. + for (word, value) in state.iter_mut().zip(input.iter()) { + *word += value.expect("poseidon_sponge is called with a padded input"); + } + } permute::(state, mds_matrix, round_constants); @@ -138,49 +144,70 @@ fn poseidon_duplex, const T: usize, const RATE: for (word, value) in output.iter_mut().zip(state.iter()) { *word = Some(*value); } - output + Squeezing(output) } -pub(crate) enum Sponge { - Absorbing(SpongeState), - Squeezing(SpongeState), +mod private { + pub trait SealedSpongeMode {} + impl SealedSpongeMode for super::Absorbing {} + impl SealedSpongeMode for super::Squeezing {} } -impl Sponge { - pub(crate) fn absorb(val: F) -> Self { - let mut input = [None; RATE]; - input[0] = Some(val); - Sponge::Absorbing(input) +/// The state of the `Sponge`. +pub trait SpongeMode: private::SealedSpongeMode {} + +/// The absorbing state of the `Sponge`. +#[derive(Debug)] +pub struct Absorbing(pub(crate) SpongeRate); + +/// The squeezing state of the `Sponge`. +#[derive(Debug)] +pub struct Squeezing(pub(crate) SpongeRate); + +impl SpongeMode for Absorbing {} +impl SpongeMode for Squeezing {} + +impl Absorbing { + pub(crate) fn init_with(val: F) -> Self { + Self( + iter::once(Some(val)) + .chain((1..RATE).map(|_| None)) + .collect::>() + .try_into() + .unwrap(), + ) } } -/// A Poseidon duplex sponge. -pub(crate) struct Duplex, const T: usize, const RATE: usize> { - sponge: Sponge, +/// A Poseidon sponge. +pub(crate) struct Sponge< + F: FieldExt, + S: Spec, + M: SpongeMode, + const T: usize, + const RATE: usize, +> { + mode: M, state: State, - pad_and_add: Box, &SpongeState)>, mds_matrix: Mds, round_constants: Vec<[F; T]>, _marker: PhantomData, } -impl, const T: usize, const RATE: usize> Duplex { - /// Constructs a new duplex sponge for the given Poseidon specification. - pub(crate) fn new( - spec: S, - initial_capacity_element: F, - pad_and_add: Box, &SpongeState)>, - ) -> Self { - let (round_constants, mds_matrix, _) = spec.constants(); +impl, const T: usize, const RATE: usize> + Sponge, T, RATE> +{ + /// Constructs a new sponge for the given Poseidon specification. + pub(crate) fn new(initial_capacity_element: F) -> Self { + let (round_constants, mds_matrix, _) = S::constants(); - let input = [None; RATE]; + let mode = Absorbing([None; RATE]); let mut state = [F::zero(); T]; state[RATE] = initial_capacity_element; - Duplex { - sponge: Sponge::Absorbing(input), + Sponge { + mode, state, - pad_and_add, mds_matrix, round_constants, _marker: PhantomData::default(), @@ -189,71 +216,78 @@ impl, const T: usize, const RATE: usize> Duplex /// Absorbs an element into the sponge. pub(crate) fn absorb(&mut self, value: F) { - match self.sponge { - Sponge::Absorbing(ref mut input) => { - for entry in input.iter_mut() { - if entry.is_none() { - *entry = Some(value); - return; - } - } - - // We've already absorbed as many elements as we can - let _ = poseidon_duplex::( - &mut self.state, - input, - &self.pad_and_add, - &self.mds_matrix, - &self.round_constants, - ); - self.sponge = Sponge::absorb(value); - } - Sponge::Squeezing(_) => { - // Drop the remaining output elements - self.sponge = Sponge::absorb(value); + for entry in self.mode.0.iter_mut() { + if entry.is_none() { + *entry = Some(value); + return; } } + + // We've already absorbed as many elements as we can + let _ = poseidon_sponge::( + &mut self.state, + Some(&self.mode), + &self.mds_matrix, + &self.round_constants, + ); + self.mode = Absorbing::init_with(value); } + /// Transitions the sponge into its squeezing state. + pub(crate) fn finish_absorbing(mut self) -> Sponge, T, RATE> { + let mode = poseidon_sponge::( + &mut self.state, + Some(&self.mode), + &self.mds_matrix, + &self.round_constants, + ); + + Sponge { + mode, + state: self.state, + mds_matrix: self.mds_matrix, + round_constants: self.round_constants, + _marker: PhantomData::default(), + } + } +} + +impl, const T: usize, const RATE: usize> + Sponge, T, RATE> +{ /// Squeezes an element from the sponge. pub(crate) fn squeeze(&mut self) -> F { loop { - match self.sponge { - Sponge::Absorbing(ref input) => { - self.sponge = Sponge::Squeezing(poseidon_duplex::( - &mut self.state, - input, - &self.pad_and_add, - &self.mds_matrix, - &self.round_constants, - )); - } - Sponge::Squeezing(ref mut output) => { - for entry in output.iter_mut() { - if let Some(e) = entry.take() { - return e; - } - } - - // We've already squeezed out all available elements - self.sponge = Sponge::Absorbing([None; RATE]); + for entry in self.mode.0.iter_mut() { + if let Some(e) = entry.take() { + return e; } } + + // We've already squeezed out all available elements + self.mode = poseidon_sponge::( + &mut self.state, + None, + &self.mds_matrix, + &self.round_constants, + ); } } } /// A domain in which a Poseidon hash function is being used. -pub trait Domain: Copy + fmt::Debug { +pub trait Domain { + /// Iterator that outputs padding field elements. + type Padding: IntoIterator; + + /// The name of this domain, for debug formatting purposes. + fn name() -> String; + /// The initial capacity element, encoding this domain. - fn initial_capacity_element(&self) -> F; + fn initial_capacity_element() -> F; - /// The padding that will be added to each state word by [`Domain::pad_and_add`]. - fn padding(&self) -> SpongeState; - - /// Returns a function that will update the given state with the given input to a - /// duplex permutation round, applying padding according to this domain specification. - fn pad_and_add(&self) -> Box, &SpongeState)>; + /// Returns the padding to be appended to the input. + fn padding(input_len: usize) -> Self::Padding; } /// A Poseidon hash function used with constant input length. @@ -262,59 +296,44 @@ pub trait Domain: Copy + fmt::De #[derive(Clone, Copy, Debug)] pub struct ConstantLength; -impl Domain - for ConstantLength -{ - fn initial_capacity_element(&self) -> F { +impl Domain for ConstantLength { + type Padding = iter::Take>; + + fn name() -> String { + format!("ConstantLength<{}>", L) + } + + fn initial_capacity_element() -> F { // Capacity value is $length \cdot 2^64 + (o-1)$ where o is the output length. // We hard-code an output length of 1. F::from_u128((L as u128) << 64) } - fn padding(&self) -> SpongeState { - // For constant-input-length hashing, padding consists of the field elements being - // zero. - let mut padding = [None; RATE]; - for word in padding.iter_mut().skip(L) { - *word = Some(F::zero()); - } - padding - } - - fn pad_and_add(&self) -> Box, &SpongeState)> { - Box::new(|state, input| { - // `Iterator::zip` short-circuits when one iterator completes, so this will only - // mutate the rate portion of the state. - for (word, value) in state.iter_mut().zip(input.iter()) { - // For constant-input-length hashing, padding consists of the field - // elements being zero, so we don't add anything to the state. - if let Some(value) = value { - *word += value; - } - } - }) + fn padding(input_len: usize) -> Self::Padding { + assert_eq!(input_len, L); + // For constant-input-length hashing, we pad the input with zeroes to a multiple + // of RATE. On its own this would not be sponge-compliant padding, but the + // Poseidon authors encode the constant length into the capacity element, ensuring + // that inputs of different lengths do not share the same permutation. + let k = (L + RATE - 1) / RATE; + iter::repeat(F::zero()).take(k * RATE - L) } } -/// A Poseidon hash function, built around a duplex sponge. +/// A Poseidon hash function, built around a sponge. pub struct Hash< F: FieldExt, S: Spec, - D: Domain, + D: Domain, const T: usize, const RATE: usize, > { - duplex: Duplex, - domain: D, + sponge: Sponge, T, RATE>, + _domain: PhantomData, } -impl< - F: FieldExt, - S: Spec, - D: Domain, - const T: usize, - const RATE: usize, - > fmt::Debug for Hash +impl, D: Domain, const T: usize, const RATE: usize> + fmt::Debug for Hash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Hash") @@ -322,28 +341,19 @@ impl< .field("rate", &RATE) .field("R_F", &S::full_rounds()) .field("R_P", &S::partial_rounds()) - .field("domain", &self.domain) + .field("domain", &D::name()) .finish() } } -impl< - F: FieldExt, - S: Spec, - D: Domain, - const T: usize, - const RATE: usize, - > Hash +impl, D: Domain, const T: usize, const RATE: usize> + Hash { /// Initializes a new hasher. - pub fn init(spec: S, domain: D) -> Self { + pub fn init() -> Self { Hash { - duplex: Duplex::new( - spec, - domain.initial_capacity_element(), - domain.pad_and_add(), - ), - domain, + sponge: Sponge::new(D::initial_capacity_element()), + _domain: PhantomData::default(), } } } @@ -353,10 +363,12 @@ impl, const T: usize, const RATE: usize, const { /// Hashes the given input. pub fn hash(mut self, message: [F; L]) -> F { - for value in array::IntoIter::new(message) { - self.duplex.absorb(value); + for value in + array::IntoIter::new(message).chain( as Domain>::padding(L)) + { + self.sponge.absorb(value); } - self.duplex.squeeze() + self.sponge.finish_absorbing().squeeze() } } @@ -369,11 +381,11 @@ mod tests { #[test] fn orchard_spec_equivalence() { - let message = [pallas::Base::from_u64(6), pallas::Base::from_u64(42)]; + let message = [pallas::Base::from(6), pallas::Base::from(42)]; - let (round_constants, mds, _) = OrchardNullifier.constants(); + let (round_constants, mds, _) = OrchardNullifier::constants(); - let hasher = Hash::init(OrchardNullifier, ConstantLength); + let hasher = Hash::<_, OrchardNullifier, ConstantLength<2>, 3, 2>::init(); let result = hasher.hash(message); // The result should be equivalent to just directly applying the permutation and diff --git a/src/primitives/poseidon/p128pow5t3.rs b/src/primitives/poseidon/p128pow5t3.rs index 31c7217c..5052613d 100644 --- a/src/primitives/poseidon/p128pow5t3.rs +++ b/src/primitives/poseidon/p128pow5t3.rs @@ -25,11 +25,11 @@ impl Spec for P128Pow5T3 { val.pow_vartime(&[5]) } - fn secure_mds(&self) -> usize { + fn secure_mds() -> usize { unimplemented!() } - fn constants(&self) -> (Vec<[Fp; 3]>, Mds, Mds) { + fn constants() -> (Vec<[Fp; 3]>, Mds, Mds) { ( super::fp::ROUND_CONSTANTS[..].to_vec(), super::fp::MDS, @@ -51,11 +51,11 @@ impl Spec for P128Pow5T3 { val.pow_vartime(&[5]) } - fn secure_mds(&self) -> usize { + fn secure_mds() -> usize { unimplemented!() } - fn constants(&self) -> (Vec<[Fq; 3]>, Mds, Mds) { + fn constants() -> (Vec<[Fq; 3]>, Mds, Mds) { ( super::fq::ROUND_CONSTANTS[..].to_vec(), super::fq::MDS, @@ -80,21 +80,15 @@ mod tests { /// The same Poseidon specification as poseidon::P128Pow5T3, but constructed /// such that its constants will be generated at runtime. #[derive(Debug)] - pub struct P128Pow5T3Gen { - secure_mds: usize, - _field: PhantomData, - } + pub struct P128Pow5T3Gen(PhantomData); - impl P128Pow5T3Gen { - pub fn new(secure_mds: usize) -> Self { - P128Pow5T3Gen { - secure_mds, - _field: PhantomData::default(), - } + impl P128Pow5T3Gen { + pub fn new() -> Self { + P128Pow5T3Gen(PhantomData::default()) } } - impl Spec for P128Pow5T3Gen { + impl Spec for P128Pow5T3Gen { fn full_rounds() -> usize { 8 } @@ -107,8 +101,8 @@ mod tests { val.pow_vartime(&[5]) } - fn secure_mds(&self) -> usize { - self.secure_mds + fn secure_mds() -> usize { + SECURE_MDS } } @@ -119,8 +113,7 @@ mod tests { expected_mds: [[F; 3]; 3], expected_mds_inv: [[F; 3]; 3], ) { - let poseidon = P128Pow5T3Gen::::new(0); - let (round_constants, mds, mds_inv) = poseidon.constants(); + let (round_constants, mds, mds_inv) = P128Pow5T3Gen::::constants(); for (actual, expected) in round_constants .iter() @@ -196,7 +189,7 @@ mod tests { ]), ]; - permute::, 3, 2>(&mut input, &fp::MDS, &fp::ROUND_CONSTANTS); + permute::, 3, 2>(&mut input, &fp::MDS, &fp::ROUND_CONSTANTS); assert_eq!(input, expected_output); } @@ -247,7 +240,7 @@ mod tests { ]), ]; - permute::, 3, 2>(&mut input, &fq::MDS, &fq::ROUND_CONSTANTS); + permute::, 3, 2>(&mut input, &fq::MDS, &fq::ROUND_CONSTANTS); assert_eq!(input, expected_output); } } @@ -255,7 +248,7 @@ mod tests { #[test] fn permute_test_vectors() { { - let (round_constants, mds, _) = super::P128Pow5T3.constants(); + let (round_constants, mds, _) = super::P128Pow5T3::constants(); for tv in crate::primitives::poseidon::test_vectors::fp::permute() { let mut state = [ @@ -273,7 +266,7 @@ mod tests { } { - let (round_constants, mds, _) = super::P128Pow5T3.constants(); + let (round_constants, mds, _) = super::P128Pow5T3::constants(); for tv in crate::primitives::poseidon::test_vectors::fq::permute() { let mut state = [ @@ -299,7 +292,8 @@ mod tests { Fp::from_repr(tv.input[1]).unwrap(), ]; - let result = Hash::init(super::P128Pow5T3, ConstantLength).hash(message); + let result = + Hash::<_, super::P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message); assert_eq!(result.to_repr(), tv.output); } @@ -310,7 +304,8 @@ mod tests { Fq::from_repr(tv.input[1]).unwrap(), ]; - let result = Hash::init(super::P128Pow5T3, ConstantLength).hash(message); + let result = + Hash::<_, super::P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message); assert_eq!(result.to_repr(), tv.output); } diff --git a/src/primitives/sinsemilla/constants.rs b/src/primitives/sinsemilla/constants.rs index 47f3535f..d09c283b 100644 --- a/src/primitives/sinsemilla/constants.rs +++ b/src/primitives/sinsemilla/constants.rs @@ -66,8 +66,8 @@ mod tests { use crate::constants::{ COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION, }; - use group::Curve; - use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt}; + use group::{ff::PrimeField, Curve}; + use halo2::arithmetic::{CurveAffine, CurveExt}; use halo2::pasta::pallas; #[test] @@ -93,11 +93,11 @@ mod tests { assert_eq!( *coords.x(), - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap() + pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap() ); assert_eq!( *coords.y(), - pallas::Base::from_bytes(&Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap() + pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap() ); } @@ -109,11 +109,11 @@ mod tests { assert_eq!( *coords.x(), - pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.0).unwrap() + pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap() ); assert_eq!( *coords.y(), - pallas::Base::from_bytes(&Q_COMMIT_IVK_M_GENERATOR.1).unwrap() + pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap() ); } @@ -125,18 +125,18 @@ mod tests { assert_eq!( *coords.x(), - pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap() + pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap() ); assert_eq!( *coords.y(), - pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap() + pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap() ); } #[test] fn inv_two_pow_k() { - let two_pow_k = pallas::Base::from_u64(1u64 << K); - let inv_two_pow_k = pallas::Base::from_bytes(&INV_TWO_POW_K).unwrap(); + let two_pow_k = pallas::Base::from(1u64 << K); + let inv_two_pow_k = pallas::Base::from_repr(INV_TWO_POW_K).unwrap(); assert_eq!(two_pow_k * inv_two_pow_k, pallas::Base::one()); } diff --git a/src/spec.rs b/src/spec.rs index ddd394c6..d94605ed 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -70,11 +70,11 @@ impl ConditionallySelectable for NonZeroPallasBase { impl NonZeroPallasBase { pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { - pallas::Base::from_bytes(bytes).and_then(NonZeroPallasBase::from_base) + pallas::Base::from_repr(*bytes).and_then(NonZeroPallasBase::from_base) } pub(crate) fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + self.0.to_repr() } pub(crate) fn from_base(b: pallas::Base) -> CtOption { @@ -116,7 +116,7 @@ impl ConditionallySelectable for NonZeroPallasScalar { impl NonZeroPallasScalar { pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { - pallas::Scalar::from_bytes(bytes).and_then(NonZeroPallasScalar::from_scalar) + pallas::Scalar::from_repr(*bytes).and_then(NonZeroPallasScalar::from_scalar) } pub(crate) fn from_scalar(s: pallas::Scalar) -> CtOption { @@ -212,7 +212,8 @@ pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint { /// /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base { - poseidon::Hash::init(poseidon::P128Pow5T3, poseidon::ConstantLength).hash([nk, rho]) + poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init() + .hash([nk, rho]) } /// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement]. @@ -250,8 +251,8 @@ pub(crate) fn extract_p_bottom(point: CtOption) -> CtOption(bits: &[bool; L]) -> F { - F::from_u64(lebs2ip::(bits)) +pub fn lebs2ip_field(bits: &[bool; L]) -> F { + F::from(lebs2ip::(bits)) } /// The u64 integer represented by an L-bit little-endian bitstring. diff --git a/src/tree.rs b/src/tree.rs index 436642a9..84d921ff 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -9,7 +9,7 @@ use crate::{ primitives::sinsemilla::{i2lebsp_k, HashDomain}, }; use incrementalmerkletree::{Altitude, Hashable}; -use pasta_curves::{arithmetic::FieldExt, pallas}; +use pasta_curves::pallas; use ff::{Field, PrimeField, PrimeFieldBits}; use lazy_static::lazy_static; @@ -23,7 +23,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; // The uncommitted leaf is defined as pallas::Base(2). // lazy_static! { - static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from_u64(2); + static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from(2); pub(crate) static ref EMPTY_ROOTS: Vec = { iter::empty() .chain(Some(MerkleHashOrchard::empty_leaf())) @@ -168,7 +168,7 @@ impl MerkleHashOrchard { /// Convert this digest to its canonical byte representation. pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + self.0.to_repr() } /// Parses a incremental tree leaf digest from the bytes of @@ -177,7 +177,7 @@ impl MerkleHashOrchard { /// Returns the empty `CtOption` if the provided bytes represent /// a non-canonical encoding. pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - pallas::Base::from_bytes(bytes).map(MerkleHashOrchard) + pallas::Base::from_repr(*bytes).map(MerkleHashOrchard) } } @@ -195,7 +195,7 @@ impl std::cmp::Eq for MerkleHashOrchard {} impl std::hash::Hash for MerkleHashOrchard { fn hash(&self, state: &mut H) { >::from(self.0) - .map(|b| b.to_bytes()) + .map(|b| b.to_repr()) .hash(state) } } @@ -272,7 +272,9 @@ pub mod testing { #[cfg(test)] use crate::tree::{MerkleHashOrchard, EMPTY_ROOTS}; #[cfg(test)] - use pasta_curves::{arithmetic::FieldExt, pallas}; + use group::ff::PrimeField; + #[cfg(test)] + use pasta_curves::pallas; #[cfg(test)] use std::convert::TryInto; @@ -293,7 +295,7 @@ pub mod testing { tree.append(&cmx); tree.witness(); - assert_eq!(tree.root().0, pallas::Base::from_bytes(&tv.root).unwrap()); + assert_eq!(tree.root().0, pallas::Base::from_repr(tv.root).unwrap()); // Check paths for all leaves up to this point. The test vectors include paths // for not-yet-appended leaves (using UNCOMMITTED_ORCHARD as the leaf value), @@ -324,7 +326,7 @@ pub mod testing { assert_eq!( MerkleHashOrchard::empty_root(Altitude::from(altitude as u8)) .0 - .to_bytes(), + .to_repr(), *tv_root, "Empty root mismatch at altitude {}", altitude @@ -375,12 +377,9 @@ pub mod testing { let mut frontier = BridgeFrontier::::empty(); for commitment in commitments.iter() { - let cmx = MerkleHashOrchard(pallas::Base::from_bytes(commitment).unwrap()); + let cmx = MerkleHashOrchard(pallas::Base::from_repr(*commitment).unwrap()); frontier.append(&cmx); } - assert_eq!( - frontier.root().0, - pallas::Base::from_bytes(&anchor).unwrap() - ); + assert_eq!(frontier.root().0, pallas::Base::from_repr(anchor).unwrap()); } } diff --git a/src/value.rs b/src/value.rs index a27c5682..77857727 100644 --- a/src/value.rs +++ b/src/value.rs @@ -44,7 +44,7 @@ use bitvec::{array::BitArray, order::Lsb0}; use ff::{Field, PrimeField}; use group::{Curve, Group, GroupEncoding}; use pasta_curves::{ - arithmetic::{CurveAffine, CurveExt, FieldExt}, + arithmetic::{CurveAffine, CurveExt}, pallas, }; use rand::RngCore; @@ -265,9 +265,9 @@ impl ValueCommitment { let abs_value = u64::try_from(value.0.abs()).expect("value must be in valid range"); let value = if value.0.is_negative() { - -pallas::Scalar::from_u64(abs_value) + -pallas::Scalar::from(abs_value) } else { - pallas::Scalar::from_u64(abs_value) + pallas::Scalar::from(abs_value) }; ValueCommitment(V * value + R * rcv.0)