Refactor Sinsemilla tests to be generic over domains.

This removes the need to import OrchardHashDomains and
OrchardCommitDomains to the sinsemilla crate.
This commit is contained in:
therealyingtong 2021-08-25 13:36:39 +08:00
parent 1acf0c2c15
commit c6a37af154
7 changed files with 406 additions and 155 deletions

View File

@ -23,16 +23,22 @@ ecc = { package = "halo2_ecc", version = "0.0", path = "../halo2_ecc" }
ff = "0.10"
group = "0.10"
halo2 = { version = "0.0", optional = true }
lazy_static = "1"
pasta_curves = "0.1.2"
rand = "0.8"
subtle = "2.3"
utilities = { package = "halo2_utilities", version = "0.0", path = "../halo2_utilities" }
# Developer tooling dependencies
plotters = { version = "0.3.0", optional = true }
[dev-dependencies]
orchard = "0.0"
criterion = "0.3"
[features]
dev-graph = ["halo2/dev-graph", "plotters"]
testing = []
test-ecc = ["ecc/testing"]
[[bench]]
name = "primitive"

View File

@ -88,8 +88,8 @@ where
pub struct SinsemillaChip<Hash, Commit, Fixed>
where
Hash: HashDomains<pallas::Affine>,
Fixed: FixedPoints<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
Fixed: FixedPoints<pallas::Affine>,
{
config: SinsemillaConfig<Hash, Commit, Fixed>,
}
@ -97,8 +97,8 @@ where
impl<Hash, Commit, Fixed> Chip<pallas::Base> for SinsemillaChip<Hash, Commit, Fixed>
where
Hash: HashDomains<pallas::Affine>,
Fixed: FixedPoints<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
Fixed: FixedPoints<pallas::Affine>,
{
type Config = SinsemillaConfig<Hash, Commit, Fixed>;
type Loaded = ();

View File

@ -1,5 +1,5 @@
//! Gadget and chips for the Sinsemilla hash function.
use ecc::gadget::{self as Ecc, EccInstructions};
use ecc::gadget::{self as Ecc, EccInstructions, FixedPoints};
use ff::PrimeField;
use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::arithmetic::{CurveAffine, FieldExt};
@ -41,7 +41,7 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
/// A point output of [`Self::hash_to_point`].
type NonIdentityPoint: Clone + Debug;
/// A type enumerating the fixed points used in `CommitDomains`.
type FixedPoints: Clone + Debug;
type FixedPoints: FixedPoints<C>;
/// HashDomains used in this instruction.
type HashDomains: HashDomains<C>;
@ -300,8 +300,8 @@ where
}
/// Trait allowing circuit's Sinsemilla CommitDomains to be enumerated.
pub trait CommitDomains<C: CurveAffine, F: Clone + Debug, H: HashDomains<C>>:
Clone + Debug
pub trait CommitDomains<C: CurveAffine, F: FixedPoints<C>, H: HashDomains<C>>:
Clone + Debug + Eq
{
/// Returns the fixed point corresponding to the R constant used for
/// randomization in this CommitDomain.
@ -313,7 +313,7 @@ pub trait CommitDomains<C: CurveAffine, F: Clone + Debug, H: HashDomains<C>>:
/// Trait allowing circuit's Sinsemilla HashDomains to be enumerated.
#[allow(non_snake_case)]
pub trait HashDomains<C: CurveAffine>: Clone + Debug {
pub trait HashDomains<C: CurveAffine>: Clone + Debug + Eq {
fn Q(&self) -> C;
}
@ -396,46 +396,54 @@ where
}
}
#[cfg(test)]
mod tests {
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error},
};
#[cfg(feature = "testing")]
pub mod testing {
use crate::{
chip::{SinsemillaChip, SinsemillaConfig},
gadget::{CommitDomain, HashDomain, Message, MessagePiece},
primitive::{self as sinsemilla, K},
gadget::{CommitDomain, CommitDomains, HashDomain, HashDomains, Message},
primitive as sinsemilla,
};
use ecc::{
chip::{EccChip, EccConfig},
gadget::NonIdentityPoint,
};
use orchard::constants::{
fixed_bases::COMMIT_IVK_PERSONALIZATION, sinsemilla::MERKLE_CRH_PERSONALIZATION,
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
gadget::{FixedPoints, NonIdentityPoint, Point},
};
use utilities::lookup_range_check::LookupRangeCheckConfig;
use group::Curve;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
plonk::{Circuit, ConstraintSystem, Error},
};
use pasta_curves::{arithmetic::FieldExt, pallas};
use group::{prime::PrimeCurveAffine, Curve};
use std::convert::TryInto;
use std::marker::PhantomData;
struct MyCircuit {}
pub struct MyCircuit<Hash, Commit, FixedBase, S: SinsemillaTest<Hash, Commit, FixedBase>>(
pub PhantomData<(Hash, Commit, FixedBase, S)>,
)
where
Hash: HashDomains<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, FixedBase, Hash>,
FixedBase: FixedPoints<pallas::Affine>;
impl Circuit<pallas::Base> for MyCircuit {
impl<Hash, Commit, FixedBase, S: SinsemillaTest<Hash, Commit, FixedBase>> Circuit<pallas::Base>
for MyCircuit<Hash, Commit, FixedBase, S>
where
Hash: HashDomains<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, FixedBase, Hash>,
FixedBase: FixedPoints<pallas::Affine>,
{
type Config = (
EccConfig,
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
SinsemillaConfig<Hash, Commit, FixedBase>,
SinsemillaConfig<Hash, Commit, FixedBase>,
);
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
MyCircuit {}
MyCircuit(PhantomData)
}
#[allow(non_snake_case)]
@ -478,7 +486,7 @@ mod tests {
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
let ecc_config = EccChip::<OrchardFixedBases>::configure(
let ecc_config = EccChip::<FixedBase>::configure(
meta,
advices,
lagrange_coeffs,
@ -512,65 +520,33 @@ mod tests {
let ecc_chip = EccChip::construct(config.0);
// The two `SinsemillaChip`s share the same lookup table.
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(
config.1.clone(),
&mut layouter,
)?;
SinsemillaChip::<Hash, Commit, FixedBase>::load(config.1.clone(), &mut layouter)?;
// This MerkleCRH example is purely for illustrative purposes.
// It is not an implementation of the Orchard protocol spec.
{
let chip1 = SinsemillaChip::construct(config.1);
for domain in S::hash_domains().iter() {
let chip1 = SinsemillaChip::construct(config.1.clone());
let merkle_crh = HashDomain::new(
chip1.clone(),
ecc_chip.clone(),
&OrchardHashDomains::MerkleCrh,
);
let hash_domain = HashDomain::new(chip1.clone(), ecc_chip.clone(), domain);
// Layer 31, l = MERKLE_DEPTH_ORCHARD - 1 - layer = 0
let l_bitstring = vec![Some(false); K];
let l = MessagePiece::from_bitstring(
chip1.clone(),
layouter.namespace(|| "l"),
&l_bitstring,
)?;
let message: Vec<Option<bool>> =
(0..500).map(|_| Some(rand::random::<bool>())).collect();
// Left leaf
let left_bitstring: Vec<Option<bool>> =
(0..250).map(|_| Some(rand::random::<bool>())).collect();
let left = MessagePiece::from_bitstring(
chip1.clone(),
layouter.namespace(|| "left"),
&left_bitstring,
)?;
let (result, _) = {
let message = Message::from_bitstring(
chip1,
layouter.namespace(|| "witness message"),
message.clone(),
)?;
hash_domain.hash_to_point(layouter.namespace(|| "hash"), message)?
};
// Right leaf
let right_bitstring: Vec<Option<bool>> =
(0..250).map(|_| Some(rand::random::<bool>())).collect();
let right = MessagePiece::from_bitstring(
chip1.clone(),
layouter.namespace(|| "right"),
&right_bitstring,
)?;
let l_bitstring: Option<Vec<bool>> = l_bitstring.into_iter().collect();
let left_bitstring: Option<Vec<bool>> = left_bitstring.into_iter().collect();
let right_bitstring: Option<Vec<bool>> = right_bitstring.into_iter().collect();
// Witness expected parent
let expected_parent = {
let expected_parent = if let (Some(l), Some(left), Some(right)) =
(l_bitstring, left_bitstring, right_bitstring)
{
let merkle_crh = sinsemilla::HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let point = merkle_crh
.hash_to_point(
l.into_iter()
.chain(left.into_iter())
.chain(right.into_iter()),
)
.unwrap();
let expected_result = {
let message: Option<Vec<bool>> = message.into_iter().collect();
let expected_result = if let Some(message) = message {
let point = sinsemilla::HashDomain {
Q: hash_domain.Q.to_curve(),
}
.hash_to_point(message.into_iter())
.unwrap();
Some(point.to_affine())
} else {
None
@ -578,31 +554,22 @@ mod tests {
NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "Witness expected parent"),
expected_parent,
layouter.namespace(|| "Witness expected result"),
expected_result,
)?
};
// Parent
let (parent, _) = {
let message = Message::from_pieces(chip1, vec![l, left, right]);
merkle_crh.hash_to_point(layouter.namespace(|| "parent"), message)?
};
parent.constrain_equal(
layouter.namespace(|| "parent == expected parent"),
&expected_parent,
result.constrain_equal(
layouter.namespace(|| "result == expected result"),
&expected_result,
)?;
}
{
let chip2 = SinsemillaChip::construct(config.2);
for domain in S::commit_domains().iter() {
let chip2 = SinsemillaChip::construct(config.2.clone());
let commit_domain = CommitDomain::new(chip2.clone(), ecc_chip.clone(), domain);
let commit_ivk = CommitDomain::new(
chip2.clone(),
ecc_chip.clone(),
&OrchardCommitDomains::CommitIvk,
);
let r_val = pallas::Scalar::rand();
let message: Vec<Option<bool>> =
(0..500).map(|_| Some(rand::random::<bool>())).collect();
@ -613,15 +580,21 @@ mod tests {
layouter.namespace(|| "witness message"),
message.clone(),
)?;
commit_ivk.commit(layouter.namespace(|| "commit"), message, Some(r_val))?
commit_domain.commit(layouter.namespace(|| "commit"), message, Some(r_val))?
};
// Witness expected result.
let expected_result = {
let message: Option<Vec<bool>> = message.into_iter().collect();
let expected_result = if let Some(message) = message {
let domain = sinsemilla::CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
let point = domain.commit(message.into_iter(), &r_val).unwrap();
let point = sinsemilla::CommitDomain {
M: sinsemilla::HashDomain {
Q: domain.hash_domain().Q().to_curve(),
},
R: domain.r().generator().to_curve(),
}
.commit(message.into_iter(), &r_val)
.unwrap();
Some(point.to_affine())
} else {
None
@ -637,15 +610,109 @@ mod tests {
result.constrain_equal(
layouter.namespace(|| "result == expected result"),
&expected_result,
)
)?;
}
Ok(())
}
}
pub trait SinsemillaTest<Hash, Commit, FixedBase>
where
Hash: HashDomains<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, FixedBase, Hash>,
FixedBase: FixedPoints<pallas::Affine>,
{
fn hash_domains() -> Vec<Hash>;
fn commit_domains() -> Vec<Commit>;
}
}
#[cfg(feature = "testing")]
#[cfg(feature = "test-ecc")]
pub mod tests {
use crate::{
gadget::{CommitDomains, HashDomains},
primitive as sinsemilla,
};
use ecc::{
chip::{compute_lagrange_coeffs, find_zs_and_us, NUM_WINDOWS},
gadget::{FixedPoints, H},
};
use group::Curve;
use pasta_curves::pallas;
use lazy_static::lazy_static;
#[cfg(feature = "testing")]
#[cfg(feature = "test-ecc")]
lazy_static! {
static ref PERSONALIZATION: &'static str = "personalization";
static ref COMMIT_DOMAIN: sinsemilla::CommitDomain =
sinsemilla::CommitDomain::new(*PERSONALIZATION);
static ref Q: pallas::Affine = COMMIT_DOMAIN.Q().to_affine();
static ref R: pallas::Affine = COMMIT_DOMAIN.R().to_affine();
static ref ZS_AND_US: Vec<(u64, [[u8; 32]; H])> = find_zs_and_us(*R, NUM_WINDOWS).unwrap();
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct FixedBase;
impl FixedPoints<pallas::Affine> for FixedBase {
fn generator(&self) -> pallas::Affine {
*R
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
ZS_AND_US.iter().map(|(_, us)| *us).collect()
}
fn z(&self) -> Vec<u64> {
ZS_AND_US.iter().map(|(z, _)| *z).collect()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct Hash;
impl HashDomains<pallas::Affine> for Hash {
fn Q(&self) -> pallas::Affine {
*Q
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Commit;
impl CommitDomains<pallas::Affine, FixedBase, Hash> for Commit {
fn r(&self) -> FixedBase {
FixedBase
}
fn hash_domain(&self) -> Hash {
Hash
}
}
struct Test;
impl super::testing::SinsemillaTest<Hash, Commit, FixedBase> for Test {
fn hash_domains() -> Vec<Hash> {
vec![Hash]
}
fn commit_domains() -> Vec<Commit> {
vec![Commit]
}
}
#[test]
fn sinsemilla_chip() {
use halo2::dev::MockProver;
let k = 11;
let circuit = MyCircuit {};
let circuit =
super::testing::MyCircuit::<Hash, Commit, FixedBase, Test>(std::marker::PhantomData);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}
@ -660,7 +727,8 @@ mod tests {
root.fill(&WHITE).unwrap();
let root = root.titled("SinsemillaHash", ("sans-serif", 60)).unwrap();
let circuit = MyCircuit {};
let circuit =
super::testing::MyCircuit::<Hash, Commit, FixedBase, Test>(std::marker::PhantomData);
halo2::dev::CircuitLayout::default()
.render(11, &circuit, &root)
.unwrap();

View File

@ -13,15 +13,11 @@ use std::iter;
pub mod chip;
/// SWU hash-to-curve personalization for the Merkle CRH generator
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
/// Depth of the Merkle tree.
pub(crate) const MERKLE_DEPTH: usize = 32;
/// $\mathsf{MerkleDepth^{Orchard}}$
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
/// Number of bits in a Pallas base field element.
pub(crate) const L_ORCHARD_BASE: usize = 255;
pub(crate) const L_PALLAS_BASE: usize = 255;
/// The sequence of bits representing a u64 in little-endian order.
///
@ -50,7 +46,7 @@ pub trait MerkleInstructions<
{
/// Compute MerkleCRH for a given `layer`. The hash that computes the root
/// is at layer 0, and the hashes that are applied to two leaves are at
/// layer `MERKLE_DEPTH_ORCHARD - 1` = layer 31.
/// layer `MERKLE_DEPTH - 1` = layer 31.
#[allow(non_snake_case)]
fn hash_layer(
&self,
@ -135,7 +131,7 @@ where
let mut node = leaf;
for (l, ((sibling, pos), chip)) in path.iter().zip(pos.iter()).zip(chips).enumerate() {
// `l` = MERKLE_DEPTH_ORCHARD - layer - 1, which is the index obtained from
// `l` = MERKLE_DEPTH - layer - 1, which is the index obtained from
// enumerating this Merkle path (going from leaf to root).
// For example, when `layer = 31` (the first sibling on the Merkle path),
// we have `l` = 32 - 31 - 1 = 0.
@ -164,47 +160,69 @@ where
}
}
#[cfg(test)]
pub mod tests {
#[cfg(feature = "testing")]
pub mod testing {
use super::{
chip::{MerkleChip, MerkleConfig},
i2lebsp, MerklePath, L_ORCHARD_BASE, MERKLE_CRH_PERSONALIZATION, MERKLE_DEPTH_ORCHARD,
i2lebsp, MerklePath, L_PALLAS_BASE, MERKLE_DEPTH,
};
use crate::{chip::SinsemillaChip, primitive::HashDomain};
use crate::{
chip::SinsemillaChip,
gadget::{CommitDomains, HashDomains},
primitive::HashDomain,
};
use ecc::gadget::FixedPoints;
use utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions, Var};
use orchard::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
use ff::PrimeFieldBits;
use group::prime::PrimeCurveAffine;
use halo2::{
arithmetic::FieldExt,
circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
note::commitment::ExtractedNoteCommitment,
pasta::pallas,
plonk::{Circuit, ConstraintSystem, Error},
tree,
};
use pasta_curves::pallas;
use rand::random;
use std::convert::TryInto;
use std::marker::PhantomData;
#[derive(Default)]
struct MyCircuit {
leaf: Option<pallas::Base>,
leaf_pos: Option<u32>,
merkle_path: Option<[pallas::Base; MERKLE_DEPTH_ORCHARD]>,
pub struct MyCircuit<Hash, Commit, FixedBase, S: MerkleTest<Hash>>
where
Hash: HashDomains<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, FixedBase, Hash>,
FixedBase: FixedPoints<pallas::Affine>,
{
pub leaf: Option<pallas::Base>,
pub leaf_pos: Option<u32>,
pub merkle_path: Option<[pallas::Base; MERKLE_DEPTH]>,
pub _marker: PhantomData<(Hash, Commit, FixedBase, S)>,
}
impl Circuit<pallas::Base> for MyCircuit {
impl<Hash, Commit, FixedBase, S: MerkleTest<Hash>> Circuit<pallas::Base>
for MyCircuit<Hash, Commit, FixedBase, S>
where
Hash: HashDomains<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, FixedBase, Hash>,
FixedBase: FixedPoints<pallas::Affine>,
{
type Config = (
MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
MerkleConfig<Hash, Commit, FixedBase>,
MerkleConfig<Hash, Commit, FixedBase>,
);
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
Self {
leaf: None,
leaf_pos: None,
merkle_path: None,
_marker: PhantomData,
}
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
@ -268,7 +286,7 @@ pub mod tests {
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
// Load generator table (shared across both configs)
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(
SinsemillaChip::<Hash, Commit, FixedBase>::load(
config.0.sinsemilla_config.clone(),
&mut layouter,
)?;
@ -286,7 +304,7 @@ pub mod tests {
let path = MerklePath {
chip_1,
chip_2,
domain: OrchardHashDomains::MerkleCrh,
domain: S::hash_domain(),
leaf_pos: self.leaf_pos,
path: self.merkle_path,
};
@ -295,6 +313,10 @@ pub mod tests {
path.calculate_root(layouter.namespace(|| "calculate root"), leaf)?;
if let Some(leaf_pos) = self.leaf_pos {
let domain = HashDomain {
Q: S::hash_domain().Q().to_curve(),
};
// The expected final root
let final_root = {
let path = tree::MerklePath::new(leaf_pos, self.merkle_path.unwrap());
@ -310,24 +332,104 @@ pub mod tests {
Ok(())
}
}
}
#[cfg(feature = "testing")]
#[cfg(feature = "test-ecc")]
pub mod tests {
use ecc::{
chip::{compute_lagrange_coeffs, find_zs_and_us, NUM_WINDOWS},
gadget::{FixedPoints, H},
};
use crate::{
gadget::{CommitDomains, HashDomains},
primitive::{CommitDomain, HashDomain},
};
use group::Curve;
use pasta_curves::pallas;
use lazy_static::lazy_static;
lazy_static! {
static ref PERSONALIZATION: &'static str = "MerkleCRH";
static ref HASH_DOMAIN: HashDomain = HashDomain::new(*PERSONALIZATION);
static ref Q: pallas::Affine = HASH_DOMAIN.Q().to_affine();
static ref R: pallas::Affine = CommitDomain::new(*PERSONALIZATION).R().to_affine();
static ref ZS_AND_US: Vec<(u64, [[u8; 32]; H])> = find_zs_and_us(*R, NUM_WINDOWS).unwrap();
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct FixedBase;
impl FixedPoints<pallas::Affine> for FixedBase {
fn generator(&self) -> pallas::Affine {
*R
}
fn u(&self) -> Vec<[[u8; 32]; H]> {
ZS_AND_US.iter().map(|(_, us)| *us).collect()
}
fn z(&self) -> Vec<u64> {
ZS_AND_US.iter().map(|(z, _)| *z).collect()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
struct Hash;
impl HashDomains<pallas::Affine> for Hash {
fn Q(&self) -> pallas::Affine {
*Q
}
}
// This test does not make use of the CommitDomain.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Commit;
impl CommitDomains<pallas::Affine, FixedBase, Hash> for Commit {
fn r(&self) -> FixedBase {
FixedBase
}
fn hash_domain(&self) -> Hash {
Hash
}
}
struct Test;
impl super::testing::MerkleTest<Hash> for Test {
fn hash_domain() -> Hash {
Hash
}
}
#[test]
fn merkle_chip() {
use crate::merkle::{i2lebsp, MERKLE_DEPTH};
use halo2::dev::MockProver;
use pasta_curves::arithmetic::FieldExt;
use rand::random;
use std::convert::TryInto;
// Choose a random leaf and position
let leaf = pallas::Base::rand();
let pos = random::<u32>();
// Choose a path of random inner nodes
let path: Vec<_> = (0..(MERKLE_DEPTH_ORCHARD))
.map(|_| pallas::Base::rand())
.collect();
let path: Vec<_> = (0..(MERKLE_DEPTH)).map(|_| pallas::Base::rand()).collect();
// The root is provided as a public input in the Orchard circuit.
let circuit = MyCircuit {
let circuit = super::testing::MyCircuit::<Hash, Commit, FixedBase, Test> {
leaf: Some(leaf),
leaf_pos: Some(pos),
merkle_path: Some(path.try_into().unwrap()),
_marker: std::marker::PhantomData,
};
let prover = MockProver::run(11, &circuit, vec![]).unwrap();
@ -343,7 +445,7 @@ pub mod tests {
root.fill(&WHITE).unwrap();
let root = root.titled("MerkleCRH Path", ("sans-serif", 60)).unwrap();
let circuit = MyCircuit::default();
let circuit = super::testing::MyCircuit::<Hash, Commit, FixedBase, Test>::default();
halo2::dev::CircuitLayout::default()
.show_labels(false)
.render(11, &circuit, &root)

View File

@ -6,7 +6,7 @@ use halo2::{
};
use pasta_curves::{arithmetic::FieldExt, pallas};
use super::{MerkleInstructions, L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD};
use super::{MerkleInstructions, L_PALLAS_BASE, MERKLE_DEPTH};
use crate::{
chip::{SinsemillaChip, SinsemillaConfig},
gadget::{CommitDomains, HashDomains, SinsemillaInstructions},
@ -176,7 +176,7 @@ where
}
impl<Hash, Commit, F>
MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K }, { sinsemilla::C }>
MerkleInstructions<pallas::Affine, MERKLE_DEPTH, { sinsemilla::K }, { sinsemilla::C }>
for MerkleChip<Hash, Commit, F>
where
Hash: HashDomains<pallas::Affine>,
@ -188,7 +188,7 @@ where
&self,
mut layouter: impl Layouter<pallas::Base>,
Q: pallas::Affine,
// l = MERKLE_DEPTH_ORCHARD - layer - 1
// l = MERKLE_DEPTH - layer - 1
l: usize,
left: Self::Var,
right: Self::Var,
@ -230,7 +230,7 @@ where
let b_1 = {
let b_1 = left
.value()
.map(|value| bitrange_subset(value, 250..L_ORCHARD_BASE));
.map(|value| bitrange_subset(value, 250..L_PALLAS_BASE));
config
.sinsemilla_config
@ -271,7 +271,7 @@ where
// `c = bits 5..=254 of `right`
let c = right
.value()
.map(|value| bitrange_subset(value, 5..L_ORCHARD_BASE));
.map(|value| bitrange_subset(value, 5..L_PALLAS_BASE));
self.witness_message_piece(layouter.namespace(|| "Witness c"), c, 25)?
};
@ -296,7 +296,7 @@ where
|| "Check piece decomposition",
|mut region| {
// Set the fixed column `l` to the current l.
// Recall that l = MERKLE_DEPTH_ORCHARD - layer - 1.
// Recall that l = MERKLE_DEPTH - layer - 1.
// The layer with 2^n nodes is called "layer n".
config.q_decompose.enable(&mut region, 0)?;
region.assign_advice_from_constant(
@ -356,7 +356,6 @@ where
// Check layer hash output against Sinsemilla primitives hash
#[cfg(test)]
{
use super::MERKLE_CRH_PERSONALIZATION;
use crate::{merkle::i2lebsp, primitive::HashDomain};
use ff::PrimeFieldBits;
@ -366,15 +365,15 @@ where
.to_le_bits()
.iter()
.by_val()
.take(L_ORCHARD_BASE)
.take(L_PALLAS_BASE)
.collect();
let right: Vec<_> = right
.to_le_bits()
.iter()
.by_val()
.take(L_ORCHARD_BASE)
.take(L_PALLAS_BASE)
.collect();
let merkle_crh = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
let merkle_crh = HashDomain { Q };
let mut message = l.to_vec();
message.extend_from_slice(&left);

View File

@ -121,7 +121,7 @@ impl<I: Iterator<Item = bool>> Iterator for Pad<I> {
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
pub struct HashDomain {
Q: pallas::Point,
pub(crate) Q: pallas::Point,
}
impl HashDomain {
@ -164,8 +164,9 @@ impl HashDomain {
}
/// Returns the Sinsemilla $Q$ constant for this domain.
#[cfg(test)]
#[cfg(feature = "testing")]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub(crate) fn Q(&self) -> pallas::Point {
self.Q
}
@ -176,8 +177,8 @@ impl HashDomain {
#[derive(Debug)]
#[allow(non_snake_case)]
pub struct CommitDomain {
M: HashDomain,
R: pallas::Point,
pub(crate) M: HashDomain,
pub(crate) R: pallas::Point,
}
impl CommitDomain {

View File

@ -265,3 +265,78 @@ mod tests {
assert_eq!(two_pow_k * inv_two_pow_k, pallas::Base::one());
}
}
#[cfg(feature = "test-sinsemilla")]
#[cfg(feature = "test-ecc")]
#[test]
fn test_orchard_domains() {
use halo2::dev::MockProver;
use sinsemilla::gadget::testing;
struct OrchardTest;
impl testing::SinsemillaTest<OrchardHashDomains, OrchardCommitDomains, super::OrchardFixedBases>
for OrchardTest
{
fn hash_domains() -> Vec<OrchardHashDomains> {
vec![OrchardHashDomains::MerkleCrh]
}
fn commit_domains() -> Vec<OrchardCommitDomains> {
vec![
OrchardCommitDomains::CommitIvk,
OrchardCommitDomains::NoteCommit,
]
}
}
let k = 11;
let circuit = testing::MyCircuit::<
OrchardHashDomains,
OrchardCommitDomains,
super::OrchardFixedBases,
OrchardTest,
>(std::marker::PhantomData);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}
#[cfg(feature = "test-sinsemilla")]
#[cfg(feature = "test-ecc")]
#[test]
fn test_merkle_crh() {
use halo2::dev::MockProver;
use rand::random;
use sinsemilla::merkle::testing;
use std::convert::TryInto;
struct OrchardTest;
impl testing::MerkleTest<OrchardHashDomains> for OrchardTest {
fn hash_domain() -> OrchardHashDomains {
OrchardHashDomains::MerkleCrh
}
}
// Choose a random leaf and position
let leaf = pallas::Base::rand();
let pos = random::<u32>();
// Choose a path of random inner nodes
let path: Vec<_> = (0..(super::MERKLE_DEPTH_ORCHARD))
.map(|_| pallas::Base::rand())
.collect();
let k = 11;
let circuit = testing::MyCircuit::<
OrchardHashDomains,
OrchardCommitDomains,
super::OrchardFixedBases,
OrchardTest,
> {
leaf: Some(leaf),
leaf_pos: Some(pos),
merkle_path: Some(path.try_into().unwrap()),
_marker: std::marker::PhantomData,
};
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}