mirror of https://github.com/zcash/orchard.git
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:
parent
1acf0c2c15
commit
c6a37af154
|
@ -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"
|
||||
|
|
|
@ -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 = ();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(()))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue