Skip to content

Commit 96290d9

Browse files
gadget::sinsemilla.rs: Add Sinsemilla test.
1 parent d692304 commit 96290d9

File tree

2 files changed

+203
-1
lines changed

2 files changed

+203
-1
lines changed

src/circuit/gadget/ecc.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,11 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> X<C, EccC
301301
pub fn from_inner(chip: EccChip, inner: EccChip::X) -> Self {
302302
X { chip, inner }
303303
}
304+
305+
/// Returns the inner `EccChip::X` representation in the circuit
306+
pub fn inner(&self) -> &EccChip::X {
307+
&self.inner
308+
}
304309
}
305310

306311
/// A constant elliptic curve point over the given curve, for which window tables have

src/circuit/gadget/sinsemilla.rs

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::fmt::Debug;
66
pub mod chip;
77
mod message;
88

9-
// pub use chip::{SinsemillaChip, SinsemillaConfig};
9+
pub use chip::{SinsemillaChip, SinsemillaConfig};
1010

1111
/// The set of circuit instructions required to use the [`Sinsemilla`](https://zcash.github.io/halo2/design/gadgets/sinsemilla.html) gadget.
1212
/// This trait is bounded on two constant parameters: `K`, the number of bits
@@ -303,3 +303,200 @@ where
303303
p.map(|p| p.extract_p())
304304
}
305305
}
306+
307+
#[cfg(test)]
308+
mod tests {
309+
use halo2::{
310+
arithmetic::{CurveAffine, FieldExt},
311+
circuit::{layouter::SingleChipLayouter, Layouter},
312+
dev::MockProver,
313+
pasta::pallas,
314+
plonk::{Assignment, Circuit, ConstraintSystem, Error},
315+
};
316+
317+
use super::{
318+
chip::{SinsemillaCommitDomains, SinsemillaHashDomains},
319+
CommitDomain, HashDomain, Message, SinsemillaChip, SinsemillaConfig,
320+
SinsemillaInstructions,
321+
};
322+
323+
use crate::circuit::gadget::{
324+
ecc::{
325+
chip::{EccChip, EccConfig},
326+
ScalarFixed,
327+
},
328+
utilities::Var,
329+
};
330+
331+
use std::convert::TryInto;
332+
333+
struct MyCircuit<C: CurveAffine, const K: usize, const MAX_WORDS: usize> {
334+
_marker: std::marker::PhantomData<C>,
335+
}
336+
337+
impl<C: CurveAffine, const K: usize, const MAX_WORDS: usize> Circuit<C::Base>
338+
for MyCircuit<C, K, MAX_WORDS>
339+
{
340+
type Config = (
341+
EccConfig,
342+
SinsemillaConfig<C, K, MAX_WORDS>,
343+
SinsemillaConfig<C, K, MAX_WORDS>,
344+
);
345+
346+
#[allow(non_snake_case)]
347+
fn configure(meta: &mut ConstraintSystem<C::Base>) -> Self::Config {
348+
let advices = [
349+
meta.advice_column(),
350+
meta.advice_column(),
351+
meta.advice_column(),
352+
meta.advice_column(),
353+
meta.advice_column(),
354+
meta.advice_column(),
355+
meta.advice_column(),
356+
meta.advice_column(),
357+
meta.advice_column(),
358+
meta.advice_column(),
359+
];
360+
let perm = meta.permutation(
361+
&advices
362+
.iter()
363+
.map(|advice| (*advice).into())
364+
.collect::<Vec<_>>(),
365+
);
366+
367+
let ecc_config = EccChip::<C>::configure(meta, advices, perm.clone());
368+
369+
// Fixed columns for the Sinsemilla generator lookup table
370+
let lookup = (
371+
meta.fixed_column(),
372+
meta.fixed_column(),
373+
meta.fixed_column(),
374+
);
375+
376+
let config1 = SinsemillaChip::<C, K, MAX_WORDS>::configure(
377+
meta,
378+
advices[..5].try_into().unwrap(),
379+
lookup,
380+
perm.clone(),
381+
);
382+
let config2 = SinsemillaChip::<C, K, MAX_WORDS>::configure(
383+
meta,
384+
advices[5..].try_into().unwrap(),
385+
lookup,
386+
perm,
387+
);
388+
(ecc_config, config1, config2)
389+
}
390+
391+
fn synthesize(
392+
&self,
393+
cs: &mut impl Assignment<C::Base>,
394+
config: Self::Config,
395+
) -> Result<(), Error> {
396+
let mut layouter = SingleChipLayouter::new(cs)?;
397+
let ecc_chip = EccChip::<C>::construct(config.0);
398+
399+
// The two `SinsemillaChip`s share the same lookup table.
400+
SinsemillaChip::<C, K, MAX_WORDS>::load(config.1.clone(), &mut layouter)?;
401+
402+
// This MerkleCRH example is purely for illustrative purposes.
403+
// It is not an implementation of the Orchard protocol spec.
404+
{
405+
let chip1 = SinsemillaChip::<C, K, MAX_WORDS>::construct(config.1);
406+
407+
let merkle_crh = HashDomain::new(
408+
chip1.clone(),
409+
ecc_chip.clone(),
410+
&SinsemillaHashDomains::MerkleCrh,
411+
);
412+
413+
// Left leaf
414+
let left = {
415+
let left: Vec<Option<bool>> =
416+
(0..250).map(|_| Some(rand::random::<bool>())).collect();
417+
let left = Message::from_bitstring(
418+
chip1.clone(),
419+
layouter.namespace(|| "witness left"),
420+
left,
421+
25,
422+
)?;
423+
let left = merkle_crh.hash_to_point(layouter.namespace(|| "left"), left)?;
424+
let left = left.extract_p();
425+
chip1.witness_message_piece_field(
426+
layouter.namespace(|| "witness left piece"),
427+
left.inner().value(),
428+
25,
429+
)?
430+
};
431+
432+
// Right leaf
433+
let right = {
434+
let right: Vec<Option<bool>> =
435+
(0..250).map(|_| Some(rand::random::<bool>())).collect();
436+
let right = Message::from_bitstring(
437+
chip1.clone(),
438+
layouter.namespace(|| "witness right"),
439+
right,
440+
25,
441+
)?;
442+
let right = merkle_crh.hash_to_point(layouter.namespace(|| "right"), right)?;
443+
let right = right.extract_p();
444+
chip1.witness_message_piece_field(
445+
layouter.namespace(|| "witness left piece"),
446+
right.inner().value(),
447+
25,
448+
)?
449+
};
450+
451+
// Layer 0
452+
let l = {
453+
let merkle_depth_orchard = 32;
454+
let layer = 0;
455+
let l = C::Base::from_u64(merkle_depth_orchard - 1 - layer);
456+
chip1.witness_message_piece_field(layouter.namespace(|| "l"), Some(l), 1)?
457+
};
458+
459+
// Parent
460+
let parent = Message::from_pieces(chip1, vec![l, left, right]);
461+
merkle_crh.hash_to_point(layouter.namespace(|| "parent"), parent)?;
462+
}
463+
464+
{
465+
let chip2 = SinsemillaChip::<C, K, MAX_WORDS>::construct(config.2);
466+
467+
let commit_ivk = CommitDomain::new(
468+
chip2.clone(),
469+
ecc_chip.clone(),
470+
&SinsemillaCommitDomains::CommitIvk,
471+
);
472+
let r = ScalarFixed::<C, EccChip<C>>::new(
473+
ecc_chip,
474+
layouter.namespace(|| "r"),
475+
Some(C::Scalar::rand()),
476+
)?;
477+
let message: Vec<Option<bool>> =
478+
(0..500).map(|_| Some(rand::random::<bool>())).collect();
479+
let message = Message::from_bitstring(
480+
chip2,
481+
layouter.namespace(|| "witness message"),
482+
message,
483+
50,
484+
)?;
485+
commit_ivk.commit(layouter.namespace(|| "commit"), message, r)?;
486+
}
487+
488+
Ok(())
489+
}
490+
}
491+
492+
#[test]
493+
fn sinsemilla() {
494+
use crate::primitives::sinsemilla::{C, K};
495+
let k = 11;
496+
let circuit = MyCircuit::<pallas::Affine, K, C> {
497+
_marker: std::marker::PhantomData,
498+
};
499+
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
500+
assert_eq!(prover.verify(), Ok(()))
501+
}
502+
}

0 commit comments

Comments
 (0)