mirror of https://github.com/zcash/halo2.git
Merge pull request #186 from zcash/refactor-gadget-crates
Prepare to extract gadgets into crates
This commit is contained in:
commit
159ab53da5
|
@ -11,11 +11,27 @@ use memuse::DynamicUsage;
|
|||
use pasta_curves::{arithmetic::CurveAffine, pallas, vesta};
|
||||
use rand::RngCore;
|
||||
|
||||
use self::commit_ivk::CommitIvkConfig;
|
||||
use self::gadget::{
|
||||
ecc::{
|
||||
chip::{EccChip, EccConfig},
|
||||
FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
|
||||
},
|
||||
poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig},
|
||||
sinsemilla::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
merkle::{
|
||||
chip::{MerkleChip, MerkleConfig},
|
||||
MerklePath,
|
||||
},
|
||||
},
|
||||
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
||||
};
|
||||
use self::note_commit::NoteCommitConfig;
|
||||
use crate::{
|
||||
constants::{
|
||||
load::{NullifierK, OrchardFixedBasesFull, ValueCommitV},
|
||||
util::gen_const_array,
|
||||
MERKLE_DEPTH_ORCHARD,
|
||||
util::gen_const_array, NullifierK, OrchardCommitDomains, OrchardFixedBases,
|
||||
OrchardFixedBasesFull, OrchardHashDomains, ValueCommitV, MERKLE_DEPTH_ORCHARD,
|
||||
},
|
||||
keys::{
|
||||
CommitIvkRandomness, DiversifiedTransmissionKey, NullifierDerivingKey, SpendValidatingKey,
|
||||
|
@ -33,29 +49,12 @@ use crate::{
|
|||
tree::{Anchor, MerkleHashOrchard},
|
||||
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
|
||||
};
|
||||
use gadget::{
|
||||
ecc::{
|
||||
chip::{EccChip, EccConfig},
|
||||
FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
|
||||
},
|
||||
poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig},
|
||||
sinsemilla::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig, SinsemillaHashDomains},
|
||||
commit_ivk::CommitIvkConfig,
|
||||
merkle::{
|
||||
chip::{MerkleChip, MerkleConfig},
|
||||
MerklePath,
|
||||
},
|
||||
note_commit::NoteCommitConfig,
|
||||
},
|
||||
utilities::UtilitiesInstructions,
|
||||
};
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
use self::gadget::utilities::lookup_range_check::LookupRangeCheckConfig;
|
||||
|
||||
mod commit_ivk;
|
||||
pub mod gadget;
|
||||
mod note_commit;
|
||||
|
||||
/// Size of the Orchard circuit.
|
||||
const K: u32 = 11;
|
||||
|
@ -79,12 +78,14 @@ pub struct Config {
|
|||
// Selector for the field addition gate poseidon_hash(nk, rho_old) + psi_old.
|
||||
q_add: Selector,
|
||||
advices: [Column<Advice>; 10],
|
||||
ecc_config: EccConfig,
|
||||
ecc_config: EccConfig<OrchardFixedBases>,
|
||||
poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
|
||||
merkle_config_1: MerkleConfig,
|
||||
merkle_config_2: MerkleConfig,
|
||||
sinsemilla_config_1: SinsemillaConfig,
|
||||
sinsemilla_config_2: SinsemillaConfig,
|
||||
merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
sinsemilla_config_1:
|
||||
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
sinsemilla_config_2:
|
||||
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
commit_ivk_config: CommitIvkConfig,
|
||||
old_note_commit_config: NoteCommitConfig,
|
||||
new_note_commit_config: NoteCommitConfig,
|
||||
|
@ -234,7 +235,8 @@ impl plonk::Circuit<pallas::Base> 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);
|
||||
let ecc_config =
|
||||
EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check);
|
||||
|
||||
// Configuration for the Poseidon hash.
|
||||
let poseidon_config = PoseidonChip::configure::<poseidon::P128Pow5T3>(
|
||||
|
@ -390,14 +392,14 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
|
||||
// Merkle path validity check.
|
||||
let anchor = {
|
||||
let path = self.path.map(|typed_path| {
|
||||
let path: Option<[pallas::Base; MERKLE_DEPTH_ORCHARD]> = self.path.map(|typed_path| {
|
||||
// TODO: Replace with array::map once MSRV is 1.55.0.
|
||||
gen_const_array(|i| typed_path[i].inner())
|
||||
});
|
||||
let merkle_inputs = MerklePath {
|
||||
chip_1: config.merkle_chip_1(),
|
||||
chip_2: config.merkle_chip_2(),
|
||||
domain: SinsemillaHashDomains::MerkleCrh,
|
||||
domain: OrchardHashDomains::MerkleCrh,
|
||||
leaf_pos: self.pos,
|
||||
path,
|
||||
};
|
||||
|
@ -444,7 +446,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
|
||||
// commitment = [v_net] ValueCommitV
|
||||
let (commitment, _) = {
|
||||
let value_commit_v = ValueCommitV::get();
|
||||
let value_commit_v = ValueCommitV;
|
||||
let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), value_commit_v);
|
||||
value_commit_v.mul(layouter.namespace(|| "[v_net] ValueCommitV"), v_net.clone())?
|
||||
};
|
||||
|
|
|
@ -8,28 +8,32 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
|
|||
use crate::{
|
||||
circuit::gadget::{
|
||||
ecc::{chip::EccChip, X},
|
||||
sinsemilla::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
CommitDomain, Message, MessagePiece,
|
||||
},
|
||||
utilities::{bitrange_subset, bool_check},
|
||||
},
|
||||
constants::T_P,
|
||||
};
|
||||
|
||||
use super::{
|
||||
chip::{SinsemillaChip, SinsemillaCommitDomains, SinsemillaConfig},
|
||||
CommitDomain, Message, MessagePiece,
|
||||
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommitIvkConfig {
|
||||
q_commit_ivk: Selector,
|
||||
advices: [Column<Advice>; 10],
|
||||
sinsemilla_config: SinsemillaConfig,
|
||||
sinsemilla_config:
|
||||
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
}
|
||||
|
||||
impl CommitIvkConfig {
|
||||
pub(in crate::circuit) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
advices: [Column<Advice>; 10],
|
||||
sinsemilla_config: SinsemillaConfig,
|
||||
sinsemilla_config: SinsemillaConfig<
|
||||
OrchardHashDomains,
|
||||
OrchardCommitDomains,
|
||||
OrchardFixedBases,
|
||||
>,
|
||||
) -> Self {
|
||||
let q_commit_ivk = meta.selector();
|
||||
|
||||
|
@ -222,13 +226,17 @@ impl CommitIvkConfig {
|
|||
#[allow(clippy::type_complexity)]
|
||||
pub(in crate::circuit) fn assign_region(
|
||||
&self,
|
||||
sinsemilla_chip: SinsemillaChip,
|
||||
ecc_chip: EccChip,
|
||||
sinsemilla_chip: SinsemillaChip<
|
||||
OrchardHashDomains,
|
||||
OrchardCommitDomains,
|
||||
OrchardFixedBases,
|
||||
>,
|
||||
ecc_chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
ak: AssignedCell<pallas::Base, pallas::Base>,
|
||||
nk: AssignedCell<pallas::Base, pallas::Base>,
|
||||
rivk: Option<pallas::Scalar>,
|
||||
) -> Result<X<pallas::Affine, EccChip>, Error> {
|
||||
) -> Result<X<pallas::Affine, EccChip<OrchardFixedBases>>, Error> {
|
||||
// <https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
|
||||
// We need to hash `ak || nk` where each of `ak`, `nk` is a field element (255 bits).
|
||||
//
|
||||
|
@ -263,13 +271,13 @@ impl CommitIvkConfig {
|
|||
});
|
||||
|
||||
// Constrain b_0 to be 4 bits.
|
||||
let b_0 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let b_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "b_0 is 4 bits"),
|
||||
b_0,
|
||||
4,
|
||||
)?;
|
||||
// Constrain b_2 to be 5 bits.
|
||||
let b_2 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let b_2 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "b_2 is 5 bits"),
|
||||
b_2,
|
||||
5,
|
||||
|
@ -307,7 +315,7 @@ impl CommitIvkConfig {
|
|||
.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(
|
||||
let d_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "d_0 is 9 bits"),
|
||||
d_0,
|
||||
9,
|
||||
|
@ -329,11 +337,8 @@ impl CommitIvkConfig {
|
|||
sinsemilla_chip.clone(),
|
||||
vec![a.clone(), b.clone(), c.clone(), d.clone()],
|
||||
);
|
||||
let domain = CommitDomain::new(
|
||||
sinsemilla_chip,
|
||||
ecc_chip,
|
||||
&SinsemillaCommitDomains::CommitIvk,
|
||||
);
|
||||
let domain =
|
||||
CommitDomain::new(sinsemilla_chip, ecc_chip, &OrchardCommitDomains::CommitIvk);
|
||||
domain.short_commit(layouter.namespace(|| "Hash ak||nk"), message, rivk)?
|
||||
};
|
||||
|
||||
|
@ -406,7 +411,7 @@ impl CommitIvkConfig {
|
|||
let t_p = pallas::Base::from_u128(T_P);
|
||||
a + two_pow_130 - t_p
|
||||
});
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"),
|
||||
a_prime,
|
||||
13,
|
||||
|
@ -449,7 +454,7 @@ impl CommitIvkConfig {
|
|||
let t_p = pallas::Base::from_u128(T_P);
|
||||
b_2 + c * two_pow_5 + two_pow_140 - t_p
|
||||
});
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose low 140 bits of (b_2 + c * 2^5 + 2^140 - t_P)"),
|
||||
b2_c_prime,
|
||||
14,
|
||||
|
@ -636,7 +641,10 @@ mod tests {
|
|||
sinsemilla::chip::SinsemillaChip,
|
||||
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
||||
},
|
||||
constants::{COMMIT_IVK_PERSONALIZATION, L_ORCHARD_BASE, T_Q},
|
||||
constants::{
|
||||
fixed_bases::COMMIT_IVK_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases,
|
||||
OrchardHashDomains, L_ORCHARD_BASE, T_Q,
|
||||
},
|
||||
primitives::sinsemilla::CommitDomain,
|
||||
};
|
||||
use group::ff::{Field, PrimeFieldBits};
|
||||
|
@ -663,7 +671,7 @@ mod tests {
|
|||
}
|
||||
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
type Config = (CommitIvkConfig, EccConfig);
|
||||
type Config = (CommitIvkConfig, EccConfig<OrchardFixedBases>);
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
|
@ -709,7 +717,11 @@ mod tests {
|
|||
];
|
||||
|
||||
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
|
||||
let sinsemilla_config = SinsemillaChip::configure(
|
||||
let sinsemilla_config = SinsemillaChip::<
|
||||
OrchardHashDomains,
|
||||
OrchardCommitDomains,
|
||||
OrchardFixedBases,
|
||||
>::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[2],
|
||||
|
@ -721,7 +733,12 @@ mod tests {
|
|||
let commit_ivk_config =
|
||||
CommitIvkConfig::configure(meta, advices, sinsemilla_config);
|
||||
|
||||
let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check);
|
||||
let ecc_config = EccChip::<OrchardFixedBases>::configure(
|
||||
meta,
|
||||
advices,
|
||||
lagrange_coeffs,
|
||||
range_check,
|
||||
);
|
||||
|
||||
(commit_ivk_config, ecc_config)
|
||||
}
|
||||
|
@ -734,7 +751,7 @@ mod tests {
|
|||
let (commit_ivk_config, ecc_config) = config;
|
||||
|
||||
// Load the Sinsemilla generator lookup table used by the whole circuit.
|
||||
SinsemillaChip::load(commit_ivk_config.sinsemilla_config.clone(), &mut layouter)?;
|
||||
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(commit_ivk_config.sinsemilla_config.clone(), &mut layouter)?;
|
||||
|
||||
// Construct a Sinsemilla chip
|
||||
let sinsemilla_chip =
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use pasta_curves::pallas;
|
||||
|
||||
use crate::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
|
||||
use ecc::chip::EccChip;
|
||||
use poseidon::Pow5Chip as PoseidonChip;
|
||||
use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip};
|
||||
|
@ -12,23 +13,31 @@ pub(crate) mod sinsemilla;
|
|||
pub mod utilities;
|
||||
|
||||
impl super::Config {
|
||||
pub(super) fn ecc_chip(&self) -> EccChip {
|
||||
pub(super) fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
|
||||
EccChip::construct(self.ecc_config.clone())
|
||||
}
|
||||
|
||||
pub(super) fn sinsemilla_chip_1(&self) -> SinsemillaChip {
|
||||
pub(super) fn sinsemilla_chip_1(
|
||||
&self,
|
||||
) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
|
||||
SinsemillaChip::construct(self.sinsemilla_config_1.clone())
|
||||
}
|
||||
|
||||
pub(super) fn sinsemilla_chip_2(&self) -> SinsemillaChip {
|
||||
pub(super) fn sinsemilla_chip_2(
|
||||
&self,
|
||||
) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
|
||||
SinsemillaChip::construct(self.sinsemilla_config_2.clone())
|
||||
}
|
||||
|
||||
pub(super) fn merkle_chip_1(&self) -> MerkleChip {
|
||||
pub(super) fn merkle_chip_1(
|
||||
&self,
|
||||
) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
|
||||
MerkleChip::construct(self.merkle_config_1.clone())
|
||||
}
|
||||
|
||||
pub(super) fn merkle_chip_2(&self) -> MerkleChip {
|
||||
pub(super) fn merkle_chip_2(
|
||||
&self,
|
||||
) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
|
||||
MerkleChip::construct(self.merkle_config_2.clone())
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ use crate::circuit::gadget::utilities::UtilitiesInstructions;
|
|||
pub mod chip;
|
||||
|
||||
/// The set of circuit instructions required to use the ECC gadgets.
|
||||
pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions<C::Base> {
|
||||
pub trait EccInstructions<C: CurveAffine>:
|
||||
Chip<C::Base> + UtilitiesInstructions<C::Base> + Clone + Debug + Eq
|
||||
{
|
||||
/// Variable representing an element of the elliptic curve's base field, that
|
||||
/// is used as a scalar in variable-base scalar mul.
|
||||
///
|
||||
|
@ -41,12 +43,11 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
/// Variable representing the affine short Weierstrass x-coordinate of an
|
||||
/// elliptic curve point.
|
||||
type X: Clone + Debug;
|
||||
/// Enumeration of the set of fixed bases to be used in scalar mul with a full-width scalar.
|
||||
type FixedPoints: Clone + Debug;
|
||||
/// Enumeration of the set of fixed bases to be used in scalar mul with a base field element.
|
||||
type FixedPointsBaseField: Clone + Debug;
|
||||
/// Enumeration of the set of fixed bases to be used in short signed scalar mul.
|
||||
type FixedPointsShort: Clone + Debug;
|
||||
/// Enumeration of the set of fixed bases to be used in scalar mul.
|
||||
/// TODO: When associated consts can be used as const generics, introduce
|
||||
/// `Self::NUM_WINDOWS`, `Self::NUM_WINDOWS_BASE_FIELD`, `Self::NUM_WINDOWS_SHORT`
|
||||
/// and use them to differentiate `FixedPoints` types.
|
||||
type FixedPoints: FixedPoints<C>;
|
||||
|
||||
/// Constrains point `a` to be equal in value to point `b`.
|
||||
fn constrain_equal(
|
||||
|
@ -107,7 +108,7 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
scalar: Option<C::Scalar>,
|
||||
base: &Self::FixedPoints,
|
||||
base: &<Self::FixedPoints as FixedPoints<C>>::FullScalar,
|
||||
) -> Result<(Self::Point, Self::ScalarFixed), Error>;
|
||||
|
||||
/// Performs fixed-base scalar multiplication using a short signed scalar, returning
|
||||
|
@ -116,7 +117,7 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
magnitude_sign: (Self::Var, Self::Var),
|
||||
base: &Self::FixedPointsShort,
|
||||
base: &<Self::FixedPoints as FixedPoints<C>>::ShortScalar,
|
||||
) -> Result<(Self::Point, Self::ScalarFixedShort), Error>;
|
||||
|
||||
/// Performs fixed-base scalar multiplication using a base field element as the scalar.
|
||||
|
@ -126,10 +127,17 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
base_field_elem: Self::Var,
|
||||
base: &Self::FixedPointsBaseField,
|
||||
base: &<Self::FixedPoints as FixedPoints<C>>::Base,
|
||||
) -> Result<Self::Point, Error>;
|
||||
}
|
||||
|
||||
/// Defines the fixed points for a given instantiation of the ECC chip.
|
||||
pub trait FixedPoints<C: CurveAffine>: Debug + Eq + Clone {
|
||||
type FullScalar: Debug + Eq + Clone;
|
||||
type ShortScalar: Debug + Eq + Clone;
|
||||
type Base: Debug + Eq + Clone;
|
||||
}
|
||||
|
||||
/// An element of the given elliptic curve's base field, that is used as a scalar
|
||||
/// in variable-base scalar mul.
|
||||
///
|
||||
|
@ -142,41 +150,33 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
/// to be in the base field of the curve. (See non-normative notes in
|
||||
/// https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents.)
|
||||
#[derive(Debug)]
|
||||
pub struct ScalarVar<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> {
|
||||
pub struct ScalarVar<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: EccChip::ScalarVar,
|
||||
}
|
||||
|
||||
/// A full-width element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul.
|
||||
#[derive(Debug)]
|
||||
pub struct ScalarFixed<C: CurveAffine, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
pub struct ScalarFixed<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: EccChip::ScalarFixed,
|
||||
}
|
||||
|
||||
/// A signed short element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul.
|
||||
#[derive(Debug)]
|
||||
pub struct ScalarFixedShort<C: CurveAffine, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
pub struct ScalarFixedShort<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: EccChip::ScalarFixedShort,
|
||||
}
|
||||
|
||||
/// A non-identity elliptic curve point over the given curve.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NonIdentityPoint<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> {
|
||||
pub struct NonIdentityPoint<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: EccChip::NonIdentityPoint,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq>
|
||||
NonIdentityPoint<C, EccChip>
|
||||
{
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C>> NonIdentityPoint<C, EccChip> {
|
||||
/// Constructs a new point with the given value.
|
||||
pub fn new(
|
||||
chip: EccChip,
|
||||
|
@ -351,12 +351,12 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
|
|||
/// The affine short Weierstrass x-coordinate of an elliptic curve point over the
|
||||
/// given curve.
|
||||
#[derive(Debug)]
|
||||
pub struct X<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> {
|
||||
pub struct X<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: EccChip::X,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> X<C, EccChip> {
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C>> X<C, EccChip> {
|
||||
/// Wraps the given x-coordinate (obtained directly from an instruction) in a gadget.
|
||||
pub fn from_inner(chip: EccChip, inner: EccChip::X) -> Self {
|
||||
X { chip, inner }
|
||||
|
@ -373,18 +373,28 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> X<C, EccC
|
|||
///
|
||||
/// Used in scalar multiplication with full-width scalars.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FixedPoint<C: CurveAffine, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
pub struct FixedPoint<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: EccChip::FixedPoints,
|
||||
inner: <EccChip::FixedPoints as FixedPoints<C>>::FullScalar,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine, EccChip> FixedPoint<C, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
/// A constant elliptic curve point over the given curve, used in scalar multiplication
|
||||
/// with a base field element
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FixedPointBaseField<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: <EccChip::FixedPoints as FixedPoints<C>>::Base,
|
||||
}
|
||||
|
||||
/// A constant elliptic curve point over the given curve, used in scalar multiplication
|
||||
/// with a short signed exponent
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FixedPointShort<C: CurveAffine, EccChip: EccInstructions<C>> {
|
||||
chip: EccChip,
|
||||
inner: <EccChip::FixedPoints as FixedPoints<C>>::ShortScalar,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPoint<C, EccChip> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
/// Returns `[by] self`.
|
||||
pub fn mul(
|
||||
|
@ -409,26 +419,15 @@ where
|
|||
}
|
||||
|
||||
/// Wraps the given fixed base (obtained directly from an instruction) in a gadget.
|
||||
pub fn from_inner(chip: EccChip, inner: EccChip::FixedPoints) -> Self {
|
||||
FixedPoint { chip, inner }
|
||||
pub fn from_inner(
|
||||
chip: EccChip,
|
||||
inner: <EccChip::FixedPoints as FixedPoints<C>>::FullScalar,
|
||||
) -> Self {
|
||||
Self { chip, inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// A constant elliptic curve point over the given curve, used in scalar multiplication
|
||||
/// with a base field element
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FixedPointBaseField<C: CurveAffine, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
chip: EccChip,
|
||||
inner: EccChip::FixedPointsBaseField,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine, EccChip> FixedPointBaseField<C, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPointBaseField<C, EccChip> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
/// Returns `[by] self`.
|
||||
pub fn mul(
|
||||
|
@ -445,26 +444,15 @@ where
|
|||
}
|
||||
|
||||
/// Wraps the given fixed base (obtained directly from an instruction) in a gadget.
|
||||
pub fn from_inner(chip: EccChip, inner: EccChip::FixedPointsBaseField) -> Self {
|
||||
FixedPointBaseField { chip, inner }
|
||||
pub fn from_inner(
|
||||
chip: EccChip,
|
||||
inner: <EccChip::FixedPoints as FixedPoints<C>>::Base,
|
||||
) -> Self {
|
||||
Self { chip, inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// A constant elliptic curve point over the given curve, used in scalar multiplication
|
||||
/// with a short signed exponent
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FixedPointShort<C: CurveAffine, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
chip: EccChip,
|
||||
inner: EccChip::FixedPointsShort,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine, EccChip> FixedPointShort<C, EccChip>
|
||||
where
|
||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||
{
|
||||
impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPointShort<C, EccChip> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
/// Returns `[by] self`.
|
||||
pub fn mul(
|
||||
|
@ -489,8 +477,11 @@ where
|
|||
}
|
||||
|
||||
/// Wraps the given fixed base (obtained directly from an instruction) in a gadget.
|
||||
pub fn from_inner(chip: EccChip, inner: EccChip::FixedPointsShort) -> Self {
|
||||
FixedPointShort { chip, inner }
|
||||
pub fn from_inner(
|
||||
chip: EccChip,
|
||||
inner: <EccChip::FixedPoints as FixedPoints<C>>::ShortScalar,
|
||||
) -> Self {
|
||||
Self { chip, inner }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,6 +498,7 @@ mod tests {
|
|||
|
||||
use super::chip::{EccChip, EccConfig};
|
||||
use crate::circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig;
|
||||
use crate::constants::OrchardFixedBases;
|
||||
|
||||
struct MyCircuit {
|
||||
test_errors: bool,
|
||||
|
@ -514,7 +506,7 @@ mod tests {
|
|||
|
||||
#[allow(non_snake_case)]
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
type Config = EccConfig;
|
||||
type Config = EccConfig<OrchardFixedBases>;
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
|
@ -550,7 +542,7 @@ mod tests {
|
|||
meta.enable_constant(constants);
|
||||
|
||||
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
|
||||
EccChip::configure(meta, advices, lagrange_coeffs, range_check)
|
||||
EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use super::EccInstructions;
|
||||
use super::{EccInstructions, FixedPoints};
|
||||
use crate::{
|
||||
circuit::gadget::utilities::{
|
||||
lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions,
|
||||
},
|
||||
constants::{self, NullifierK, OrchardFixedBasesFull, ValueCommitV},
|
||||
primitives::sinsemilla,
|
||||
};
|
||||
use arrayvec::ArrayVec;
|
||||
|
@ -20,10 +19,13 @@ use std::convert::TryInto;
|
|||
|
||||
pub(super) mod add;
|
||||
pub(super) mod add_incomplete;
|
||||
pub mod constants;
|
||||
pub(super) mod mul;
|
||||
pub(super) mod mul_fixed;
|
||||
pub(super) mod witness_point;
|
||||
|
||||
pub use constants::*;
|
||||
|
||||
/// A curve point represented in affine (x, y) coordinates, or the
|
||||
/// identity represented as (0, 0).
|
||||
/// Each coordinate is assigned to a cell.
|
||||
|
@ -131,7 +133,7 @@ impl From<NonIdentityEccPoint> for EccPoint {
|
|||
/// Configuration for the ECC chip
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct EccConfig {
|
||||
pub struct EccConfig<FixedPoints: super::FixedPoints<pallas::Affine>> {
|
||||
/// Advice columns needed by instructions in the ECC chip.
|
||||
pub advices: [Column<Advice>; 10],
|
||||
|
||||
|
@ -145,11 +147,11 @@ pub struct EccConfig {
|
|||
mul: mul::Config,
|
||||
|
||||
/// Fixed-base full-width scalar multiplication
|
||||
mul_fixed_full: mul_fixed::full_width::Config,
|
||||
mul_fixed_full: mul_fixed::full_width::Config<FixedPoints>,
|
||||
/// Fixed-base signed short scalar multiplication
|
||||
mul_fixed_short: mul_fixed::short::Config,
|
||||
mul_fixed_short: mul_fixed::short::Config<FixedPoints>,
|
||||
/// Fixed-base mul using a base field element as a scalar
|
||||
mul_fixed_base_field: mul_fixed::base_field_elem::Config,
|
||||
mul_fixed_base_field: mul_fixed::base_field_elem::Config<FixedPoints>,
|
||||
|
||||
/// Witness point
|
||||
witness_point: witness_point::Config,
|
||||
|
@ -158,14 +160,59 @@ pub struct EccConfig {
|
|||
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
}
|
||||
|
||||
/// A chip implementing EccInstructions
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct EccChip {
|
||||
config: EccConfig,
|
||||
/// A trait representing the kind of scalar used with a particular `FixedPoint`.
|
||||
///
|
||||
/// This trait exists because of limitations around const generics.
|
||||
pub trait ScalarKind {
|
||||
const NUM_WINDOWS: usize;
|
||||
}
|
||||
|
||||
impl Chip<pallas::Base> for EccChip {
|
||||
type Config = EccConfig;
|
||||
/// Type marker representing a full-width scalar for use in fixed-base scalar
|
||||
/// multiplication.
|
||||
pub enum FullScalar {}
|
||||
impl ScalarKind for FullScalar {
|
||||
const NUM_WINDOWS: usize = NUM_WINDOWS;
|
||||
}
|
||||
|
||||
/// Type marker representing a signed 64-bit scalar for use in fixed-base scalar
|
||||
/// multiplication.
|
||||
pub enum ShortScalar {}
|
||||
impl ScalarKind for ShortScalar {
|
||||
const NUM_WINDOWS: usize = NUM_WINDOWS_SHORT;
|
||||
}
|
||||
|
||||
/// Type marker representing a base field element being used as a scalar in fixed-base
|
||||
/// scalar multiplication.
|
||||
pub enum BaseFieldElem {}
|
||||
impl ScalarKind for BaseFieldElem {
|
||||
const NUM_WINDOWS: usize = NUM_WINDOWS;
|
||||
}
|
||||
|
||||
/// Returns information about a fixed point.
|
||||
///
|
||||
/// TODO: When associated consts can be used as const generics, introduce a
|
||||
/// `const NUM_WINDOWS: usize` associated const, and return `NUM_WINDOWS`-sized
|
||||
/// arrays instead of `Vec`s.
|
||||
pub trait FixedPoint<C: CurveAffine>: std::fmt::Debug + Eq + Clone {
|
||||
type ScalarKind: ScalarKind;
|
||||
|
||||
fn generator(&self) -> C;
|
||||
fn u(&self) -> Vec<[[u8; 32]; H]>;
|
||||
fn z(&self) -> Vec<u64>;
|
||||
|
||||
fn lagrange_coeffs(&self) -> Vec<[C::Base; H]> {
|
||||
compute_lagrange_coeffs(self.generator(), Self::ScalarKind::NUM_WINDOWS)
|
||||
}
|
||||
}
|
||||
|
||||
/// A chip implementing EccInstructions
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct EccChip<FixedPoints: super::FixedPoints<pallas::Affine>> {
|
||||
config: EccConfig<FixedPoints>,
|
||||
}
|
||||
|
||||
impl<FixedPoints: super::FixedPoints<pallas::Affine>> Chip<pallas::Base> for EccChip<FixedPoints> {
|
||||
type Config = EccConfig<FixedPoints>;
|
||||
type Loaded = ();
|
||||
|
||||
fn config(&self) -> &Self::Config {
|
||||
|
@ -177,11 +224,13 @@ impl Chip<pallas::Base> for EccChip {
|
|||
}
|
||||
}
|
||||
|
||||
impl UtilitiesInstructions<pallas::Base> for EccChip {
|
||||
impl<Fixed: super::FixedPoints<pallas::Affine>> UtilitiesInstructions<pallas::Base>
|
||||
for EccChip<Fixed>
|
||||
{
|
||||
type Var = AssignedCell<pallas::Base, pallas::Base>;
|
||||
}
|
||||
|
||||
impl EccChip {
|
||||
impl<FixedPoints: super::FixedPoints<pallas::Affine>> EccChip<FixedPoints> {
|
||||
pub fn construct(config: <Self as Chip<pallas::Base>>::Config) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
|
@ -213,7 +262,7 @@ impl EccChip {
|
|||
|
||||
// Create config that is shared across short, base-field, and full-width
|
||||
// fixed-base scalar mul.
|
||||
let mul_fixed = mul_fixed::Config::configure(
|
||||
let mul_fixed = mul_fixed::Config::<FixedPoints>::configure(
|
||||
meta,
|
||||
lagrange_coeffs,
|
||||
advices[4],
|
||||
|
@ -225,13 +274,15 @@ impl EccChip {
|
|||
);
|
||||
|
||||
// Create gate that is only used in full-width fixed-base scalar mul.
|
||||
let mul_fixed_full = mul_fixed::full_width::Config::configure(meta, mul_fixed);
|
||||
let mul_fixed_full =
|
||||
mul_fixed::full_width::Config::<FixedPoints>::configure(meta, mul_fixed.clone());
|
||||
|
||||
// Create gate that is only used in short fixed-base scalar mul.
|
||||
let mul_fixed_short = mul_fixed::short::Config::configure(meta, mul_fixed);
|
||||
let mul_fixed_short =
|
||||
mul_fixed::short::Config::<FixedPoints>::configure(meta, mul_fixed.clone());
|
||||
|
||||
// Create gate that is only used in fixed-base mul using a base field element.
|
||||
let mul_fixed_base_field = mul_fixed::base_field_elem::Config::configure(
|
||||
let mul_fixed_base_field = mul_fixed::base_field_elem::Config::<FixedPoints>::configure(
|
||||
meta,
|
||||
advices[6..9].try_into().unwrap(),
|
||||
range_check,
|
||||
|
@ -260,7 +311,7 @@ impl EccChip {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct EccScalarFixed {
|
||||
value: Option<pallas::Scalar>,
|
||||
windows: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS }>,
|
||||
windows: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS }>,
|
||||
}
|
||||
|
||||
// TODO: Make V a `u64`
|
||||
|
@ -285,8 +336,7 @@ type MagnitudeSign = (MagnitudeCell, SignCell);
|
|||
pub struct EccScalarFixedShort {
|
||||
magnitude: MagnitudeCell,
|
||||
sign: SignCell,
|
||||
running_sum:
|
||||
ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS_SHORT + 1 }>,
|
||||
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS_SHORT + 1 }>,
|
||||
}
|
||||
|
||||
/// A base field element used for fixed-base scalar multiplication.
|
||||
|
@ -301,7 +351,7 @@ pub struct EccScalarFixedShort {
|
|||
#[derive(Clone, Debug)]
|
||||
struct EccBaseFieldElemFixed {
|
||||
base_field_elem: AssignedCell<pallas::Base, pallas::Base>,
|
||||
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS + 1 }>,
|
||||
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS + 1 }>,
|
||||
}
|
||||
|
||||
impl EccBaseFieldElemFixed {
|
||||
|
@ -310,16 +360,22 @@ impl EccBaseFieldElemFixed {
|
|||
}
|
||||
}
|
||||
|
||||
impl EccInstructions<pallas::Affine> for EccChip {
|
||||
impl<Fixed: FixedPoints<pallas::Affine>> EccInstructions<pallas::Affine> for EccChip<Fixed>
|
||||
where
|
||||
<Fixed as FixedPoints<pallas::Affine>>::Base:
|
||||
FixedPoint<pallas::Affine, ScalarKind = BaseFieldElem>,
|
||||
<Fixed as FixedPoints<pallas::Affine>>::FullScalar:
|
||||
FixedPoint<pallas::Affine, ScalarKind = FullScalar>,
|
||||
<Fixed as FixedPoints<pallas::Affine>>::ShortScalar:
|
||||
FixedPoint<pallas::Affine, ScalarKind = ShortScalar>,
|
||||
{
|
||||
type ScalarFixed = EccScalarFixed;
|
||||
type ScalarFixedShort = EccScalarFixedShort;
|
||||
type ScalarVar = AssignedCell<pallas::Base, pallas::Base>;
|
||||
type Point = EccPoint;
|
||||
type NonIdentityPoint = NonIdentityEccPoint;
|
||||
type X = AssignedCell<pallas::Base, pallas::Base>;
|
||||
type FixedPoints = OrchardFixedBasesFull;
|
||||
type FixedPointsBaseField = NullifierK;
|
||||
type FixedPointsShort = ValueCommitV;
|
||||
type FixedPoints = Fixed;
|
||||
|
||||
fn constrain_equal(
|
||||
&self,
|
||||
|
@ -413,13 +469,13 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
|||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
base: &Self::FixedPoints,
|
||||
base: &<Self::FixedPoints as FixedPoints<pallas::Affine>>::FullScalar,
|
||||
) -> Result<(Self::Point, Self::ScalarFixed), Error> {
|
||||
let config = self.config().mul_fixed_full;
|
||||
let config = self.config().mul_fixed_full.clone();
|
||||
config.assign(
|
||||
layouter.namespace(|| format!("fixed-base mul of {:?}", base)),
|
||||
scalar,
|
||||
*base,
|
||||
base,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -427,9 +483,9 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
|||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
magnitude_sign: MagnitudeSign,
|
||||
base: &Self::FixedPointsShort,
|
||||
base: &<Self::FixedPoints as FixedPoints<pallas::Affine>>::ShortScalar,
|
||||
) -> Result<(Self::Point, Self::ScalarFixedShort), Error> {
|
||||
let config: mul_fixed::short::Config = self.config().mul_fixed_short;
|
||||
let config = self.config().mul_fixed_short.clone();
|
||||
config.assign(
|
||||
layouter.namespace(|| format!("short fixed-base mul of {:?}", base)),
|
||||
magnitude_sign,
|
||||
|
@ -441,13 +497,13 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
|||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
base_field_elem: AssignedCell<pallas::Base, pallas::Base>,
|
||||
base: &Self::FixedPointsBaseField,
|
||||
base: &<Self::FixedPoints as FixedPoints<pallas::Affine>>::Base,
|
||||
) -> Result<Self::Point, Error> {
|
||||
let config = self.config().mul_fixed_base_field;
|
||||
let config = self.config().mul_fixed_base_field.clone();
|
||||
config.assign(
|
||||
layouter.namespace(|| format!("base-field elem fixed-base mul of {:?}", base)),
|
||||
base_field_elem,
|
||||
*base,
|
||||
base,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
use arrayvec::ArrayVec;
|
||||
use group::{
|
||||
ff::{Field, PrimeField},
|
||||
Curve,
|
||||
};
|
||||
use halo2::arithmetic::lagrange_interpolate;
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pallas,
|
||||
};
|
||||
|
||||
/// Window size for fixed-base scalar multiplication
|
||||
pub const FIXED_BASE_WINDOW_SIZE: usize = 3;
|
||||
|
||||
/// $2^{`FIXED_BASE_WINDOW_SIZE`}$
|
||||
pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of windows for a full-width scalar
|
||||
pub const NUM_WINDOWS: usize =
|
||||
(pallas::Scalar::NUM_BITS as usize + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of windows for a short signed scalar
|
||||
pub const NUM_WINDOWS_SHORT: usize =
|
||||
(L_SCALAR_SHORT + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// $\ell_\mathsf{value}$
|
||||
/// Number of bits in an unsigned short scalar.
|
||||
pub(crate) const L_SCALAR_SHORT: usize = 64;
|
||||
|
||||
/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
|
||||
/// <https://github.com/zcash/pasta>
|
||||
pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
|
||||
|
||||
/// The Pallas base field modulus is $p = 2^{254} + \mathsf{t_p}$.
|
||||
/// <https://github.com/zcash/pasta>
|
||||
pub(crate) const T_P: u128 = 45560315531419706090280762371685220353;
|
||||
|
||||
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
|
||||
/// Each window will have $2^3 = 8$ points. The tables are computed as described in
|
||||
/// [the Orchard book](https://zcash.github.io/orchard/design/circuit/gadgets/ecc/fixed-base-scalar-mul.html#load-fixed-base).
|
||||
fn compute_window_table<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
|
||||
let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows);
|
||||
|
||||
// Generate window table entries for all windows but the last.
|
||||
// For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B.
|
||||
// Here, w ranges from [0..`num_windows - 1`)
|
||||
for w in 0..(num_windows - 1) {
|
||||
window_table.push(
|
||||
(0..H)
|
||||
.map(|k| {
|
||||
// scalar = (k+2)*(8^w)
|
||||
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::<ArrayVec<C, H>>()
|
||||
.into_inner()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
// Generate window table entries for the last window, w = `num_windows - 1`.
|
||||
// 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::Scalar::zero(), |acc, j| {
|
||||
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::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()
|
||||
})
|
||||
.collect::<ArrayVec<C, H>>()
|
||||
.into_inner()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
window_table
|
||||
}
|
||||
|
||||
/// For each window, we interpolate the $x$-coordinate.
|
||||
/// Here, we pre-compute and store the coefficients of the interpolation polynomial.
|
||||
pub fn compute_lagrange_coeffs<C: CurveAffine>(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(i as u64)).collect();
|
||||
|
||||
let window_table = compute_window_table(base, num_windows);
|
||||
|
||||
window_table
|
||||
.iter()
|
||||
.map(|window_points| {
|
||||
let x_window_points: Vec<_> = window_points
|
||||
.iter()
|
||||
.map(|point| *point.coordinates().unwrap().x())
|
||||
.collect();
|
||||
lagrange_interpolate(&points, &x_window_points)
|
||||
.into_iter()
|
||||
.collect::<ArrayVec<C::Base, H>>()
|
||||
.into_inner()
|
||||
.unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// For each window, $z$ is a field element such that for each point $(x, y)$ in the window:
|
||||
/// - $z + y = u^2$ (some square in the field); and
|
||||
/// - $z - y$ is not a square.
|
||||
/// If successful, return a vector of `(z: u64, us: [C::Base; H])` for each window.
|
||||
///
|
||||
/// This function was used to generate the `z`s and `u`s for the Orchard fixed
|
||||
/// bases. The outputs of this function have been stored as constants, and it
|
||||
/// is not called anywhere in this codebase. However, we keep this function here
|
||||
/// as a utility for those who wish to use it with different parameters.
|
||||
pub fn find_zs_and_us<C: CurveAffine>(
|
||||
base: C,
|
||||
num_windows: usize,
|
||||
) -> Option<Vec<(u64, [C::Base; H])>> {
|
||||
// Closure to find z and u's for one window
|
||||
let find_z_and_us = |window_points: &[C]| {
|
||||
assert_eq!(H, window_points.len());
|
||||
|
||||
let ys: Vec<_> = window_points
|
||||
.iter()
|
||||
.map(|point| *point.coordinates().unwrap().y())
|
||||
.collect();
|
||||
(0..(1000 * (1 << (2 * H)))).find_map(|z| {
|
||||
ys.iter()
|
||||
.map(|&y| {
|
||||
if (-y + C::Base::from(z)).sqrt().is_none().into() {
|
||||
(y + C::Base::from(z)).sqrt().into()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Option<ArrayVec<C::Base, H>>>()
|
||||
.map(|us| (z, us.into_inner().unwrap()))
|
||||
})
|
||||
};
|
||||
|
||||
let window_table = compute_window_table(base, num_windows);
|
||||
window_table
|
||||
.iter()
|
||||
.map(|window_points| find_z_and_us(window_points))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Test that the z-values and u-values satisfy the conditions:
|
||||
// 1. z + y = u^2,
|
||||
// 2. z - y is not a square
|
||||
// for the y-coordinate of each fixed-base multiple in each window.
|
||||
pub fn test_zs_and_us<C: CurveAffine>(base: C, 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate
|
||||
// for each fixed-base multiple in each window.
|
||||
pub fn test_lagrange_coeffs<C: CurveAffine>(base: C, num_windows: usize) {
|
||||
/// Evaluate y = f(x) given the coefficients of f(x)
|
||||
fn evaluate<C: CurveAffine>(x: u8, coeffs: &[C::Base]) -> C::Base {
|
||||
let x = C::Base::from(x as u64);
|
||||
coeffs
|
||||
.iter()
|
||||
.rev()
|
||||
.cloned()
|
||||
.reduce(|acc, coeff| acc * x + coeff)
|
||||
.unwrap_or_else(C::Base::zero)
|
||||
}
|
||||
|
||||
let lagrange_coeffs = compute_lagrange_coeffs(base, num_windows);
|
||||
|
||||
// Check first 84 windows, i.e. `k_0, k_1, ..., k_83`
|
||||
for (idx, coeffs) in lagrange_coeffs[0..(num_windows - 1)].iter().enumerate() {
|
||||
// Test each three-bit chunk in this window.
|
||||
for bits in 0..(H as u8) {
|
||||
{
|
||||
// Interpolate the x-coordinate using this window's coefficients
|
||||
let interpolated_x = evaluate::<C>(bits, coeffs);
|
||||
|
||||
// Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B.
|
||||
let point = base
|
||||
* 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.
|
||||
assert_eq!(x, interpolated_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check last window.
|
||||
for bits in 0..(H as u8) {
|
||||
// Interpolate the x-coordinate using the last window's coefficients
|
||||
let interpolated_x = evaluate::<C>(bits, &lagrange_coeffs[num_windows - 1]);
|
||||
|
||||
// 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(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 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();
|
||||
|
||||
// Check that the interpolated x-coordinate matches the actual one.
|
||||
assert_eq!(x, interpolated_x);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::{ff::Field, Curve, Group};
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pallas,
|
||||
};
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::{compute_window_table, find_zs_and_us, test_lagrange_coeffs, H, NUM_WINDOWS};
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary Pallas point.
|
||||
pub fn arb_point()(bytes in prop::array::uniform32(0u8..)) -> pallas::Point {
|
||||
// Instead of rejecting out-of-range bytes, let's reduce them.
|
||||
let mut buf = [0; 64];
|
||||
buf[..32].copy_from_slice(&bytes);
|
||||
let scalar = pallas::Scalar::from_bytes_wide(&buf);
|
||||
pallas::Point::generator() * scalar
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn lagrange_coeffs(
|
||||
base in arb_point(),
|
||||
) {
|
||||
test_lagrange_coeffs(base.to_affine(), NUM_WINDOWS);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zs_and_us() {
|
||||
let base = pallas::Point::random(rand::rngs::OsRng);
|
||||
let (z, u): (Vec<u64>, Vec<[pallas::Base; H]>) =
|
||||
find_zs_and_us(base.to_affine(), NUM_WINDOWS)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.unzip();
|
||||
let window_table = compute_window_table(base.to_affine(), 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();
|
||||
assert_eq!(pallas::Base::from(*z) + y, u * u); // allow either square root
|
||||
assert!(bool::from((pallas::Base::from(*z) - y).sqrt().is_none()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
use super::{add, EccPoint, NonIdentityEccPoint};
|
||||
use super::{add, EccPoint, NonIdentityEccPoint, T_Q};
|
||||
use crate::{
|
||||
circuit::gadget::utilities::{bool_check, lookup_range_check::LookupRangeCheckConfig, ternary},
|
||||
constants::T_Q,
|
||||
primitives::sinsemilla,
|
||||
};
|
||||
use std::{
|
||||
|
@ -481,11 +480,12 @@ pub mod tests {
|
|||
},
|
||||
utilities::UtilitiesInstructions,
|
||||
};
|
||||
use crate::constants::OrchardFixedBases;
|
||||
|
||||
pub fn test_mul(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
p: &NonIdentityPoint<pallas::Affine, EccChip>,
|
||||
p: &NonIdentityPoint<pallas::Affine, EccChip<OrchardFixedBases>>,
|
||||
p_val: pallas::Affine,
|
||||
) -> Result<(), Error> {
|
||||
let column = chip.config().advices[0];
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use super::Z;
|
||||
use super::{T_Q, Z};
|
||||
use crate::{
|
||||
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q,
|
||||
primitives::sinsemilla,
|
||||
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, primitives::sinsemilla,
|
||||
};
|
||||
use halo2::circuit::AssignedCell;
|
||||
use halo2::{
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use super::{
|
||||
add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort,
|
||||
NonIdentityEccPoint,
|
||||
add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort, FixedPoint,
|
||||
NonIdentityEccPoint, FIXED_BASE_WINDOW_SIZE, H,
|
||||
};
|
||||
use crate::circuit::gadget::utilities::decompose_running_sum::RunningSumConfig;
|
||||
use crate::constants::{
|
||||
self,
|
||||
load::{NullifierK, OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV, WindowUs},
|
||||
};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use group::{ff::PrimeField, Curve};
|
||||
use halo2::{
|
||||
|
@ -27,60 +25,15 @@ pub mod short;
|
|||
lazy_static! {
|
||||
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(constants::H as u64);
|
||||
static ref H_BASE: pallas::Base = pallas::Base::from(constants::H as u64);
|
||||
static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from(H as u64);
|
||||
static ref H_BASE: pallas::Base = pallas::Base::from(H as u64);
|
||||
}
|
||||
|
||||
// A sum type for both full-width and short bases. This enables us to use the
|
||||
// shared functionality of full-width and short fixed-base scalar multiplication.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum OrchardFixedBases {
|
||||
Full(OrchardFixedBasesFull),
|
||||
NullifierK,
|
||||
ValueCommitV,
|
||||
}
|
||||
|
||||
impl From<OrchardFixedBasesFull> for OrchardFixedBases {
|
||||
fn from(full_width_base: OrchardFixedBasesFull) -> Self {
|
||||
Self::Full(full_width_base)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValueCommitV> for OrchardFixedBases {
|
||||
fn from(_value_commit_v: ValueCommitV) -> Self {
|
||||
Self::ValueCommitV
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NullifierK> for OrchardFixedBases {
|
||||
fn from(_nullifier_k: NullifierK) -> Self {
|
||||
Self::NullifierK
|
||||
}
|
||||
}
|
||||
|
||||
impl OrchardFixedBases {
|
||||
pub fn generator(self) -> pallas::Affine {
|
||||
match self {
|
||||
Self::ValueCommitV => constants::value_commit_v::generator(),
|
||||
Self::NullifierK => constants::nullifier_k::generator(),
|
||||
Self::Full(base) => base.generator(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn u(self) -> Vec<WindowUs> {
|
||||
match self {
|
||||
Self::ValueCommitV => ValueCommitV::get().u_short.0.as_ref().to_vec(),
|
||||
Self::NullifierK => NullifierK.u().0.as_ref().to_vec(),
|
||||
Self::Full(base) => base.u().0.as_ref().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct Config {
|
||||
running_sum_config: RunningSumConfig<pallas::Base, { constants::FIXED_BASE_WINDOW_SIZE }>,
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Config<FixedPoints: super::FixedPoints<pallas::Affine>> {
|
||||
running_sum_config: RunningSumConfig<pallas::Base, FIXED_BASE_WINDOW_SIZE>,
|
||||
// The fixed Lagrange interpolation coefficients for `x_p`.
|
||||
lagrange_coeffs: [Column<Fixed>; constants::H],
|
||||
lagrange_coeffs: [Column<Fixed>; H],
|
||||
// The fixed `z` for each window such that `y + z = u^2`.
|
||||
fixed_z: Column<Fixed>,
|
||||
// Decomposition of an `n-1`-bit scalar into `k`-bit windows:
|
||||
|
@ -96,13 +49,14 @@ pub struct Config {
|
|||
add_config: add::Config,
|
||||
// Configuration for `add_incomplete`
|
||||
add_incomplete_config: add_incomplete::Config,
|
||||
_marker: PhantomData<FixedPoints>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
lagrange_coeffs: [Column<Fixed>; constants::H],
|
||||
lagrange_coeffs: [Column<Fixed>; H],
|
||||
window: Column<Advice>,
|
||||
x_p: Column<Advice>,
|
||||
y_p: Column<Advice>,
|
||||
|
@ -126,6 +80,7 @@ impl Config {
|
|||
u,
|
||||
add_config,
|
||||
add_incomplete_config,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
// Check relationships between this config and `add_config`.
|
||||
|
@ -182,7 +137,7 @@ impl Config {
|
|||
|
||||
// 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(constants::H as u64);
|
||||
let word = z_cur - z_next * pallas::Base::from(H as u64);
|
||||
|
||||
self.coords_check(meta, q_mul_fixed_running_sum, word)
|
||||
});
|
||||
|
@ -200,7 +155,7 @@ impl Config {
|
|||
let z = meta.query_fixed(self.fixed_z, Rotation::cur());
|
||||
let u = meta.query_advice(self.u, Rotation::cur());
|
||||
|
||||
let window_pow: Vec<Expression<pallas::Base>> = (0..constants::H)
|
||||
let window_pow: Vec<Expression<pallas::Base>> = (0..H)
|
||||
.map(|pow| {
|
||||
(0..pow).fold(Expression::Constant(pallas::Base::one()), |acc, _| {
|
||||
acc * window.clone()
|
||||
|
@ -231,63 +186,45 @@ impl Config {
|
|||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn assign_region_inner<const NUM_WINDOWS: usize>(
|
||||
fn assign_region_inner<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
scalar: &ScalarFixed,
|
||||
base: OrchardFixedBases,
|
||||
base: &F,
|
||||
coords_check_toggle: Selector,
|
||||
) -> Result<(NonIdentityEccPoint, NonIdentityEccPoint), Error> {
|
||||
// Assign fixed columns for given fixed base
|
||||
self.assign_fixed_constants::<NUM_WINDOWS>(region, offset, base, coords_check_toggle)?;
|
||||
self.assign_fixed_constants::<F, NUM_WINDOWS>(region, offset, base, coords_check_toggle)?;
|
||||
|
||||
// Initialize accumulator
|
||||
let acc = self.initialize_accumulator(region, offset, base, scalar)?;
|
||||
let acc = self.initialize_accumulator::<F, NUM_WINDOWS>(region, offset, base, scalar)?;
|
||||
|
||||
// Process all windows excluding least and most significant windows
|
||||
let acc = self.add_incomplete(region, offset, acc, base, scalar)?;
|
||||
let acc = self.add_incomplete::<F, NUM_WINDOWS>(region, offset, acc, base, scalar)?;
|
||||
|
||||
// Process most significant window using complete addition
|
||||
let mul_b = self.process_msb::<NUM_WINDOWS>(region, offset, base, scalar)?;
|
||||
let mul_b = self.process_msb::<F, NUM_WINDOWS>(region, offset, base, scalar)?;
|
||||
|
||||
Ok((acc, mul_b))
|
||||
}
|
||||
|
||||
fn assign_fixed_constants<const NUM_WINDOWS: usize>(
|
||||
fn assign_fixed_constants<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
base: OrchardFixedBases,
|
||||
base: &F,
|
||||
coords_check_toggle: Selector,
|
||||
) -> Result<(), Error> {
|
||||
let mut constants = None;
|
||||
let build_constants = || {
|
||||
let lagrange_coeffs = base.lagrange_coeffs();
|
||||
assert_eq!(lagrange_coeffs.len(), NUM_WINDOWS);
|
||||
|
||||
let build_constants = || match base {
|
||||
OrchardFixedBases::ValueCommitV => {
|
||||
assert_eq!(NUM_WINDOWS, constants::NUM_WINDOWS_SHORT);
|
||||
let base = ValueCommitV::get();
|
||||
(
|
||||
base.lagrange_coeffs_short.0.as_ref().to_vec(),
|
||||
base.z_short.0.as_ref().to_vec(),
|
||||
)
|
||||
}
|
||||
OrchardFixedBases::Full(base) => {
|
||||
assert_eq!(NUM_WINDOWS, constants::NUM_WINDOWS);
|
||||
let base: OrchardFixedBase = base.into();
|
||||
(
|
||||
base.lagrange_coeffs.0.as_ref().to_vec(),
|
||||
base.z.0.as_ref().to_vec(),
|
||||
)
|
||||
}
|
||||
OrchardFixedBases::NullifierK => {
|
||||
assert_eq!(NUM_WINDOWS, constants::NUM_WINDOWS);
|
||||
let base: OrchardFixedBase = NullifierK.into();
|
||||
(
|
||||
base.lagrange_coeffs.0.as_ref().to_vec(),
|
||||
base.z.0.as_ref().to_vec(),
|
||||
)
|
||||
}
|
||||
let z = base.z();
|
||||
assert_eq!(z.len(), NUM_WINDOWS);
|
||||
|
||||
(lagrange_coeffs, z)
|
||||
};
|
||||
|
||||
// Assign fixed columns for given fixed base
|
||||
|
@ -295,7 +232,7 @@ impl Config {
|
|||
coords_check_toggle.enable(region, window + offset)?;
|
||||
|
||||
// Assign x-coordinate Lagrange interpolation coefficients
|
||||
for k in 0..(constants::H) {
|
||||
for k in 0..H {
|
||||
region.assign_fixed(
|
||||
|| {
|
||||
format!(
|
||||
|
@ -310,7 +247,7 @@ impl Config {
|
|||
constants = Some(build_constants());
|
||||
}
|
||||
let lagrange_coeffs = &constants.as_ref().unwrap().0;
|
||||
Ok(lagrange_coeffs[window].0[k])
|
||||
Ok(lagrange_coeffs[window][k])
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
@ -322,7 +259,7 @@ impl Config {
|
|||
window + offset,
|
||||
|| {
|
||||
let z = &constants.as_ref().unwrap().1;
|
||||
Ok(z[window])
|
||||
Ok(pallas::Base::from(z[window]))
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
@ -330,17 +267,18 @@ impl Config {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn process_window(
|
||||
fn process_window<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
w: usize,
|
||||
k: Option<pallas::Scalar>,
|
||||
k_usize: Option<usize>,
|
||||
base: OrchardFixedBases,
|
||||
base: &F,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
let base_value = base.generator();
|
||||
let base_u = base.u();
|
||||
assert_eq!(base_u.len(), NUM_WINDOWS);
|
||||
|
||||
// Compute [(k_w + 2) ⋅ 8^w]B
|
||||
let mul_b = {
|
||||
|
@ -376,17 +314,17 @@ impl Config {
|
|||
};
|
||||
|
||||
// Assign u = (y_p + z_w).sqrt()
|
||||
let u_val = k_usize.map(|k| base_u[w].0[k]);
|
||||
let u_val = k_usize.map(|k| pallas::Base::from_bytes(&base_u[w][k]).unwrap());
|
||||
region.assign_advice(|| "u", self.u, offset + w, || u_val.ok_or(Error::Synthesis))?;
|
||||
|
||||
Ok(mul_b)
|
||||
}
|
||||
|
||||
fn initialize_accumulator(
|
||||
fn initialize_accumulator<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
base: OrchardFixedBases,
|
||||
base: &F,
|
||||
scalar: &ScalarFixed,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
// Recall that the message at each window `w` is represented as
|
||||
|
@ -395,15 +333,15 @@ impl Config {
|
|||
let w = 0;
|
||||
let k0 = scalar.windows_field()[0];
|
||||
let k0_usize = scalar.windows_usize()[0];
|
||||
self.process_window(region, offset, w, k0, k0_usize, base)
|
||||
self.process_window::<_, NUM_WINDOWS>(region, offset, w, k0, k0_usize, base)
|
||||
}
|
||||
|
||||
fn add_incomplete(
|
||||
fn add_incomplete<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
mut acc: NonIdentityEccPoint,
|
||||
base: OrchardFixedBases,
|
||||
base: &F,
|
||||
scalar: &ScalarFixed,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
let scalar_windows_field = scalar.windows_field();
|
||||
|
@ -417,7 +355,8 @@ impl Config {
|
|||
.skip(1)
|
||||
{
|
||||
// Compute [(k_w + 2) ⋅ 8^w]B
|
||||
let mul_b = self.process_window(region, offset, w, *k, *k_usize, base)?;
|
||||
let mul_b =
|
||||
self.process_window::<_, NUM_WINDOWS>(region, offset, w, *k, *k_usize, base)?;
|
||||
|
||||
// Add to the accumulator
|
||||
acc = self
|
||||
|
@ -427,17 +366,17 @@ impl Config {
|
|||
Ok(acc)
|
||||
}
|
||||
|
||||
fn process_msb<const NUM_WINDOWS: usize>(
|
||||
fn process_msb<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
base: OrchardFixedBases,
|
||||
base: &F,
|
||||
scalar: &ScalarFixed,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
// Assign u = (y_p + z_w).sqrt() for the most significant window
|
||||
{
|
||||
let u_val =
|
||||
scalar.windows_usize()[NUM_WINDOWS - 1].map(|k| base.u()[NUM_WINDOWS - 1].0[k]);
|
||||
let u_val = scalar.windows_usize()[NUM_WINDOWS - 1]
|
||||
.map(|k| pallas::Base::from_bytes(&base.u()[NUM_WINDOWS - 1][k]).unwrap());
|
||||
region.assign_advice(
|
||||
|| "u",
|
||||
self.u,
|
||||
|
@ -448,12 +387,7 @@ impl Config {
|
|||
|
||||
// offset_acc = \sum_{j = 0}^{NUM_WINDOWS - 2} 2^{FIXED_BASE_WINDOW_SIZE*j + 1}
|
||||
let offset_acc = (0..(NUM_WINDOWS - 1)).fold(pallas::Scalar::zero(), |acc, w| {
|
||||
acc + (*TWO_SCALAR).pow(&[
|
||||
constants::FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
])
|
||||
acc + (*TWO_SCALAR).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 0, 0, 0])
|
||||
});
|
||||
|
||||
// `scalar = [k * 8^84 - offset_acc]`, where `offset_acc = \sum_{j = 0}^{83} 2^{FIXED_BASE_WINDOW_SIZE*j + 1}`.
|
||||
|
@ -558,7 +492,7 @@ impl ScalarFixed {
|
|||
.map(|window| {
|
||||
if let Some(window) = window {
|
||||
let window = window.get_lower_32() as usize;
|
||||
assert!(window < constants::H);
|
||||
assert!(window < H);
|
||||
Some(window)
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use super::super::{EccBaseFieldElemFixed, EccPoint, NullifierK};
|
||||
use super::super::{EccBaseFieldElemFixed, EccPoint, FixedPoints, NUM_WINDOWS, T_P};
|
||||
use super::H_BASE;
|
||||
|
||||
use crate::{
|
||||
circuit::gadget::utilities::{
|
||||
bitrange_subset, lookup_range_check::LookupRangeCheckConfig, range_check,
|
||||
},
|
||||
constants::{self, T_P},
|
||||
primitives::sinsemilla,
|
||||
};
|
||||
use halo2::circuit::AssignedCell;
|
||||
|
||||
use group::ff::PrimeField;
|
||||
use halo2::{
|
||||
circuit::Layouter,
|
||||
circuit::{AssignedCell, Layouter},
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
@ -18,20 +18,20 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
|
|||
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Config {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
|
||||
q_mul_fixed_base_field: Selector,
|
||||
canon_advices: [Column<Advice>; 3],
|
||||
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
super_config: super::Config,
|
||||
super_config: super::Config<Fixed>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
pub(crate) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
canon_advices: [Column<Advice>; 3],
|
||||
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
super_config: super::Config,
|
||||
super_config: super::Config<Fixed>,
|
||||
) -> Self {
|
||||
for advice in canon_advices.iter() {
|
||||
meta.enable_equality(*advice);
|
||||
|
@ -161,8 +161,11 @@ impl Config {
|
|||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
scalar: AssignedCell<pallas::Base, pallas::Base>,
|
||||
base: NullifierK,
|
||||
) -> Result<EccPoint, Error> {
|
||||
base: &<Fixed as FixedPoints<pallas::Affine>>::Base,
|
||||
) -> Result<EccPoint, Error>
|
||||
where
|
||||
<Fixed as FixedPoints<pallas::Affine>>::Base: super::super::FixedPoint<pallas::Affine>,
|
||||
{
|
||||
let (scalar, acc, mul_b) = layouter.assign_region(
|
||||
|| "Base-field elem fixed-base mul (incomplete addition)",
|
||||
|mut region| {
|
||||
|
@ -175,8 +178,8 @@ impl Config {
|
|||
offset,
|
||||
scalar.clone(),
|
||||
true,
|
||||
constants::L_ORCHARD_BASE,
|
||||
constants::NUM_WINDOWS,
|
||||
pallas::Base::NUM_BITS as usize,
|
||||
NUM_WINDOWS,
|
||||
)?;
|
||||
EccBaseFieldElemFixed {
|
||||
base_field_elem: running_sum[0].clone(),
|
||||
|
@ -184,15 +187,13 @@ impl Config {
|
|||
}
|
||||
};
|
||||
|
||||
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,
|
||||
)?;
|
||||
let (acc, mul_b) = self.super_config.assign_region_inner::<_, NUM_WINDOWS>(
|
||||
&mut region,
|
||||
offset,
|
||||
&(&scalar).into(),
|
||||
base,
|
||||
self.super_config.running_sum_config.q_range_check,
|
||||
)?;
|
||||
|
||||
Ok((scalar, acc, mul_b))
|
||||
},
|
||||
|
@ -214,9 +215,9 @@ impl Config {
|
|||
#[cfg(test)]
|
||||
// Check that the correct multiple is obtained.
|
||||
{
|
||||
use group::{ff::PrimeField, Curve};
|
||||
use super::super::FixedPoint;
|
||||
use group::Curve;
|
||||
|
||||
let base: super::OrchardFixedBases = base.into();
|
||||
let scalar = &scalar
|
||||
.base_field_elem()
|
||||
.value()
|
||||
|
@ -387,32 +388,31 @@ pub mod tests {
|
|||
|
||||
use crate::circuit::gadget::{
|
||||
ecc::{
|
||||
chip::{EccChip, NullifierK},
|
||||
chip::{EccChip, FixedPoint, H},
|
||||
FixedPointBaseField, NonIdentityPoint, Point,
|
||||
},
|
||||
utilities::UtilitiesInstructions,
|
||||
};
|
||||
use crate::constants;
|
||||
use crate::constants::{NullifierK, OrchardFixedBases};
|
||||
|
||||
pub fn test_mul_fixed_base_field(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
) -> Result<(), Error> {
|
||||
// nullifier_k
|
||||
let nullifier_k = NullifierK;
|
||||
test_single_base(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "nullifier_k"),
|
||||
FixedPointBaseField::from_inner(chip, nullifier_k),
|
||||
nullifier_k.generator(),
|
||||
FixedPointBaseField::from_inner(chip, NullifierK),
|
||||
NullifierK.generator(),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::op_ref)]
|
||||
fn test_single_base(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
base: FixedPointBaseField<pallas::Affine, EccChip>,
|
||||
base: FixedPointBaseField<pallas::Affine, EccChip<OrchardFixedBases>>,
|
||||
base_val: pallas::Affine,
|
||||
) -> Result<(), Error> {
|
||||
let rng = OsRng;
|
||||
|
@ -420,11 +420,11 @@ pub mod tests {
|
|||
let column = chip.config().advices[0];
|
||||
|
||||
fn constrain_equal_non_id(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
base_val: pallas::Affine,
|
||||
scalar_val: pallas::Base,
|
||||
result: Point<pallas::Affine, EccChip>,
|
||||
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>,
|
||||
) -> Result<(), Error> {
|
||||
// Move scalar from base field into scalar field (which always fits for Pallas).
|
||||
let scalar = pallas::Scalar::from_repr(scalar_val.to_repr()).unwrap();
|
||||
|
@ -461,7 +461,7 @@ pub mod tests {
|
|||
// (There is another *non-canonical* sequence
|
||||
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
|
||||
{
|
||||
let h = pallas::Base::from(constants::H as u64);
|
||||
let h = pallas::Base::from(H as u64);
|
||||
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
|
||||
.chars()
|
||||
.fold(pallas::Base::zero(), |acc, c| {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use super::super::{EccPoint, EccScalarFixed, OrchardFixedBasesFull};
|
||||
use super::super::{EccPoint, EccScalarFixed, FixedPoints, FIXED_BASE_WINDOW_SIZE, H, NUM_WINDOWS};
|
||||
|
||||
use crate::{
|
||||
circuit::gadget::utilities::range_check,
|
||||
constants::{self, util, L_ORCHARD_SCALAR, NUM_WINDOWS},
|
||||
};
|
||||
use crate::circuit::gadget::utilities::{decompose_word, range_check};
|
||||
use arrayvec::ArrayVec;
|
||||
use ff::PrimeField;
|
||||
use halo2::{
|
||||
circuit::{AssignedCell, Layouter, Region},
|
||||
plonk::{ConstraintSystem, Error, Selector},
|
||||
|
@ -12,16 +10,16 @@ use halo2::{
|
|||
};
|
||||
use pasta_curves::pallas;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct Config {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
|
||||
q_mul_fixed_full: Selector,
|
||||
super_config: super::Config,
|
||||
super_config: super::Config<Fixed>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
pub(crate) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
super_config: super::Config,
|
||||
super_config: super::Config<Fixed>,
|
||||
) -> Self {
|
||||
let config = Self {
|
||||
q_mul_fixed_full: meta.selector(),
|
||||
|
@ -46,7 +44,7 @@ impl Config {
|
|||
// 1 * (window - 0) * (window - 1) * ... * (window - 7)
|
||||
.chain(Some((
|
||||
"window range check",
|
||||
q_mul_fixed_full * range_check(window, constants::H),
|
||||
q_mul_fixed_full * range_check(window, H),
|
||||
)))
|
||||
});
|
||||
}
|
||||
|
@ -60,7 +58,9 @@ impl Config {
|
|||
offset: usize,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
) -> Result<EccScalarFixed, Error> {
|
||||
let windows = self.decompose_scalar_fixed::<L_ORCHARD_SCALAR>(scalar, offset, region)?;
|
||||
let windows = self.decompose_scalar_fixed::<{ pallas::Scalar::NUM_BITS as usize }>(
|
||||
scalar, offset, region,
|
||||
)?;
|
||||
|
||||
Ok(EccScalarFixed {
|
||||
value: scalar,
|
||||
|
@ -84,11 +84,7 @@ impl Config {
|
|||
|
||||
// Decompose scalar into `k-bit` windows
|
||||
let scalar_windows: Option<Vec<u8>> = scalar.map(|scalar| {
|
||||
util::decompose_word::<pallas::Scalar>(
|
||||
&scalar,
|
||||
SCALAR_NUM_BITS,
|
||||
constants::FIXED_BASE_WINDOW_SIZE,
|
||||
)
|
||||
decompose_word::<pallas::Scalar>(&scalar, SCALAR_NUM_BITS, FIXED_BASE_WINDOW_SIZE)
|
||||
});
|
||||
|
||||
// Store the scalar decomposition
|
||||
|
@ -122,8 +118,12 @@ impl Config {
|
|||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
base: OrchardFixedBasesFull,
|
||||
) -> Result<(EccPoint, EccScalarFixed), Error> {
|
||||
base: &<Fixed as FixedPoints<pallas::Affine>>::FullScalar,
|
||||
) -> Result<(EccPoint, EccScalarFixed), Error>
|
||||
where
|
||||
<Fixed as FixedPoints<pallas::Affine>>::FullScalar:
|
||||
super::super::FixedPoint<pallas::Affine>,
|
||||
{
|
||||
let (scalar, acc, mul_b) = layouter.assign_region(
|
||||
|| "Full-width fixed-base mul (incomplete addition)",
|
||||
|mut region| {
|
||||
|
@ -131,15 +131,13 @@ impl Config {
|
|||
|
||||
let scalar = self.witness(&mut region, offset, scalar)?;
|
||||
|
||||
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,
|
||||
)?;
|
||||
let (acc, mul_b) = self.super_config.assign_region_inner::<_, NUM_WINDOWS>(
|
||||
&mut region,
|
||||
offset,
|
||||
&(&scalar).into(),
|
||||
base,
|
||||
self.q_mul_fixed_full,
|
||||
)?;
|
||||
|
||||
Ok((scalar, acc, mul_b))
|
||||
},
|
||||
|
@ -161,9 +159,9 @@ impl Config {
|
|||
#[cfg(test)]
|
||||
// Check that the correct multiple is obtained.
|
||||
{
|
||||
use super::super::FixedPoint;
|
||||
use group::Curve;
|
||||
|
||||
let base: super::OrchardFixedBases = base.into();
|
||||
let real_mul = scalar.value.map(|scalar| base.generator() * scalar);
|
||||
let result = result.point();
|
||||
|
||||
|
@ -184,13 +182,13 @@ pub mod tests {
|
|||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::circuit::gadget::ecc::{
|
||||
chip::{EccChip, OrchardFixedBasesFull},
|
||||
chip::{EccChip, FixedPoint as _, H},
|
||||
FixedPoint, NonIdentityPoint, Point,
|
||||
};
|
||||
use crate::constants;
|
||||
use crate::constants::{OrchardFixedBases, OrchardFixedBasesFull};
|
||||
|
||||
pub fn test_mul_fixed(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
) -> Result<(), Error> {
|
||||
// commit_ivk_r
|
||||
|
@ -234,17 +232,17 @@ pub mod tests {
|
|||
|
||||
#[allow(clippy::op_ref)]
|
||||
fn test_single_base(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
base: FixedPoint<pallas::Affine, EccChip>,
|
||||
base: FixedPoint<pallas::Affine, EccChip<OrchardFixedBases>>,
|
||||
base_val: pallas::Affine,
|
||||
) -> Result<(), Error> {
|
||||
fn constrain_equal_non_id(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
base_val: pallas::Affine,
|
||||
scalar_val: pallas::Scalar,
|
||||
result: Point<pallas::Affine, EccChip>,
|
||||
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>,
|
||||
) -> Result<(), Error> {
|
||||
let expected = NonIdentityPoint::new(
|
||||
chip,
|
||||
|
@ -273,7 +271,7 @@ pub mod tests {
|
|||
// (There is another *non-canonical* sequence
|
||||
// 5333333333333333333333333333333333333333332711161673731021062440252244051273333333333 in octal.)
|
||||
{
|
||||
let h = pallas::Scalar::from(constants::H as u64);
|
||||
let h = pallas::Scalar::from(H as u64);
|
||||
let scalar_fixed = "1333333333333333333333333333333333333333333333333333333333333333333333333333333333334"
|
||||
.chars()
|
||||
.fold(pallas::Scalar::zero(), |acc, c| {
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use std::{array, convert::TryInto};
|
||||
|
||||
use super::super::{EccPoint, EccScalarFixedShort};
|
||||
use crate::{
|
||||
circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check},
|
||||
constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT},
|
||||
};
|
||||
use super::super::{EccPoint, EccScalarFixedShort, FixedPoints, L_SCALAR_SHORT, NUM_WINDOWS_SHORT};
|
||||
use crate::circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check};
|
||||
|
||||
use halo2::{
|
||||
circuit::{Layouter, Region},
|
||||
|
@ -13,17 +10,17 @@ use halo2::{
|
|||
};
|
||||
use pasta_curves::pallas;
|
||||
|
||||
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
|
||||
pub struct Config {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Config<Fixed: FixedPoints<pallas::Affine>> {
|
||||
// Selector used for fixed-base scalar mul with short signed exponent.
|
||||
q_mul_fixed_short: Selector,
|
||||
super_config: super::Config,
|
||||
super_config: super::Config<Fixed>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
pub(crate) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
super_config: super::Config,
|
||||
super_config: super::Config<Fixed>,
|
||||
) -> Self {
|
||||
let config = Self {
|
||||
q_mul_fixed_short: meta.selector(),
|
||||
|
@ -84,7 +81,7 @@ impl Config {
|
|||
offset,
|
||||
magnitude.clone(),
|
||||
true,
|
||||
L_VALUE,
|
||||
L_SCALAR_SHORT,
|
||||
NUM_WINDOWS_SHORT,
|
||||
)?;
|
||||
|
||||
|
@ -99,8 +96,12 @@ impl Config {
|
|||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
magnitude_sign: MagnitudeSign,
|
||||
base: &ValueCommitV,
|
||||
) -> Result<(EccPoint, EccScalarFixedShort), Error> {
|
||||
base: &<Fixed as FixedPoints<pallas::Affine>>::ShortScalar,
|
||||
) -> Result<(EccPoint, EccScalarFixedShort), Error>
|
||||
where
|
||||
<Fixed as FixedPoints<pallas::Affine>>::ShortScalar:
|
||||
super::super::FixedPoint<pallas::Affine>,
|
||||
{
|
||||
let (scalar, acc, mul_b) = layouter.assign_region(
|
||||
|| "Short fixed-base mul (incomplete addition)",
|
||||
|mut region| {
|
||||
|
@ -109,13 +110,15 @@ impl Config {
|
|||
// Decompose the scalar
|
||||
let scalar = self.decompose(&mut region, offset, magnitude_sign.clone())?;
|
||||
|
||||
let (acc, mul_b) = self.super_config.assign_region_inner::<NUM_WINDOWS_SHORT>(
|
||||
&mut region,
|
||||
offset,
|
||||
&(&scalar).into(),
|
||||
base.clone().into(),
|
||||
self.super_config.running_sum_config.q_range_check,
|
||||
)?;
|
||||
let (acc, mul_b) = self
|
||||
.super_config
|
||||
.assign_region_inner::<_, NUM_WINDOWS_SHORT>(
|
||||
&mut region,
|
||||
offset,
|
||||
&(&scalar).into(),
|
||||
base,
|
||||
self.super_config.running_sum_config.q_range_check,
|
||||
)?;
|
||||
|
||||
Ok((scalar, acc, mul_b))
|
||||
},
|
||||
|
@ -187,14 +190,13 @@ impl Config {
|
|||
// Invalid values result in constraint failures which are
|
||||
// tested at the circuit-level.
|
||||
{
|
||||
use super::super::FixedPoint;
|
||||
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(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();
|
||||
|
||||
let scalar = scalar.magnitude.value().zip(scalar.sign.value()).map(
|
||||
|(magnitude, sign)| {
|
||||
// Move magnitude from base field into scalar field (which always fits
|
||||
|
@ -237,25 +239,24 @@ pub mod tests {
|
|||
|
||||
use crate::circuit::gadget::{
|
||||
ecc::{
|
||||
chip::{EccChip, MagnitudeSign},
|
||||
chip::{EccChip, FixedPoint, MagnitudeSign},
|
||||
FixedPointShort, NonIdentityPoint, Point,
|
||||
},
|
||||
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
||||
};
|
||||
use crate::constants::load::ValueCommitV;
|
||||
use crate::constants::{OrchardFixedBases, ValueCommitV};
|
||||
|
||||
#[allow(clippy::op_ref)]
|
||||
pub fn test_mul_fixed_short(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
) -> Result<(), Error> {
|
||||
// value_commit_v
|
||||
let value_commit_v = ValueCommitV::get();
|
||||
let base_val = value_commit_v.generator;
|
||||
let value_commit_v = FixedPointShort::from_inner(chip.clone(), value_commit_v);
|
||||
let base_val = ValueCommitV.generator();
|
||||
let value_commit_v = FixedPointShort::from_inner(chip.clone(), ValueCommitV);
|
||||
|
||||
fn load_magnitude_sign(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
magnitude: pallas::Base,
|
||||
sign: pallas::Base,
|
||||
|
@ -269,11 +270,11 @@ pub mod tests {
|
|||
}
|
||||
|
||||
fn constrain_equal_non_id(
|
||||
chip: EccChip,
|
||||
chip: EccChip<OrchardFixedBases>,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
base_val: pallas::Affine,
|
||||
scalar_val: pallas::Scalar,
|
||||
result: Point<pallas::Affine, EccChip>,
|
||||
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>,
|
||||
) -> Result<(), Error> {
|
||||
let expected = NonIdentityPoint::new(
|
||||
chip,
|
||||
|
@ -370,7 +371,10 @@ pub mod tests {
|
|||
|
||||
#[test]
|
||||
fn invalid_magnitude_sign() {
|
||||
use crate::circuit::gadget::{ecc::chip::EccConfig, utilities::UtilitiesInstructions};
|
||||
use crate::circuit::gadget::{
|
||||
ecc::chip::{EccConfig, FixedPoint},
|
||||
utilities::UtilitiesInstructions,
|
||||
};
|
||||
use halo2::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
dev::{FailureLocation, MockProver, VerifyFailure},
|
||||
|
@ -390,7 +394,7 @@ pub mod tests {
|
|||
}
|
||||
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
type Config = EccConfig;
|
||||
type Config = EccConfig<OrchardFixedBases>;
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
|
@ -427,7 +431,7 @@ pub mod tests {
|
|||
meta.enable_constant(constants);
|
||||
|
||||
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
|
||||
EccChip::configure(meta, advices, lagrange_coeffs, range_check)
|
||||
EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check)
|
||||
}
|
||||
|
||||
fn synthesize(
|
||||
|
@ -449,7 +453,7 @@ pub mod tests {
|
|||
(magnitude, sign)
|
||||
};
|
||||
|
||||
short_config.assign(layouter, magnitude_sign, &ValueCommitV::get())?;
|
||||
short_config.assign(layouter, magnitude_sign, &ValueCommitV)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -563,7 +567,7 @@ pub mod tests {
|
|||
};
|
||||
|
||||
let negation_check_y = {
|
||||
*(ValueCommitV::get().generator * pallas::Scalar::from(magnitude_u64))
|
||||
*(ValueCommitV.generator() * pallas::Scalar::from(magnitude_u64))
|
||||
.to_affine()
|
||||
.coordinates()
|
||||
.unwrap()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Gadget and chips for the Sinsemilla hash function.
|
||||
use crate::circuit::gadget::{
|
||||
ecc::{self, EccInstructions},
|
||||
ecc::{self, EccInstructions, FixedPoints},
|
||||
utilities::Var,
|
||||
};
|
||||
use group::ff::{Field, PrimeField};
|
||||
|
@ -9,10 +9,8 @@ use pasta_curves::arithmetic::CurveAffine;
|
|||
use std::fmt::Debug;
|
||||
|
||||
pub mod chip;
|
||||
pub mod commit_ivk;
|
||||
pub mod merkle;
|
||||
mod message;
|
||||
pub mod note_commit;
|
||||
|
||||
/// The set of circuit instructions required to use the [`Sinsemilla`](https://zcash.github.io/halo2/design/gadgets/sinsemilla.html) gadget.
|
||||
/// This trait is bounded on two constant parameters: `K`, the number of bits
|
||||
|
@ -49,7 +47,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>;
|
||||
|
@ -140,7 +138,7 @@ where
|
|||
/// Constructs a message from a vector of [`MessagePiece`]s.
|
||||
///
|
||||
/// [`MessagePiece`]: SinsemillaInstructions::MessagePiece
|
||||
fn from_pieces(
|
||||
pub fn from_pieces(
|
||||
chip: SinsemillaChip,
|
||||
pieces: Vec<MessagePiece<C, SinsemillaChip, K, MAX_WORDS>>,
|
||||
) -> Self {
|
||||
|
@ -169,7 +167,8 @@ impl<C: CurveAffine, SinsemillaChip, const K: usize, const MAX_WORDS: usize>
|
|||
where
|
||||
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
|
||||
{
|
||||
fn inner(&self) -> SinsemillaChip::MessagePiece {
|
||||
/// Returns the inner MessagePiece contained in this gadget.
|
||||
pub fn inner(&self) -> SinsemillaChip::MessagePiece {
|
||||
self.inner.clone()
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +210,8 @@ where
|
|||
Self::from_field_elem(chip, layouter, piece_value, num_words)
|
||||
}
|
||||
|
||||
fn from_field_elem(
|
||||
/// Constructs a MessagePiece from a field element.
|
||||
pub fn from_field_elem(
|
||||
chip: SinsemillaChip,
|
||||
layouter: impl Layouter<C::Base>,
|
||||
field_elem: Option<C::Base>,
|
||||
|
@ -303,12 +303,12 @@ where
|
|||
}
|
||||
|
||||
/// Trait allowing circuit's Sinsemilla CommitDomains to be enumerated.
|
||||
pub trait CommitDomains<C: CurveAffine, F: Clone + Debug, H: HashDomains<C>>:
|
||||
pub trait CommitDomains<C: CurveAffine, F: FixedPoints<C>, H: HashDomains<C>>:
|
||||
Clone + Debug
|
||||
{
|
||||
/// Returns the fixed point corresponding to the R constant used for
|
||||
/// randomization in this CommitDomain.
|
||||
fn r(&self) -> F;
|
||||
fn r(&self) -> F::FullScalar;
|
||||
|
||||
/// Returns the HashDomain contained in this CommitDomain
|
||||
fn hash_domain(&self) -> H;
|
||||
|
@ -357,6 +357,7 @@ where
|
|||
pub fn new(
|
||||
sinsemilla_chip: SinsemillaChip,
|
||||
ecc_chip: EccChip,
|
||||
// TODO: Instead of using SinsemilllaChip::CommitDomains, just use something that implements a CommitDomains trait
|
||||
domain: &SinsemillaChip::CommitDomains,
|
||||
) -> Self {
|
||||
CommitDomain {
|
||||
|
@ -414,7 +415,7 @@ mod tests {
|
|||
use rand::rngs::OsRng;
|
||||
|
||||
use super::{
|
||||
chip::{SinsemillaChip, SinsemillaCommitDomains, SinsemillaConfig, SinsemillaHashDomains},
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
CommitDomain, HashDomain, Message, MessagePiece,
|
||||
};
|
||||
|
||||
|
@ -426,7 +427,10 @@ mod tests {
|
|||
},
|
||||
utilities::lookup_range_check::LookupRangeCheckConfig,
|
||||
},
|
||||
constants::{COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION},
|
||||
constants::{
|
||||
fixed_bases::COMMIT_IVK_PERSONALIZATION, sinsemilla::MERKLE_CRH_PERSONALIZATION,
|
||||
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
|
||||
},
|
||||
primitives::sinsemilla::{self, K},
|
||||
};
|
||||
|
||||
|
@ -438,7 +442,12 @@ mod tests {
|
|||
struct MyCircuit {}
|
||||
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
type Config = (EccConfig, SinsemillaConfig, SinsemillaConfig);
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Config = (
|
||||
EccConfig<OrchardFixedBases>,
|
||||
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
);
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
|
@ -485,7 +494,12 @@ mod tests {
|
|||
|
||||
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
|
||||
|
||||
let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check);
|
||||
let ecc_config = EccChip::<OrchardFixedBases>::configure(
|
||||
meta,
|
||||
advices,
|
||||
lagrange_coeffs,
|
||||
range_check,
|
||||
);
|
||||
|
||||
let config1 = SinsemillaChip::configure(
|
||||
meta,
|
||||
|
@ -516,7 +530,10 @@ mod tests {
|
|||
let ecc_chip = EccChip::construct(config.0);
|
||||
|
||||
// The two `SinsemillaChip`s share the same lookup table.
|
||||
SinsemillaChip::load(config.1.clone(), &mut layouter)?;
|
||||
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(
|
||||
config.1.clone(),
|
||||
&mut layouter,
|
||||
)?;
|
||||
|
||||
// This MerkleCRH example is purely for illustrative purposes.
|
||||
// It is not an implementation of the Orchard protocol spec.
|
||||
|
@ -526,10 +543,10 @@ mod tests {
|
|||
let merkle_crh = HashDomain::new(
|
||||
chip1.clone(),
|
||||
ecc_chip.clone(),
|
||||
&SinsemillaHashDomains::MerkleCrh,
|
||||
&OrchardHashDomains::MerkleCrh,
|
||||
);
|
||||
|
||||
// Layer 31, l = MERKLE_DEPTH_ORCHARD - 1 - layer = 0
|
||||
// Layer 31, l = MERKLE_DEPTH - 1 - layer = 0
|
||||
let l_bitstring = vec![Some(false); K];
|
||||
let l = MessagePiece::from_bitstring(
|
||||
chip1.clone(),
|
||||
|
@ -602,7 +619,7 @@ mod tests {
|
|||
let commit_ivk = CommitDomain::new(
|
||||
chip2.clone(),
|
||||
ecc_chip.clone(),
|
||||
&SinsemillaCommitDomains::CommitIvk,
|
||||
&OrchardCommitDomains::CommitIvk,
|
||||
);
|
||||
let r_val = pallas::Scalar::random(rng);
|
||||
let message: Vec<Option<bool>> =
|
||||
|
|
|
@ -4,17 +4,14 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
circuit::gadget::{
|
||||
ecc::chip::NonIdentityEccPoint, utilities::lookup_range_check::LookupRangeCheckConfig,
|
||||
},
|
||||
constants::OrchardFixedBasesFull,
|
||||
primitives::sinsemilla::{
|
||||
self, Q_COMMIT_IVK_M_GENERATOR, Q_MERKLE_CRH, Q_NOTE_COMMITMENT_M_GENERATOR,
|
||||
ecc::{chip::NonIdentityEccPoint, FixedPoints},
|
||||
utilities::lookup_range_check::LookupRangeCheckConfig,
|
||||
},
|
||||
primitives::sinsemilla,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use group::ff::PrimeField;
|
||||
use halo2::{
|
||||
arithmetic::CurveAffine,
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
plonk::{
|
||||
Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, TableColumn,
|
||||
|
@ -31,7 +28,12 @@ mod hash_to_point;
|
|||
|
||||
/// Configuration for the Sinsemilla hash chip
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct SinsemillaConfig {
|
||||
pub struct SinsemillaConfig<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
/// Binary selector used in lookup argument and in the body of the Sinsemilla hash.
|
||||
q_sinsemilla1: Selector,
|
||||
/// Non-binary selector used in lookup argument and in the body of the Sinsemilla hash.
|
||||
|
@ -65,23 +67,44 @@ pub struct SinsemillaConfig {
|
|||
/// generators of the Sinsemilla hash.
|
||||
pub(super) generator_table: GeneratorTableConfig,
|
||||
/// An advice column configured to perform lookup range checks.
|
||||
pub(super) lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
_marker: PhantomData<(Hash, Commit, F)>,
|
||||
}
|
||||
|
||||
impl SinsemillaConfig {
|
||||
impl<Hash, Commit, F> SinsemillaConfig<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
/// Returns an array of all advice columns in this config, in arbitrary order.
|
||||
pub(super) fn advices(&self) -> [Column<Advice>; 5] {
|
||||
[self.x_a, self.x_p, self.bits, self.lambda_1, self.lambda_2]
|
||||
}
|
||||
|
||||
/// Returns the lookup range check config used in this config.
|
||||
pub fn lookup_config(&self) -> LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }> {
|
||||
self.lookup_config
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct SinsemillaChip {
|
||||
config: SinsemillaConfig,
|
||||
pub struct SinsemillaChip<Hash, Commit, Fixed>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
Fixed: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
||||
{
|
||||
config: SinsemillaConfig<Hash, Commit, Fixed>,
|
||||
}
|
||||
|
||||
impl Chip<pallas::Base> for SinsemillaChip {
|
||||
type Config = SinsemillaConfig;
|
||||
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>,
|
||||
{
|
||||
type Config = SinsemillaConfig<Hash, Commit, Fixed>;
|
||||
type Loaded = ();
|
||||
|
||||
fn config(&self) -> &Self::Config {
|
||||
|
@ -93,13 +116,18 @@ impl Chip<pallas::Base> for SinsemillaChip {
|
|||
}
|
||||
}
|
||||
|
||||
impl SinsemillaChip {
|
||||
impl<Hash, Commit, F> SinsemillaChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
pub fn construct(config: <Self as Chip<pallas::Base>>::Config) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
config: SinsemillaConfig,
|
||||
config: SinsemillaConfig<Hash, Commit, F>,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
) -> Result<<Self as Chip<pallas::Base>>::Loaded, Error> {
|
||||
// Load the lookup table.
|
||||
|
@ -124,7 +152,7 @@ impl SinsemillaChip {
|
|||
meta.enable_equality(*advice);
|
||||
}
|
||||
|
||||
let config = SinsemillaConfig {
|
||||
let config = SinsemillaConfig::<Hash, Commit, F> {
|
||||
q_sinsemilla1: meta.complex_selector(),
|
||||
q_sinsemilla2: meta.fixed_column(),
|
||||
q_sinsemilla4: meta.selector(),
|
||||
|
@ -141,6 +169,7 @@ impl SinsemillaChip {
|
|||
table_y: lookup.2,
|
||||
},
|
||||
lookup_config: range_check,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
// Set up lookup argument
|
||||
|
@ -236,8 +265,12 @@ impl SinsemillaChip {
|
|||
}
|
||||
|
||||
// Implement `SinsemillaInstructions` for `SinsemillaChip`
|
||||
impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }>
|
||||
for SinsemillaChip
|
||||
impl<Hash, Commit, F> SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }>
|
||||
for SinsemillaChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
type CellValue = AssignedCell<pallas::Base, pallas::Base>;
|
||||
|
||||
|
@ -248,10 +281,10 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
|
|||
|
||||
type X = AssignedCell<pallas::Base, pallas::Base>;
|
||||
type NonIdentityPoint = NonIdentityEccPoint;
|
||||
type FixedPoints = OrchardFixedBasesFull;
|
||||
type FixedPoints = F;
|
||||
|
||||
type HashDomains = SinsemillaHashDomains;
|
||||
type CommitDomains = SinsemillaCommitDomains;
|
||||
type HashDomains = Hash;
|
||||
type CommitDomains = Commit;
|
||||
|
||||
fn witness_message_piece(
|
||||
&self,
|
||||
|
@ -293,57 +326,3 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
|
|||
point.x()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SinsemillaHashDomains {
|
||||
NoteCommit,
|
||||
CommitIvk,
|
||||
MerkleCrh,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl HashDomains<pallas::Affine> for SinsemillaHashDomains {
|
||||
fn Q(&self) -> pallas::Affine {
|
||||
match self {
|
||||
SinsemillaHashDomains::CommitIvk => pallas::Affine::from_xy(
|
||||
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_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_repr(Q_MERKLE_CRH.0).unwrap(),
|
||||
pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SinsemillaCommitDomains {
|
||||
NoteCommit,
|
||||
CommitIvk,
|
||||
}
|
||||
|
||||
impl CommitDomains<pallas::Affine, OrchardFixedBasesFull, SinsemillaHashDomains>
|
||||
for SinsemillaCommitDomains
|
||||
{
|
||||
fn r(&self) -> OrchardFixedBasesFull {
|
||||
match self {
|
||||
Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR,
|
||||
Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR,
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_domain(&self) -> SinsemillaHashDomains {
|
||||
match self {
|
||||
Self::NoteCommit => SinsemillaHashDomains::NoteCommit,
|
||||
Self::CommitIvk => SinsemillaHashDomains::CommitIvk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use halo2::{
|
|||
poly::Rotation,
|
||||
};
|
||||
|
||||
use super::{CommitDomains, FixedPoints, HashDomains};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
|
||||
/// Table containing independent generators S[0..2^k]
|
||||
|
@ -21,7 +22,14 @@ impl GeneratorTableConfig {
|
|||
/// Even though the lookup table can be used in other parts of the circuit,
|
||||
/// this specific configuration sets up Sinsemilla-specific constraints
|
||||
/// controlled by `q_sinsemilla`, and would likely not apply to other chips.
|
||||
pub fn configure(meta: &mut ConstraintSystem<pallas::Base>, config: super::SinsemillaConfig) {
|
||||
pub fn configure<Hash, Commit, F>(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
config: super::SinsemillaConfig<Hash, Commit, F>,
|
||||
) where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
let (table_idx, table_x, table_y) = (
|
||||
config.generator_table.table_idx,
|
||||
config.generator_table.table_x,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use super::super::SinsemillaInstructions;
|
||||
use super::super::{CommitDomains, HashDomains, SinsemillaInstructions};
|
||||
use super::{NonIdentityEccPoint, SinsemillaChip};
|
||||
|
||||
use crate::circuit::gadget::ecc::FixedPoints;
|
||||
use crate::primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K, SINSEMILLA_S};
|
||||
use halo2::circuit::AssignedCell;
|
||||
use halo2::{
|
||||
|
@ -15,7 +17,12 @@ use pasta_curves::{
|
|||
|
||||
use std::ops::Deref;
|
||||
|
||||
impl SinsemillaChip {
|
||||
impl<Hash, Commit, Fixed> SinsemillaChip<Hash, Commit, Fixed>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
Fixed: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(super) fn hash_message(
|
||||
|
|
|
@ -6,16 +6,16 @@ use pasta_curves::arithmetic::CurveAffine;
|
|||
|
||||
use super::{HashDomains, SinsemillaInstructions};
|
||||
|
||||
use crate::{
|
||||
circuit::gadget::utilities::{
|
||||
cond_swap::CondSwapInstructions, transpose_option_array, UtilitiesInstructions,
|
||||
},
|
||||
spec::i2lebsp,
|
||||
use crate::circuit::gadget::utilities::{
|
||||
cond_swap::CondSwapInstructions, i2lebsp, transpose_option_array, UtilitiesInstructions,
|
||||
};
|
||||
use std::iter;
|
||||
|
||||
pub(in crate::circuit) mod chip;
|
||||
|
||||
/// SWU hash-to-curve personalization for the Merkle CRH generator
|
||||
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
|
||||
|
||||
/// Instructions to check the validity of a Merkle path of a given `PATH_LENGTH`.
|
||||
/// The hash function used is a Sinsemilla instance with `K`-bit words.
|
||||
/// The hash function can process `MAX_WORDS` words.
|
||||
|
@ -32,7 +32,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,
|
||||
|
@ -100,7 +100,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.
|
||||
|
@ -138,10 +138,10 @@ pub mod tests {
|
|||
|
||||
use crate::{
|
||||
circuit::gadget::{
|
||||
sinsemilla::chip::{SinsemillaChip, SinsemillaHashDomains},
|
||||
sinsemilla::chip::SinsemillaChip,
|
||||
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
||||
},
|
||||
constants::MERKLE_DEPTH_ORCHARD,
|
||||
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains},
|
||||
note::commitment::ExtractedNoteCommitment,
|
||||
tree,
|
||||
};
|
||||
|
@ -157,15 +157,20 @@ pub mod tests {
|
|||
use rand::{rngs::OsRng, RngCore};
|
||||
use std::convert::TryInto;
|
||||
|
||||
const MERKLE_DEPTH: usize = 32;
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyCircuit {
|
||||
leaf: Option<pallas::Base>,
|
||||
leaf_pos: Option<u32>,
|
||||
merkle_path: Option<[pallas::Base; MERKLE_DEPTH_ORCHARD]>,
|
||||
merkle_path: Option<[pallas::Base; MERKLE_DEPTH]>,
|
||||
}
|
||||
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
type Config = (MerkleConfig, MerkleConfig);
|
||||
type Config = (
|
||||
MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
);
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
|
@ -233,7 +238,10 @@ pub mod tests {
|
|||
mut layouter: impl Layouter<pallas::Base>,
|
||||
) -> Result<(), Error> {
|
||||
// Load generator table (shared across both configs)
|
||||
SinsemillaChip::load(config.0.sinsemilla_config.clone(), &mut layouter)?;
|
||||
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(
|
||||
config.0.sinsemilla_config.clone(),
|
||||
&mut layouter,
|
||||
)?;
|
||||
|
||||
// Construct Merkle chips which will be placed side-by-side in the circuit.
|
||||
let chip_1 = MerkleChip::construct(config.0.clone());
|
||||
|
@ -248,7 +256,7 @@ pub mod tests {
|
|||
let path = MerklePath {
|
||||
chip_1,
|
||||
chip_2,
|
||||
domain: SinsemillaHashDomains::MerkleCrh,
|
||||
domain: OrchardHashDomains::MerkleCrh,
|
||||
leaf_pos: self.leaf_pos,
|
||||
path: self.merkle_path,
|
||||
};
|
||||
|
@ -282,7 +290,7 @@ pub mod tests {
|
|||
let pos = rng.next_u32();
|
||||
|
||||
// Choose a path of random inner nodes
|
||||
let path: Vec<_> = (0..(MERKLE_DEPTH_ORCHARD))
|
||||
let path: Vec<_> = (0..(MERKLE_DEPTH))
|
||||
.map(|_| pallas::Base::random(rng))
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -5,38 +5,56 @@ use halo2::{
|
|||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
|
||||
use super::super::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
SinsemillaInstructions,
|
||||
};
|
||||
use super::MerkleInstructions;
|
||||
|
||||
use crate::{
|
||||
circuit::gadget::utilities::{
|
||||
bitrange_subset,
|
||||
cond_swap::{CondSwapChip, CondSwapConfig, CondSwapInstructions},
|
||||
UtilitiesInstructions,
|
||||
circuit::gadget::{
|
||||
ecc::FixedPoints,
|
||||
sinsemilla::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
CommitDomains, HashDomains, SinsemillaInstructions,
|
||||
},
|
||||
utilities::{
|
||||
bitrange_subset,
|
||||
cond_swap::{CondSwapChip, CondSwapConfig, CondSwapInstructions},
|
||||
UtilitiesInstructions,
|
||||
},
|
||||
},
|
||||
constants::{L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD},
|
||||
primitives::sinsemilla,
|
||||
};
|
||||
use group::ff::PrimeField;
|
||||
use std::array;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MerkleConfig {
|
||||
pub struct MerkleConfig<Hash, Commit, Fixed>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
Fixed: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
||||
{
|
||||
advices: [Column<Advice>; 5],
|
||||
q_decompose: Selector,
|
||||
pub(super) cond_swap_config: CondSwapConfig,
|
||||
pub(super) sinsemilla_config: SinsemillaConfig,
|
||||
pub(super) sinsemilla_config: SinsemillaConfig<Hash, Commit, Fixed>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MerkleChip {
|
||||
config: MerkleConfig,
|
||||
pub struct MerkleChip<Hash, Commit, Fixed>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
Fixed: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
||||
{
|
||||
config: MerkleConfig<Hash, Commit, Fixed>,
|
||||
}
|
||||
|
||||
impl Chip<pallas::Base> for MerkleChip {
|
||||
type Config = MerkleConfig;
|
||||
impl<Hash, Commit, Fixed> Chip<pallas::Base> for MerkleChip<Hash, Commit, Fixed>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
Fixed: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
||||
{
|
||||
type Config = MerkleConfig<Hash, Commit, Fixed>;
|
||||
type Loaded = ();
|
||||
|
||||
fn config(&self) -> &Self::Config {
|
||||
|
@ -48,11 +66,16 @@ impl Chip<pallas::Base> for MerkleChip {
|
|||
}
|
||||
}
|
||||
|
||||
impl MerkleChip {
|
||||
impl<Hash, Commit, F> MerkleChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
pub fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
sinsemilla_config: SinsemillaConfig,
|
||||
) -> MerkleConfig {
|
||||
sinsemilla_config: SinsemillaConfig<Hash, Commit, F>,
|
||||
) -> MerkleConfig<Hash, Commit, F> {
|
||||
// All five advice columns are equality-enabled by SinsemillaConfig.
|
||||
let advices = sinsemilla_config.advices();
|
||||
let cond_swap_config = CondSwapChip::configure(meta, advices);
|
||||
|
@ -152,20 +175,25 @@ impl MerkleChip {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn construct(config: MerkleConfig) -> Self {
|
||||
pub fn construct(config: MerkleConfig<Hash, Commit, F>) -> Self {
|
||||
MerkleChip { config }
|
||||
}
|
||||
}
|
||||
|
||||
impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K }, { sinsemilla::C }>
|
||||
for MerkleChip
|
||||
impl<Hash, Commit, F, const MERKLE_DEPTH: usize>
|
||||
MerkleInstructions<pallas::Affine, MERKLE_DEPTH, { sinsemilla::K }, { sinsemilla::C }>
|
||||
for MerkleChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fn hash_layer(
|
||||
&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,
|
||||
|
@ -207,13 +235,12 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
let b_1 = {
|
||||
let b_1 = left
|
||||
.value()
|
||||
.map(|value| bitrange_subset(value, 250..L_ORCHARD_BASE));
|
||||
.map(|value| bitrange_subset(value, 250..(pallas::Base::NUM_BITS as usize)));
|
||||
|
||||
config.sinsemilla_config.lookup_config.witness_short_check(
|
||||
layouter.namespace(|| "Constrain b_1 to 5 bits"),
|
||||
b_1,
|
||||
5,
|
||||
)?
|
||||
config
|
||||
.sinsemilla_config
|
||||
.lookup_config()
|
||||
.witness_short_check(layouter.namespace(|| "Constrain b_1 to 5 bits"), b_1, 5)?
|
||||
};
|
||||
|
||||
// b_2 = (bits 0..=4 of `right`)
|
||||
|
@ -221,11 +248,10 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
let b_2 = {
|
||||
let b_2 = right.value().map(|value| bitrange_subset(value, 0..5));
|
||||
|
||||
config.sinsemilla_config.lookup_config.witness_short_check(
|
||||
layouter.namespace(|| "Constrain b_2 to 5 bits"),
|
||||
b_2,
|
||||
5,
|
||||
)?
|
||||
config
|
||||
.sinsemilla_config
|
||||
.lookup_config()
|
||||
.witness_short_check(layouter.namespace(|| "Constrain b_2 to 5 bits"), b_2, 5)?
|
||||
};
|
||||
|
||||
let b = {
|
||||
|
@ -249,7 +275,7 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
// `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..(pallas::Base::NUM_BITS as usize)));
|
||||
self.witness_message_piece(layouter.namespace(|| "Witness c"), c, 25)?
|
||||
};
|
||||
|
||||
|
@ -274,7 +300,7 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
|| "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(
|
||||
|
@ -319,11 +345,9 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
// Check layer hash output against Sinsemilla primitives hash
|
||||
#[cfg(test)]
|
||||
{
|
||||
use crate::{
|
||||
constants::MERKLE_CRH_PERSONALIZATION, primitives::sinsemilla::HashDomain,
|
||||
spec::i2lebsp,
|
||||
};
|
||||
use group::ff::{PrimeField, PrimeFieldBits};
|
||||
use super::MERKLE_CRH_PERSONALIZATION;
|
||||
use crate::{primitives::sinsemilla::HashDomain, spec::i2lebsp};
|
||||
use group::ff::PrimeFieldBits;
|
||||
|
||||
if let (Some(left), Some(right)) = (left.value(), right.value()) {
|
||||
let l = i2lebsp::<10>(l as u64);
|
||||
|
@ -331,13 +355,13 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
.to_le_bits()
|
||||
.iter()
|
||||
.by_val()
|
||||
.take(L_ORCHARD_BASE)
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect();
|
||||
let right: Vec<_> = right
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_val()
|
||||
.take(L_ORCHARD_BASE)
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect();
|
||||
let merkle_crh = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
|
||||
|
||||
|
@ -355,11 +379,21 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
|
|||
}
|
||||
}
|
||||
|
||||
impl UtilitiesInstructions<pallas::Base> for MerkleChip {
|
||||
impl<Hash, Commit, F> UtilitiesInstructions<pallas::Base> for MerkleChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
type Var = AssignedCell<pallas::Base, pallas::Base>;
|
||||
}
|
||||
|
||||
impl CondSwapInstructions<pallas::Base> for MerkleChip {
|
||||
impl<Hash, Commit, F> CondSwapInstructions<pallas::Base> for MerkleChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn swap(
|
||||
&self,
|
||||
|
@ -373,51 +407,57 @@ impl CondSwapInstructions<pallas::Base> for MerkleChip {
|
|||
}
|
||||
}
|
||||
|
||||
impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }> for MerkleChip {
|
||||
type CellValue = <SinsemillaChip as SinsemillaInstructions<
|
||||
impl<Hash, Commit, F> SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }>
|
||||
for MerkleChip<Hash, Commit, F>
|
||||
where
|
||||
Hash: HashDomains<pallas::Affine>,
|
||||
F: FixedPoints<pallas::Affine>,
|
||||
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
||||
{
|
||||
type CellValue = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::CellValue;
|
||||
|
||||
type Message = <SinsemillaChip as SinsemillaInstructions<
|
||||
type Message = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::Message;
|
||||
type MessagePiece = <SinsemillaChip as SinsemillaInstructions<
|
||||
type MessagePiece = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::MessagePiece;
|
||||
type RunningSum = <SinsemillaChip as SinsemillaInstructions<
|
||||
type RunningSum = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::RunningSum;
|
||||
|
||||
type X = <SinsemillaChip as SinsemillaInstructions<
|
||||
type X = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::X;
|
||||
type NonIdentityPoint = <SinsemillaChip as SinsemillaInstructions<
|
||||
type NonIdentityPoint = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::NonIdentityPoint;
|
||||
type FixedPoints = <SinsemillaChip as SinsemillaInstructions<
|
||||
type FixedPoints = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::FixedPoints;
|
||||
|
||||
type HashDomains = <SinsemillaChip as SinsemillaInstructions<
|
||||
type HashDomains = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
>>::HashDomains;
|
||||
type CommitDomains = <SinsemillaChip as SinsemillaInstructions<
|
||||
type CommitDomains = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
||||
pallas::Affine,
|
||||
{ sinsemilla::K },
|
||||
{ sinsemilla::C },
|
||||
|
@ -430,7 +470,7 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
|
|||
num_words: usize,
|
||||
) -> Result<Self::MessagePiece, Error> {
|
||||
let config = self.config().sinsemilla_config.clone();
|
||||
let chip = SinsemillaChip::construct(config);
|
||||
let chip = SinsemillaChip::<Hash, Commit, F>::construct(config);
|
||||
chip.witness_message_piece(layouter, value, num_words)
|
||||
}
|
||||
|
||||
|
@ -443,11 +483,11 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
|
|||
message: Self::Message,
|
||||
) -> Result<(Self::NonIdentityPoint, Vec<Vec<Self::CellValue>>), Error> {
|
||||
let config = self.config().sinsemilla_config.clone();
|
||||
let chip = SinsemillaChip::construct(config);
|
||||
let chip = SinsemillaChip::<Hash, Commit, F>::construct(config);
|
||||
chip.hash_to_point(layouter, Q, message)
|
||||
}
|
||||
|
||||
fn extract(point: &Self::NonIdentityPoint) -> Self::X {
|
||||
SinsemillaChip::extract(point)
|
||||
SinsemillaChip::<Hash, Commit, F>::extract(point)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,70 @@ pub fn range_check<F: FieldExt>(word: Expression<F>, range: usize) -> Expression
|
|||
})
|
||||
}
|
||||
|
||||
/// 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`
|
||||
/// is a `w`-bit value, and `scalar = k_0 + k_1 * w + k_n * w^n`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// We are returning a `Vec<u8>` which means the window size is limited to
|
||||
/// <= 8 bits.
|
||||
pub fn decompose_word<F: PrimeFieldBits>(
|
||||
word: &F,
|
||||
word_num_bits: usize,
|
||||
window_num_bits: usize,
|
||||
) -> Vec<u8> {
|
||||
assert!(window_num_bits <= 8);
|
||||
|
||||
// Pad bits to multiple of window_num_bits
|
||||
let padding = (window_num_bits - (word_num_bits % window_num_bits)) % window_num_bits;
|
||||
let bits: Vec<bool> = word
|
||||
.to_le_bits()
|
||||
.into_iter()
|
||||
.take(word_num_bits)
|
||||
.chain(std::iter::repeat(false).take(padding))
|
||||
.collect();
|
||||
assert_eq!(bits.len(), word_num_bits + padding);
|
||||
|
||||
bits.chunks_exact(window_num_bits)
|
||||
.map(|chunk| chunk.iter().rev().fold(0, |acc, b| (acc << 1) + (*b as u8)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The u64 integer represented by an L-bit little-endian bitstring.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the bitstring is longer than 64 bits.
|
||||
pub fn lebs2ip<const L: usize>(bits: &[bool; L]) -> u64 {
|
||||
assert!(L <= 64);
|
||||
bits.iter()
|
||||
.enumerate()
|
||||
.fold(0u64, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
||||
}
|
||||
|
||||
/// The sequence of bits representing a u64 in little-endian order.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the expected length of the sequence `NUM_BITS` exceeds
|
||||
/// 64.
|
||||
pub fn i2lebsp<const NUM_BITS: usize>(int: u64) -> [bool; NUM_BITS] {
|
||||
/// Takes in an FnMut closure and returns a constant-length array with elements of
|
||||
/// type `Output`.
|
||||
fn gen_const_array<Output: Copy + Default, const LEN: usize>(
|
||||
mut closure: impl FnMut(usize) -> Output,
|
||||
) -> [Output; LEN] {
|
||||
let mut ret: [Output; LEN] = [Default::default(); LEN];
|
||||
for (bit, val) in ret.iter_mut().zip((0..LEN).map(|idx| closure(idx))) {
|
||||
*bit = val;
|
||||
}
|
||||
ret
|
||||
}
|
||||
assert!(NUM_BITS <= 64);
|
||||
gen_const_array(|mask: usize| (int & (1 << mask)) != 0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -126,8 +190,11 @@ mod tests {
|
|||
plonk::{Any, Circuit, ConstraintSystem, Error, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
use proptest::prelude::*;
|
||||
use rand::rngs::OsRng;
|
||||
use std::convert::TryInto;
|
||||
use std::iter;
|
||||
|
||||
#[test]
|
||||
fn test_range_check() {
|
||||
|
@ -283,4 +350,83 @@ mod tests {
|
|||
&[0..50, 50..100, 100..150, 150..200, 200..255],
|
||||
);
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_scalar()(bytes in prop::array::uniform32(0u8..)) -> pallas::Scalar {
|
||||
// Instead of rejecting out-of-range bytes, let's reduce them.
|
||||
let mut buf = [0; 64];
|
||||
buf[..32].copy_from_slice(&bytes);
|
||||
pallas::Scalar::from_bytes_wide(&buf)
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_decompose_word(
|
||||
scalar in arb_scalar(),
|
||||
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);
|
||||
|
||||
// Flatten bits
|
||||
let bits = decomposed
|
||||
.iter()
|
||||
.flat_map(|window| (0..window_num_bits).map(move |mask| (window & (1 << mask)) != 0));
|
||||
|
||||
// Ensure this decomposition contains 256 or fewer set bits.
|
||||
assert!(!bits.clone().skip(32*8).any(|b| b));
|
||||
|
||||
// Pad or truncate bits to 32 bytes
|
||||
let bits: Vec<bool> = bits.chain(iter::repeat(false)).take(32*8).collect();
|
||||
|
||||
let bytes: Vec<u8> = 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_repr(bytes.try_into().unwrap()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lebs2ip_round_trip() {
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
let mut rng = OsRng;
|
||||
{
|
||||
let int = rng.next_u64();
|
||||
assert_eq!(lebs2ip::<64>(&i2lebsp(int)), int);
|
||||
}
|
||||
|
||||
assert_eq!(lebs2ip::<64>(&i2lebsp(0)), 0);
|
||||
assert_eq!(
|
||||
lebs2ip::<64>(&i2lebsp(0xFFFFFFFFFFFFFFFF)),
|
||||
0xFFFFFFFFFFFFFFFF
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2lebsp_round_trip() {
|
||||
{
|
||||
let bitstring = (0..64).map(|_| rand::random()).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
i2lebsp::<64>(lebs2ip::<64>(&bitstring.clone().try_into().unwrap())).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [false; 64];
|
||||
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [true; 64];
|
||||
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [];
|
||||
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ use halo2::{
|
|||
};
|
||||
|
||||
use super::range_check;
|
||||
use crate::constants::util::decompose_word;
|
||||
use pasta_curves::arithmetic::FieldExt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -166,7 +165,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
let words: Vec<Option<u8>> = {
|
||||
let words = z_0
|
||||
.value()
|
||||
.map(|word| decompose_word::<F>(word, word_num_bits, WINDOW_NUM_BITS));
|
||||
.map(|word| super::decompose_word::<F>(word, word_num_bits, WINDOW_NUM_BITS));
|
||||
|
||||
if let Some(words) = words {
|
||||
words.into_iter().map(Some).collect()
|
||||
|
@ -217,8 +216,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::constants::{self, FIXED_BASE_WINDOW_SIZE, L_ORCHARD_BASE, L_VALUE};
|
||||
use group::ff::Field;
|
||||
use group::ff::{Field, PrimeField};
|
||||
use halo2::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
dev::{MockProver, VerifyFailure},
|
||||
|
@ -227,6 +225,12 @@ mod tests {
|
|||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::circuit::gadget::ecc::chip::{
|
||||
FIXED_BASE_WINDOW_SIZE, L_SCALAR_SHORT as L_SHORT, NUM_WINDOWS, NUM_WINDOWS_SHORT,
|
||||
};
|
||||
|
||||
const L_BASE: usize = pallas::Base::NUM_BITS as usize;
|
||||
|
||||
#[test]
|
||||
fn test_running_sum() {
|
||||
struct MyCircuit<
|
||||
|
@ -306,15 +310,11 @@ mod tests {
|
|||
let alpha = pallas::Base::random(OsRng);
|
||||
|
||||
// Strict full decomposition should pass.
|
||||
let circuit: MyCircuit<
|
||||
pallas::Base,
|
||||
L_ORCHARD_BASE,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ constants::NUM_WINDOWS },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
};
|
||||
let circuit: MyCircuit<pallas::Base, L_BASE, FIXED_BASE_WINDOW_SIZE, { NUM_WINDOWS }> =
|
||||
MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
|
@ -326,9 +326,9 @@ mod tests {
|
|||
// Strict full decomposition should pass.
|
||||
let circuit: MyCircuit<
|
||||
pallas::Base,
|
||||
L_VALUE,
|
||||
L_SHORT,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ constants::NUM_WINDOWS_SHORT },
|
||||
{ NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
|
@ -344,9 +344,9 @@ mod tests {
|
|||
// Strict partial decomposition should fail.
|
||||
let circuit: MyCircuit<
|
||||
pallas::Base,
|
||||
L_VALUE,
|
||||
L_SHORT,
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ constants::NUM_WINDOWS_SHORT },
|
||||
{ NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: true,
|
||||
|
@ -377,9 +377,9 @@ mod tests {
|
|||
// Non-strict partial decomposition should pass.
|
||||
let circuit: MyCircuit<
|
||||
pallas::Base,
|
||||
{ constants::L_VALUE },
|
||||
{ L_SHORT },
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ constants::NUM_WINDOWS_SHORT },
|
||||
{ NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
strict: false,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Make use of a K-bit lookup table to decompose a field element into K-bit
|
||||
//! words.
|
||||
|
||||
use crate::spec::lebs2ip;
|
||||
use halo2::{
|
||||
circuit::{AssignedCell, Layouter, Region},
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Selector, TableColumn},
|
||||
|
@ -364,8 +363,8 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
|||
mod tests {
|
||||
use super::LookupRangeCheckConfig;
|
||||
|
||||
use super::super::lebs2ip;
|
||||
use crate::primitives::sinsemilla::K;
|
||||
use crate::spec::lebs2ip;
|
||||
use ff::{Field, PrimeFieldBits};
|
||||
use halo2::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
|
|
|
@ -11,14 +11,13 @@ use crate::{
|
|||
chip::{EccChip, NonIdentityEccPoint},
|
||||
Point,
|
||||
},
|
||||
sinsemilla::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
CommitDomain, Message, MessagePiece,
|
||||
},
|
||||
utilities::{bitrange_subset, bool_check},
|
||||
},
|
||||
constants::T_P,
|
||||
};
|
||||
|
||||
use super::{
|
||||
chip::{SinsemillaChip, SinsemillaCommitDomains, SinsemillaConfig},
|
||||
CommitDomain, Message, MessagePiece,
|
||||
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P},
|
||||
};
|
||||
|
||||
/// The values of the running sum at the start and end of the range being used for a
|
||||
|
@ -56,7 +55,8 @@ pub struct NoteCommitConfig {
|
|||
q_notecommit_psi: Selector,
|
||||
q_y_canon: Selector,
|
||||
advices: [Column<Advice>; 10],
|
||||
sinsemilla_config: SinsemillaConfig,
|
||||
sinsemilla_config:
|
||||
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
}
|
||||
|
||||
impl NoteCommitConfig {
|
||||
|
@ -65,7 +65,11 @@ impl NoteCommitConfig {
|
|||
pub(in crate::circuit) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
advices: [Column<Advice>; 10],
|
||||
sinsemilla_config: SinsemillaConfig,
|
||||
sinsemilla_config: SinsemillaConfig<
|
||||
OrchardHashDomains,
|
||||
OrchardCommitDomains,
|
||||
OrchardFixedBases,
|
||||
>,
|
||||
) -> Self {
|
||||
let q_notecommit_b = meta.selector();
|
||||
let q_notecommit_d = meta.selector();
|
||||
|
@ -528,8 +532,8 @@ impl NoteCommitConfig {
|
|||
pub(in crate::circuit) fn assign_region(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
chip: SinsemillaChip,
|
||||
ecc_chip: EccChip,
|
||||
chip: SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
ecc_chip: EccChip<OrchardFixedBases>,
|
||||
g_d: &NonIdentityEccPoint,
|
||||
pk_d: &NonIdentityEccPoint,
|
||||
// TODO: Set V to Orchard value type
|
||||
|
@ -537,7 +541,7 @@ impl NoteCommitConfig {
|
|||
rho: AssignedCell<pallas::Base, pallas::Base>,
|
||||
psi: AssignedCell<pallas::Base, pallas::Base>,
|
||||
rcm: Option<pallas::Scalar>,
|
||||
) -> Result<Point<pallas::Affine, EccChip>, Error> {
|
||||
) -> Result<Point<pallas::Affine, EccChip<OrchardFixedBases>>, Error> {
|
||||
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());
|
||||
|
@ -562,14 +566,14 @@ impl NoteCommitConfig {
|
|||
let b_3 = pkd_x.map(|pkd_x| bitrange_subset(pkd_x, 0..4));
|
||||
|
||||
// Constrain b_0 to be 4 bits
|
||||
let b_0 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let b_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "b_0 is 4 bits"),
|
||||
b_0,
|
||||
4,
|
||||
)?;
|
||||
|
||||
// Constrain b_3 to be 4 bits
|
||||
let b_3 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let b_3 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "b_3 is 4 bits"),
|
||||
b_3,
|
||||
4,
|
||||
|
@ -607,7 +611,7 @@ impl NoteCommitConfig {
|
|||
let d_3 = value_val.map(|value| bitrange_subset(value, 8..58));
|
||||
|
||||
// Constrain d_2 to be 8 bits
|
||||
let d_2 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let d_2 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "d_2 is 8 bits"),
|
||||
d_2,
|
||||
8,
|
||||
|
@ -638,14 +642,14 @@ impl NoteCommitConfig {
|
|||
let e_1 = rho_val.map(|rho| bitrange_subset(rho, 0..4));
|
||||
|
||||
// Constrain e_0 to be 6 bits.
|
||||
let e_0 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let e_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "e_0 is 6 bits"),
|
||||
e_0,
|
||||
6,
|
||||
)?;
|
||||
|
||||
// Constrain e_1 to be 4 bits.
|
||||
let e_1 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let e_1 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "e_1 is 4 bits"),
|
||||
e_1,
|
||||
4,
|
||||
|
@ -674,7 +678,7 @@ impl NoteCommitConfig {
|
|||
let g_2 = psi_val.map(|psi| bitrange_subset(psi, 9..249));
|
||||
|
||||
// Constrain g_1 to be 9 bits.
|
||||
let g_1 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let g_1 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "g_1 is 9 bits"),
|
||||
g_1,
|
||||
9,
|
||||
|
@ -700,7 +704,7 @@ impl NoteCommitConfig {
|
|||
let h_1 = psi_val.map(|psi| bitrange_subset(psi, 254..255));
|
||||
|
||||
// Constrain h_0 to be 5 bits.
|
||||
let h_0 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let h_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "h_0 is 5 bits"),
|
||||
h_0,
|
||||
5,
|
||||
|
@ -740,7 +744,7 @@ impl NoteCommitConfig {
|
|||
h.clone(),
|
||||
],
|
||||
);
|
||||
let domain = CommitDomain::new(chip, ecc_chip, &SinsemillaCommitDomains::NoteCommit);
|
||||
let domain = CommitDomain::new(chip, ecc_chip, &OrchardCommitDomains::NoteCommit);
|
||||
domain.commit(
|
||||
layouter.namespace(|| "Process NoteCommit inputs"),
|
||||
message,
|
||||
|
@ -844,7 +848,7 @@ impl NoteCommitConfig {
|
|||
let t_p = pallas::Base::from_u128(T_P);
|
||||
a + two_pow_130 - t_p
|
||||
});
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"),
|
||||
a_prime,
|
||||
13,
|
||||
|
@ -883,7 +887,7 @@ impl NoteCommitConfig {
|
|||
b_3 + (two_pow_4 * c) + two_pow_140 - t_p
|
||||
});
|
||||
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose low 140 bits of (b_3 + 2^4 c + 2^140 - t_P)"),
|
||||
b3_c_prime,
|
||||
14,
|
||||
|
@ -922,7 +926,7 @@ impl NoteCommitConfig {
|
|||
// Decompose the low 140 bits of e1_f_prime = e_1 + 2^4 f + 2^140 - t_P,
|
||||
// and output the running sum at the end of it.
|
||||
// If e1_f_prime < 2^140, the running sum will be 0.
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose low 140 bits of (e_1 + 2^4 f + 2^140 - t_P)"),
|
||||
e1_f_prime,
|
||||
14,
|
||||
|
@ -959,7 +963,7 @@ impl NoteCommitConfig {
|
|||
g_1 + (two_pow_9 * g_2) + two_pow_130 - t_p
|
||||
});
|
||||
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose low 130 bits of (g_1 + (2^9)g_2 + 2^130 - t_P)"),
|
||||
g1_g2_prime,
|
||||
13,
|
||||
|
@ -992,14 +996,14 @@ impl NoteCommitConfig {
|
|||
};
|
||||
|
||||
// Range-constrain k_0 to be 9 bits.
|
||||
let k_0 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let k_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "Constrain k_0 to be 9 bits"),
|
||||
k_0,
|
||||
9,
|
||||
)?;
|
||||
|
||||
// Range-constrain k_2 to be 4 bits.
|
||||
let k_2 = self.sinsemilla_config.lookup_config.witness_short_check(
|
||||
let k_2 = self.sinsemilla_config.lookup_config().witness_short_check(
|
||||
layouter.namespace(|| "Constrain k_2 to be 4 bits"),
|
||||
k_2,
|
||||
4,
|
||||
|
@ -1012,7 +1016,7 @@ impl NoteCommitConfig {
|
|||
let two_pow_10 = pallas::Base::from(1 << 10);
|
||||
lsb + two * k_0 + two_pow_10 * k_1
|
||||
});
|
||||
let zs = self.sinsemilla_config.lookup_config.witness_check(
|
||||
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
||||
layouter.namespace(|| "Decompose j = LSB + (2)k_0 + (2^10)k_1"),
|
||||
j,
|
||||
25,
|
||||
|
@ -1460,7 +1464,10 @@ mod tests {
|
|||
sinsemilla::chip::SinsemillaChip,
|
||||
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
||||
},
|
||||
constants::{L_ORCHARD_BASE, L_VALUE, NOTE_COMMITMENT_PERSONALIZATION, T_Q},
|
||||
constants::{
|
||||
fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases,
|
||||
OrchardHashDomains, L_ORCHARD_BASE, L_VALUE, T_Q,
|
||||
},
|
||||
primitives::sinsemilla::CommitDomain,
|
||||
};
|
||||
|
||||
|
@ -1496,7 +1503,7 @@ mod tests {
|
|||
}
|
||||
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
type Config = (NoteCommitConfig, EccConfig);
|
||||
type Config = (NoteCommitConfig, EccConfig<OrchardFixedBases>);
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
|
@ -1543,7 +1550,11 @@ mod tests {
|
|||
];
|
||||
|
||||
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
|
||||
let sinsemilla_config = SinsemillaChip::configure(
|
||||
let sinsemilla_config = SinsemillaChip::<
|
||||
OrchardHashDomains,
|
||||
OrchardCommitDomains,
|
||||
OrchardFixedBases,
|
||||
>::configure(
|
||||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[2],
|
||||
|
@ -1554,7 +1565,12 @@ mod tests {
|
|||
let note_commit_config =
|
||||
NoteCommitConfig::configure(meta, advices, sinsemilla_config);
|
||||
|
||||
let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check);
|
||||
let ecc_config = EccChip::<OrchardFixedBases>::configure(
|
||||
meta,
|
||||
advices,
|
||||
lagrange_coeffs,
|
||||
range_check,
|
||||
);
|
||||
|
||||
(note_commit_config, ecc_config)
|
||||
}
|
||||
|
@ -1567,7 +1583,11 @@ mod tests {
|
|||
let (note_commit_config, ecc_config) = config;
|
||||
|
||||
// Load the Sinsemilla generator lookup table used by the whole circuit.
|
||||
SinsemillaChip::load(note_commit_config.sinsemilla_config.clone(), &mut layouter)?;
|
||||
SinsemillaChip::<
|
||||
OrchardHashDomains,
|
||||
OrchardCommitDomains,
|
||||
OrchardFixedBases,
|
||||
>::load(note_commit_config.sinsemilla_config.clone(), &mut layouter)?;
|
||||
|
||||
// Construct a Sinsemilla chip
|
||||
let sinsemilla_chip =
|
252
src/constants.rs
252
src/constants.rs
|
@ -1,24 +1,13 @@
|
|||
//! Constants used in the Orchard protocol.
|
||||
use arrayvec::ArrayVec;
|
||||
use ff::{Field, PrimeField};
|
||||
use group::Curve;
|
||||
use halo2::arithmetic::lagrange_interpolate;
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pallas,
|
||||
};
|
||||
|
||||
pub mod commit_ivk_r;
|
||||
pub mod note_commit_r;
|
||||
pub mod nullifier_k;
|
||||
pub mod spend_auth_g;
|
||||
pub mod value_commit_r;
|
||||
pub mod value_commit_v;
|
||||
|
||||
pub mod load;
|
||||
pub mod fixed_bases;
|
||||
pub mod sinsemilla;
|
||||
pub mod util;
|
||||
|
||||
pub use load::{NullifierK, OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV};
|
||||
pub use fixed_bases::{NullifierK, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV, H};
|
||||
pub use sinsemilla::{OrchardCommitDomains, OrchardHashDomains};
|
||||
|
||||
/// $\mathsf{MerkleDepth^{Orchard}}$
|
||||
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
|
||||
|
||||
/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
|
||||
/// <https://github.com/zcash/pasta>
|
||||
|
@ -28,12 +17,6 @@ pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
|
|||
/// <https://github.com/zcash/pasta>
|
||||
pub(crate) const T_P: u128 = 45560315531419706090280762371685220353;
|
||||
|
||||
/// $\mathsf{MerkleDepth^{Orchard}}$
|
||||
pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32;
|
||||
|
||||
/// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$
|
||||
pub(crate) const L_ORCHARD_MERKLE: usize = 255;
|
||||
|
||||
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
||||
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
||||
|
||||
|
@ -43,235 +26,14 @@ pub(crate) const L_ORCHARD_SCALAR: usize = 255;
|
|||
/// $\ell_\mathsf{value}$
|
||||
pub(crate) const L_VALUE: usize = 64;
|
||||
|
||||
/// T_PRIME_BITS is the smallest multiple of 10 such that 2^T_PRIME_BITS
|
||||
/// is larger than t_P. t_P is defined in q_P = 2^254 + t_P for the
|
||||
/// Pallas base field.
|
||||
pub(crate) const PALLAS_T_PRIME_BITS: usize = 130;
|
||||
|
||||
/// SWU hash-to-curve personalization for the spending key base point and
|
||||
/// the nullifier base point K^Orchard
|
||||
pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard";
|
||||
|
||||
/// SWU hash-to-curve personalization for the group hash for key diversification
|
||||
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &str = "z.cash:Orchard-gd";
|
||||
|
||||
/// SWU hash-to-curve personalization for the value commitment generator
|
||||
pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv";
|
||||
|
||||
/// SWU hash-to-curve value for the value commitment generator
|
||||
pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v";
|
||||
|
||||
/// SWU hash-to-curve value for the value commitment generator
|
||||
pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r";
|
||||
|
||||
/// SWU hash-to-curve personalization for the note commitment generator
|
||||
pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit";
|
||||
|
||||
/// SWU hash-to-curve personalization for the IVK commitment generator
|
||||
pub const COMMIT_IVK_PERSONALIZATION: &str = "z.cash:Orchard-CommitIvk";
|
||||
|
||||
/// SWU hash-to-curve personalization for the Merkle CRH generator
|
||||
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
|
||||
|
||||
/// Window size for fixed-base scalar multiplication
|
||||
pub const FIXED_BASE_WINDOW_SIZE: usize = 3;
|
||||
|
||||
/// $2^{`FIXED_BASE_WINDOW_SIZE`}$
|
||||
pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of windows for a full-width scalar
|
||||
pub const NUM_WINDOWS: usize =
|
||||
(pallas::Base::NUM_BITS as usize + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of windows for a short signed scalar
|
||||
pub const NUM_WINDOWS_SHORT: usize =
|
||||
(L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// For each fixed base, we calculate its scalar multiples in three-bit windows.
|
||||
/// Each window will have $2^3 = 8$ points.
|
||||
fn compute_window_table<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
|
||||
let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows);
|
||||
|
||||
// Generate window table entries for all windows but the last.
|
||||
// For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B.
|
||||
// Here, w ranges from [0..`num_windows - 1`)
|
||||
for w in 0..(num_windows - 1) {
|
||||
window_table.push(
|
||||
(0..H)
|
||||
.map(|k| {
|
||||
// scalar = (k+2)*(8^w)
|
||||
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::<ArrayVec<C, H>>()
|
||||
.into_inner()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
// Generate window table entries for the last window, w = `num_windows - 1`.
|
||||
// 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::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::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()
|
||||
})
|
||||
.collect::<ArrayVec<C, H>>()
|
||||
.into_inner()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
window_table
|
||||
}
|
||||
|
||||
/// For each window, we interpolate the $x$-coordinate.
|
||||
/// Here, we pre-compute and store the coefficients of the interpolation polynomial.
|
||||
fn compute_lagrange_coeffs<C: CurveAffine>(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(i as u64)).collect();
|
||||
|
||||
let window_table = compute_window_table(base, num_windows);
|
||||
|
||||
window_table
|
||||
.iter()
|
||||
.map(|window_points| {
|
||||
let x_window_points: Vec<_> = window_points
|
||||
.iter()
|
||||
.map(|point| *point.coordinates().unwrap().x())
|
||||
.collect();
|
||||
lagrange_interpolate(&points, &x_window_points)
|
||||
.into_iter()
|
||||
.collect::<ArrayVec<C::Base, H>>()
|
||||
.into_inner()
|
||||
.unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// For each window, $z$ is a field element such that for each point $(x, y)$ in the window:
|
||||
/// - $z + y = u^2$ (some square in the field); and
|
||||
/// - $z - y$ is not a square.
|
||||
/// If successful, return a vector of `(z: u64, us: [C::Base; H])` for each window.
|
||||
///
|
||||
/// This function was used to generate the `z`s and `u`s for the Orchard fixed
|
||||
/// bases. The outputs of this function have been stored as constants, and it
|
||||
/// is not called anywhere in this codebase. However, we keep this function here
|
||||
/// as a utility for those who wish to use it with different parameters.
|
||||
fn find_zs_and_us<C: CurveAffine>(base: C, num_windows: usize) -> Option<Vec<(u64, [C::Base; H])>> {
|
||||
// Closure to find z and u's for one window
|
||||
let find_z_and_us = |window_points: &[C]| {
|
||||
assert_eq!(H, window_points.len());
|
||||
|
||||
let ys: Vec<_> = window_points
|
||||
.iter()
|
||||
.map(|point| *point.coordinates().unwrap().y())
|
||||
.collect();
|
||||
(0..(1000 * (1 << (2 * H)))).find_map(|z| {
|
||||
ys.iter()
|
||||
.map(|&y| {
|
||||
if (-y + C::Base::from(z)).sqrt().is_none().into() {
|
||||
(y + C::Base::from(z)).sqrt().into()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Option<ArrayVec<C::Base, H>>>()
|
||||
.map(|us| (z, us.into_inner().unwrap()))
|
||||
})
|
||||
};
|
||||
|
||||
let window_table = compute_window_table(base, num_windows);
|
||||
window_table
|
||||
.iter()
|
||||
.map(|window_points| find_z_and_us(window_points))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate
|
||||
// for each fixed-base multiple in each window.
|
||||
fn test_lagrange_coeffs<C: CurveAffine>(base: C, num_windows: usize) {
|
||||
let lagrange_coeffs = compute_lagrange_coeffs(base, num_windows);
|
||||
|
||||
// Check first 84 windows, i.e. `k_0, k_1, ..., k_83`
|
||||
for (idx, coeffs) in lagrange_coeffs[0..(num_windows - 1)].iter().enumerate() {
|
||||
// Test each three-bit chunk in this window.
|
||||
for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) {
|
||||
{
|
||||
// Interpolate the x-coordinate using this window's coefficients
|
||||
let interpolated_x = util::evaluate::<C>(bits, coeffs);
|
||||
|
||||
// Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B.
|
||||
let point = base
|
||||
* 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.
|
||||
assert_eq!(x, interpolated_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check last window.
|
||||
for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) {
|
||||
// Interpolate the x-coordinate using the last window's coefficients
|
||||
let interpolated_x = util::evaluate::<C>(bits, &lagrange_coeffs[num_windows - 1]);
|
||||
|
||||
// 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(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 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();
|
||||
|
||||
// Check that the interpolated x-coordinate matches the actual one.
|
||||
assert_eq!(x, interpolated_x);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// Test that the z-values and u-values satisfy the conditions:
|
||||
// 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: 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 = 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ff::PrimeField;
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
|
||||
#[test]
|
||||
// Nodes in the Merkle tree are Pallas base field elements.
|
||||
fn l_orchard_merkle() {
|
||||
assert_eq!(super::L_ORCHARD_MERKLE, pallas::Base::NUM_BITS as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Orchard uses the Pallas base field as its base field.
|
||||
fn l_orchard_base() {
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
//! Orchard fixed bases.
|
||||
use super::{L_ORCHARD_SCALAR, L_VALUE};
|
||||
use crate::circuit::gadget::ecc::{
|
||||
chip::{BaseFieldElem, FixedPoint, FullScalar, ShortScalar},
|
||||
FixedPoints,
|
||||
};
|
||||
|
||||
use pasta_curves::pallas;
|
||||
|
||||
pub mod commit_ivk_r;
|
||||
pub mod note_commit_r;
|
||||
pub mod nullifier_k;
|
||||
pub mod spend_auth_g;
|
||||
pub mod value_commit_r;
|
||||
pub mod value_commit_v;
|
||||
|
||||
/// SWU hash-to-curve personalization for the spending key base point and
|
||||
/// the nullifier base point K^Orchard
|
||||
pub const ORCHARD_PERSONALIZATION: &str = "z.cash:Orchard";
|
||||
|
||||
/// SWU hash-to-curve personalization for the value commitment generator
|
||||
pub const VALUE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-cv";
|
||||
|
||||
/// SWU hash-to-curve value for the value commitment generator
|
||||
pub const VALUE_COMMITMENT_V_BYTES: [u8; 1] = *b"v";
|
||||
|
||||
/// SWU hash-to-curve value for the value commitment generator
|
||||
pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r";
|
||||
|
||||
/// SWU hash-to-curve personalization for the note commitment generator
|
||||
pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit";
|
||||
|
||||
/// SWU hash-to-curve personalization for the IVK commitment generator
|
||||
pub const COMMIT_IVK_PERSONALIZATION: &str = "z.cash:Orchard-CommitIvk";
|
||||
|
||||
/// Window size for fixed-base scalar multiplication
|
||||
pub const FIXED_BASE_WINDOW_SIZE: usize = 3;
|
||||
|
||||
/// $2^{`FIXED_BASE_WINDOW_SIZE`}$
|
||||
pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of windows for a full-width scalar
|
||||
pub const NUM_WINDOWS: usize =
|
||||
(L_ORCHARD_SCALAR + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
/// Number of windows for a short signed scalar
|
||||
pub const NUM_WINDOWS_SHORT: usize =
|
||||
(L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
// A sum type for both full-width and short bases. This enables us to use the
|
||||
// shared functionality of full-width and short fixed-base scalar multiplication.
|
||||
pub enum OrchardFixedBases {
|
||||
Full(OrchardFixedBasesFull),
|
||||
NullifierK,
|
||||
ValueCommitV,
|
||||
}
|
||||
|
||||
impl From<OrchardFixedBasesFull> for OrchardFixedBases {
|
||||
fn from(full_width_base: OrchardFixedBasesFull) -> Self {
|
||||
Self::Full(full_width_base)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValueCommitV> for OrchardFixedBases {
|
||||
fn from(_value_commit_v: ValueCommitV) -> Self {
|
||||
Self::ValueCommitV
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NullifierK> for OrchardFixedBases {
|
||||
fn from(_nullifier_k: NullifierK) -> Self {
|
||||
Self::NullifierK
|
||||
}
|
||||
}
|
||||
|
||||
/// The Orchard fixed bases used in scalar mul with full-width scalars.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum OrchardFixedBasesFull {
|
||||
CommitIvkR,
|
||||
NoteCommitR,
|
||||
ValueCommitR,
|
||||
SpendAuthG,
|
||||
}
|
||||
|
||||
/// NullifierK is used in scalar mul with a base field element.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct NullifierK;
|
||||
|
||||
/// ValueCommitV is used in scalar mul with a short signed scalar.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ValueCommitV;
|
||||
|
||||
impl FixedPoints<pallas::Affine> for OrchardFixedBases {
|
||||
type FullScalar = OrchardFixedBasesFull;
|
||||
type Base = NullifierK;
|
||||
type ShortScalar = ValueCommitV;
|
||||
}
|
||||
|
||||
impl FixedPoint<pallas::Affine> for OrchardFixedBasesFull {
|
||||
type ScalarKind = FullScalar;
|
||||
|
||||
fn generator(&self) -> pallas::Affine {
|
||||
match self {
|
||||
Self::CommitIvkR => commit_ivk_r::generator(),
|
||||
Self::NoteCommitR => note_commit_r::generator(),
|
||||
Self::ValueCommitR => value_commit_r::generator(),
|
||||
Self::SpendAuthG => spend_auth_g::generator(),
|
||||
}
|
||||
}
|
||||
|
||||
fn u(&self) -> Vec<[[u8; 32]; H]> {
|
||||
match self {
|
||||
Self::CommitIvkR => commit_ivk_r::U.to_vec(),
|
||||
Self::NoteCommitR => note_commit_r::U.to_vec(),
|
||||
Self::ValueCommitR => value_commit_r::U.to_vec(),
|
||||
Self::SpendAuthG => spend_auth_g::U.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
fn z(&self) -> Vec<u64> {
|
||||
match self {
|
||||
Self::CommitIvkR => commit_ivk_r::Z.to_vec(),
|
||||
Self::NoteCommitR => note_commit_r::Z.to_vec(),
|
||||
Self::ValueCommitR => value_commit_r::Z.to_vec(),
|
||||
Self::SpendAuthG => spend_auth_g::Z.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedPoint<pallas::Affine> for NullifierK {
|
||||
type ScalarKind = BaseFieldElem;
|
||||
|
||||
fn generator(&self) -> pallas::Affine {
|
||||
nullifier_k::generator()
|
||||
}
|
||||
|
||||
fn u(&self) -> Vec<[[u8; 32]; H]> {
|
||||
nullifier_k::U.to_vec()
|
||||
}
|
||||
|
||||
fn z(&self) -> Vec<u64> {
|
||||
nullifier_k::Z.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedPoint<pallas::Affine> for ValueCommitV {
|
||||
type ScalarKind = ShortScalar;
|
||||
|
||||
fn generator(&self) -> pallas::Affine {
|
||||
value_commit_v::generator()
|
||||
}
|
||||
|
||||
fn u(&self) -> Vec<[[u8; 32]; H]> {
|
||||
value_commit_v::U_SHORT.to_vec()
|
||||
}
|
||||
|
||||
fn z(&self) -> Vec<u64> {
|
||||
value_commit_v::Z_SHORT.to_vec()
|
||||
}
|
||||
}
|
|
@ -2928,10 +2928,9 @@ pub fn generator() -> pallas::Affine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
test_lagrange_coeffs, test_zs_and_us, COMMIT_IVK_PERSONALIZATION, NUM_WINDOWS,
|
||||
};
|
||||
use super::super::{COMMIT_IVK_PERSONALIZATION, NUM_WINDOWS};
|
||||
use super::*;
|
||||
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
|
||||
use crate::primitives::sinsemilla::CommitDomain;
|
||||
use group::Curve;
|
||||
use pasta_curves::{arithmetic::CurveAffine, pallas};
|
|
@ -2928,11 +2928,11 @@ pub fn generator() -> pallas::Affine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
test_lagrange_coeffs, test_zs_and_us, NOTE_COMMITMENT_PERSONALIZATION, NUM_WINDOWS,
|
||||
};
|
||||
use super::super::{NOTE_COMMITMENT_PERSONALIZATION, NUM_WINDOWS};
|
||||
use super::*;
|
||||
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
|
||||
use crate::primitives::sinsemilla::CommitDomain;
|
||||
|
||||
use group::Curve;
|
||||
use pasta_curves::{arithmetic::CurveAffine, pallas};
|
||||
|
|
@ -2927,10 +2927,9 @@ pub fn generator() -> pallas::Affine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, ORCHARD_PERSONALIZATION,
|
||||
};
|
||||
use super::super::{NUM_WINDOWS, ORCHARD_PERSONALIZATION};
|
||||
use super::*;
|
||||
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
|
||||
use group::Curve;
|
||||
use pasta_curves::{arithmetic::CurveExt, pallas};
|
||||
|
|
@ -2929,10 +2929,9 @@ pub fn generator() -> pallas::Affine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, ORCHARD_PERSONALIZATION,
|
||||
};
|
||||
use super::super::{NUM_WINDOWS, ORCHARD_PERSONALIZATION};
|
||||
use super::*;
|
||||
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
|
||||
use group::Curve;
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, CurveExt},
|
|
@ -2929,10 +2929,9 @@ pub fn generator() -> pallas::Affine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, VALUE_COMMITMENT_PERSONALIZATION,
|
||||
};
|
||||
use super::super::{NUM_WINDOWS, VALUE_COMMITMENT_PERSONALIZATION};
|
||||
use super::*;
|
||||
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
|
||||
use group::Curve;
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, CurveExt},
|
|
@ -782,10 +782,9 @@ pub fn generator() -> pallas::Affine {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{
|
||||
test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION,
|
||||
};
|
||||
use super::super::{NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION};
|
||||
use super::*;
|
||||
use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us};
|
||||
use group::Curve;
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, CurveExt},
|
||||
|
@ -803,13 +802,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange_coeffs_short() {
|
||||
fn lagrange_coeffs() {
|
||||
let base = super::generator();
|
||||
test_lagrange_coeffs(base, NUM_WINDOWS_SHORT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn z_short() {
|
||||
fn z() {
|
||||
let base = super::generator();
|
||||
test_zs_and_us(base, &Z_SHORT, &U_SHORT, NUM_WINDOWS_SHORT);
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
//! Sinsemilla generators
|
||||
use super::{OrchardFixedBases, OrchardFixedBasesFull};
|
||||
use crate::circuit::gadget::sinsemilla::{CommitDomains, HashDomains};
|
||||
use crate::spec::i2lebsp;
|
||||
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pallas,
|
||||
};
|
||||
|
||||
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
|
||||
pub const K: usize = 10;
|
||||
|
||||
/// $\frac{1}{2^K}$
|
||||
pub const INV_TWO_POW_K: [u8; 32] = [
|
||||
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 240, 63,
|
||||
];
|
||||
|
||||
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
|
||||
/// of Pallas.
|
||||
pub const C: usize = 253;
|
||||
|
||||
/// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$
|
||||
pub(crate) const L_ORCHARD_MERKLE: usize = 255;
|
||||
|
||||
/// SWU hash-to-curve personalization for the Merkle CRH generator
|
||||
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
|
||||
|
||||
/// Generator used in SinsemillaHashToPoint for note commitment
|
||||
pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
93, 116, 168, 64, 9, 186, 14, 50, 42, 221, 70, 253, 90, 15, 150, 197, 93, 237, 176, 121,
|
||||
180, 242, 159, 247, 13, 205, 251, 86, 160, 7, 128, 23,
|
||||
],
|
||||
[
|
||||
99, 172, 73, 115, 90, 10, 39, 135, 158, 94, 219, 129, 136, 18, 34, 136, 44, 201, 244, 110,
|
||||
217, 194, 190, 78, 131, 112, 198, 138, 147, 88, 160, 50,
|
||||
],
|
||||
);
|
||||
|
||||
/// Generator used in SinsemillaHashToPoint for IVK commitment
|
||||
pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
242, 130, 15, 121, 146, 47, 203, 107, 50, 162, 40, 81, 36, 204, 27, 66, 250, 65, 162, 90,
|
||||
184, 129, 204, 125, 17, 200, 169, 74, 241, 12, 188, 5,
|
||||
],
|
||||
[
|
||||
190, 222, 173, 207, 206, 229, 90, 190, 241, 165, 109, 201, 29, 53, 196, 70, 75, 5, 222, 32,
|
||||
70, 7, 89, 239, 230, 190, 26, 212, 246, 76, 1, 27,
|
||||
],
|
||||
);
|
||||
|
||||
/// Generator used in SinsemillaHashToPoint for Merkle collision-resistant hash
|
||||
pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
160, 198, 41, 127, 249, 199, 185, 248, 112, 16, 141, 192, 85, 185, 190, 201, 153, 14, 137,
|
||||
239, 90, 54, 15, 160, 185, 24, 168, 99, 150, 210, 22, 22,
|
||||
],
|
||||
[
|
||||
98, 234, 242, 37, 206, 174, 233, 134, 150, 21, 116, 5, 234, 150, 28, 226, 121, 89, 163, 79,
|
||||
62, 242, 196, 45, 153, 32, 175, 227, 163, 66, 134, 53,
|
||||
],
|
||||
);
|
||||
|
||||
pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 {
|
||||
assert!(bits.len() == K);
|
||||
bits.iter()
|
||||
.enumerate()
|
||||
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
||||
}
|
||||
|
||||
/// The sequence of K bits in little-endian order representing an integer
|
||||
/// up to `2^K` - 1.
|
||||
pub(crate) fn i2lebsp_k(int: usize) -> [bool; K] {
|
||||
assert!(int < (1 << K));
|
||||
i2lebsp(int as u64)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum OrchardHashDomains {
|
||||
NoteCommit,
|
||||
CommitIvk,
|
||||
MerkleCrh,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl HashDomains<pallas::Affine> for OrchardHashDomains {
|
||||
fn Q(&self) -> pallas::Affine {
|
||||
match self {
|
||||
OrchardHashDomains::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(),
|
||||
)
|
||||
.unwrap(),
|
||||
OrchardHashDomains::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(),
|
||||
)
|
||||
.unwrap(),
|
||||
OrchardHashDomains::MerkleCrh => pallas::Affine::from_xy(
|
||||
pallas::Base::from_bytes(&Q_MERKLE_CRH.0).unwrap(),
|
||||
pallas::Base::from_bytes(&Q_MERKLE_CRH.1).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum OrchardCommitDomains {
|
||||
NoteCommit,
|
||||
CommitIvk,
|
||||
}
|
||||
|
||||
impl CommitDomains<pallas::Affine, OrchardFixedBases, OrchardHashDomains> for OrchardCommitDomains {
|
||||
fn r(&self) -> OrchardFixedBasesFull {
|
||||
match self {
|
||||
Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR,
|
||||
Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR,
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_domain(&self) -> OrchardHashDomains {
|
||||
match self {
|
||||
Self::NoteCommit => OrchardHashDomains::NoteCommit,
|
||||
Self::CommitIvk => OrchardHashDomains::CommitIvk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::constants::{
|
||||
fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION},
|
||||
sinsemilla::MERKLE_CRH_PERSONALIZATION,
|
||||
};
|
||||
use crate::primitives::sinsemilla::{CommitDomain, HashDomain};
|
||||
use group::{ff::PrimeField, Curve};
|
||||
use halo2::arithmetic::CurveAffine;
|
||||
use halo2::pasta::pallas;
|
||||
use rand::{self, rngs::OsRng, Rng};
|
||||
|
||||
#[test]
|
||||
// Nodes in the Merkle tree are Pallas base field elements.
|
||||
fn l_orchard_merkle() {
|
||||
assert_eq!(super::L_ORCHARD_MERKLE, pallas::Base::NUM_BITS as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lebs2ip_k_round_trip() {
|
||||
let mut rng = OsRng;
|
||||
{
|
||||
let int = rng.gen_range(0..(1 << K));
|
||||
assert_eq!(lebs2ip_k(&i2lebsp_k(int)) as usize, int);
|
||||
}
|
||||
|
||||
assert_eq!(lebs2ip_k(&i2lebsp_k(0)) as usize, 0);
|
||||
assert_eq!(lebs2ip_k(&i2lebsp_k((1 << K) - 1)) as usize, (1 << K) - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2lebsp_k_round_trip() {
|
||||
{
|
||||
let bitstring = (0..K).map(|_| rand::random()).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [false; K];
|
||||
assert_eq!(
|
||||
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [true; K];
|
||||
assert_eq!(
|
||||
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn q_note_commitment_m() {
|
||||
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
|
||||
let point = domain.Q();
|
||||
let coords = point.to_affine().coordinates().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*coords.x(),
|
||||
pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
*coords.y(),
|
||||
pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn q_commit_ivk_m() {
|
||||
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
|
||||
let point = domain.Q();
|
||||
let coords = point.to_affine().coordinates().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*coords.x(),
|
||||
pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
*coords.y(),
|
||||
pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn q_merkle_crh() {
|
||||
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
|
||||
let point = domain.Q();
|
||||
let coords = point.to_affine().coordinates().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*coords.x(),
|
||||
pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
*coords.y(),
|
||||
pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inv_two_pow_k() {
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -1,47 +1,3 @@
|
|||
use ff::{Field, PrimeFieldBits};
|
||||
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`
|
||||
/// is a `w`-bit value, and `scalar = k_0 + k_1 * w + k_n * w^n`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// We are returning a `Vec<u8>` which means the window size is limited to
|
||||
/// <= 8 bits.
|
||||
pub fn decompose_word<F: PrimeFieldBits>(
|
||||
word: &F,
|
||||
word_num_bits: usize,
|
||||
window_num_bits: usize,
|
||||
) -> Vec<u8> {
|
||||
assert!(window_num_bits <= 8);
|
||||
|
||||
// Pad bits to multiple of window_num_bits
|
||||
let padding = (window_num_bits - (word_num_bits % window_num_bits)) % window_num_bits;
|
||||
let bits: Vec<bool> = word
|
||||
.to_le_bits()
|
||||
.into_iter()
|
||||
.take(word_num_bits)
|
||||
.chain(std::iter::repeat(false).take(padding))
|
||||
.collect();
|
||||
assert_eq!(bits.len(), word_num_bits + padding);
|
||||
|
||||
bits.chunks_exact(window_num_bits)
|
||||
.map(|chunk| chunk.iter().rev().fold(0, |acc, b| (acc << 1) + (*b as u8)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Evaluate y = f(x) given the coefficients of f(x)
|
||||
pub fn evaluate<C: CurveAffine>(x: u8, coeffs: &[C::Base]) -> C::Base {
|
||||
let x = C::Base::from(x as u64);
|
||||
coeffs
|
||||
.iter()
|
||||
.rev()
|
||||
.cloned()
|
||||
.reduce(|acc, coeff| acc * x + coeff)
|
||||
.unwrap_or_else(C::Base::zero)
|
||||
}
|
||||
|
||||
/// Takes in an FnMut closure and returns a constant-length array with elements of
|
||||
/// type `Output`.
|
||||
pub fn gen_const_array<Output: Copy + Default, const LEN: usize>(
|
||||
|
@ -60,49 +16,3 @@ pub(crate) fn gen_const_array_with_default<Output: Copy, const LEN: usize>(
|
|||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::decompose_word;
|
||||
use ff::PrimeField;
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
use proptest::prelude::*;
|
||||
use std::convert::TryInto;
|
||||
use std::iter;
|
||||
|
||||
prop_compose! {
|
||||
fn arb_scalar()(bytes in prop::array::uniform32(0u8..)) -> pallas::Scalar {
|
||||
// Instead of rejecting out-of-range bytes, let's reduce them.
|
||||
let mut buf = [0; 64];
|
||||
buf[..32].copy_from_slice(&bytes);
|
||||
pallas::Scalar::from_bytes_wide(&buf)
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_decompose_word(
|
||||
scalar in arb_scalar(),
|
||||
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);
|
||||
|
||||
// Flatten bits
|
||||
let bits = decomposed
|
||||
.iter()
|
||||
.flat_map(|window| (0..window_num_bits).map(move |mask| (window & (1 << mask)) != 0));
|
||||
|
||||
// Ensure this decomposition contains 256 or fewer set bits.
|
||||
assert!(!bits.clone().skip(32*8).any(|b| b));
|
||||
|
||||
// Pad or truncate bits to 32 bytes
|
||||
let bits: Vec<bool> = bits.chain(iter::repeat(false)).take(32*8).collect();
|
||||
|
||||
let bytes: Vec<u8> = 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_repr(bytes.try_into().unwrap()).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use pasta_curves::pallas;
|
|||
use subtle::{ConstantTimeEq, CtOption};
|
||||
|
||||
use crate::{
|
||||
constants::{L_ORCHARD_BASE, NOTE_COMMITMENT_PERSONALIZATION},
|
||||
constants::{fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, L_ORCHARD_BASE},
|
||||
primitives::sinsemilla,
|
||||
spec::extract_p,
|
||||
value::NoteValue,
|
||||
|
|
|
@ -1,19 +1,37 @@
|
|||
//! The Sinsemilla hash function and commitment scheme.
|
||||
|
||||
use group::Wnaf;
|
||||
use group::{Curve, Wnaf};
|
||||
use halo2::arithmetic::{CurveAffine, CurveExt};
|
||||
use pasta_curves::pallas;
|
||||
use subtle::CtOption;
|
||||
|
||||
use crate::spec::{extract_p_bottom, i2lebsp};
|
||||
|
||||
mod addition;
|
||||
use self::addition::IncompletePoint;
|
||||
|
||||
mod constants;
|
||||
mod sinsemilla_s;
|
||||
pub use constants::*;
|
||||
pub(crate) use sinsemilla_s::*;
|
||||
pub use sinsemilla_s::SINSEMILLA_S;
|
||||
|
||||
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
|
||||
pub const K: usize = 10;
|
||||
|
||||
/// $\frac{1}{2^K}$
|
||||
pub const INV_TWO_POW_K: [u8; 32] = [
|
||||
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 240, 63,
|
||||
];
|
||||
|
||||
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
|
||||
/// of Pallas.
|
||||
pub const C: usize = 253;
|
||||
|
||||
// Sinsemilla Q generators
|
||||
|
||||
/// SWU hash-to-curve personalization for Sinsemilla $Q$ generators.
|
||||
pub const Q_PERSONALIZATION: &str = "z.cash:SinsemillaQ";
|
||||
|
||||
// Sinsemilla S generators
|
||||
|
||||
/// SWU hash-to-curve personalization for Sinsemilla $S$ generators.
|
||||
pub const S_PERSONALIZATION: &str = "z.cash:SinsemillaS";
|
||||
|
||||
pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 {
|
||||
assert!(bits.len() == K);
|
||||
|
@ -22,11 +40,18 @@ pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 {
|
|||
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
||||
}
|
||||
|
||||
/// The sequence of K bits in little-endian order representing an integer
|
||||
/// up to `2^K` - 1.
|
||||
pub(crate) fn i2lebsp_k(int: usize) -> [bool; K] {
|
||||
assert!(int < (1 << K));
|
||||
i2lebsp(int as u64)
|
||||
/// Coordinate extractor for Pallas.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
|
||||
///
|
||||
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
|
||||
fn extract_p_bottom(point: CtOption<pallas::Point>) -> CtOption<pallas::Base> {
|
||||
point.map(|p| {
|
||||
p.to_affine()
|
||||
.coordinates()
|
||||
.map(|c| *c.x())
|
||||
.unwrap_or_else(pallas::Base::zero)
|
||||
})
|
||||
}
|
||||
|
||||
/// Pads the given iterator (which MUST have length $\leq K * C$) with zero-bits to a
|
||||
|
@ -196,12 +221,19 @@ impl CommitDomain {
|
|||
pub(crate) fn R(&self) -> pallas::Point {
|
||||
self.R
|
||||
}
|
||||
|
||||
/// Returns the Sinsemilla $Q$ constant for this domain.
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn Q(&self) -> pallas::Point {
|
||||
self.M.Q
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{i2lebsp_k, lebs2ip_k, Pad, K};
|
||||
use rand::{self, rngs::OsRng, Rng};
|
||||
use super::{Pad, K};
|
||||
use pasta_curves::{arithmetic::CurveExt, pallas};
|
||||
|
||||
#[test]
|
||||
fn pad() {
|
||||
|
@ -242,41 +274,20 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn lebs2ip_k_round_trip() {
|
||||
let mut rng = OsRng;
|
||||
{
|
||||
let int = rng.gen_range(0..(1 << K));
|
||||
assert_eq!(lebs2ip_k(&i2lebsp_k(int)) as usize, int);
|
||||
}
|
||||
fn sinsemilla_s() {
|
||||
use super::sinsemilla_s::SINSEMILLA_S;
|
||||
use group::Curve;
|
||||
use pasta_curves::arithmetic::CurveAffine;
|
||||
|
||||
assert_eq!(lebs2ip_k(&i2lebsp_k(0)) as usize, 0);
|
||||
assert_eq!(lebs2ip_k(&i2lebsp_k((1 << K) - 1)) as usize, (1 << K) - 1);
|
||||
}
|
||||
let hasher = pallas::Point::hash_to_curve(super::S_PERSONALIZATION);
|
||||
|
||||
#[test]
|
||||
fn i2lebsp_k_round_trip() {
|
||||
{
|
||||
let bitstring = (0..K).map(|_| rand::random()).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [false; K];
|
||||
assert_eq!(
|
||||
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let bitstring = [true; K];
|
||||
assert_eq!(
|
||||
i2lebsp_k(lebs2ip_k(&bitstring) as usize).to_vec(),
|
||||
bitstring
|
||||
);
|
||||
for j in 0..(1u32 << K) {
|
||||
let computed = {
|
||||
let point = hasher(&j.to_le_bytes()).to_affine().coordinates().unwrap();
|
||||
(*point.x(), *point.y())
|
||||
};
|
||||
let actual = SINSEMILLA_S[j as usize];
|
||||
assert_eq!(computed, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
//! Sinsemilla generators
|
||||
|
||||
/// Number of bits of each message piece in $\mathsf{SinsemillaHashToPoint}$
|
||||
pub const K: usize = 10;
|
||||
|
||||
/// $\frac{1}{2^K}$
|
||||
pub const INV_TWO_POW_K: [u8; 32] = [
|
||||
1, 0, 192, 196, 160, 229, 70, 82, 221, 165, 74, 202, 85, 7, 62, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 240, 63,
|
||||
];
|
||||
|
||||
/// The largest integer such that $2^c \leq (r_P - 1) / 2$, where $r_P$ is the order
|
||||
/// of Pallas.
|
||||
pub const C: usize = 253;
|
||||
|
||||
// Sinsemilla Q generators
|
||||
|
||||
/// SWU hash-to-curve personalization for Sinsemilla $Q$ generators.
|
||||
pub const Q_PERSONALIZATION: &str = "z.cash:SinsemillaQ";
|
||||
|
||||
/// Generator used in SinsemillaHashToPoint for note commitment
|
||||
pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
93, 116, 168, 64, 9, 186, 14, 50, 42, 221, 70, 253, 90, 15, 150, 197, 93, 237, 176, 121,
|
||||
180, 242, 159, 247, 13, 205, 251, 86, 160, 7, 128, 23,
|
||||
],
|
||||
[
|
||||
99, 172, 73, 115, 90, 10, 39, 135, 158, 94, 219, 129, 136, 18, 34, 136, 44, 201, 244, 110,
|
||||
217, 194, 190, 78, 131, 112, 198, 138, 147, 88, 160, 50,
|
||||
],
|
||||
);
|
||||
|
||||
/// Generator used in SinsemillaHashToPoint for IVK commitment
|
||||
pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
242, 130, 15, 121, 146, 47, 203, 107, 50, 162, 40, 81, 36, 204, 27, 66, 250, 65, 162, 90,
|
||||
184, 129, 204, 125, 17, 200, 169, 74, 241, 12, 188, 5,
|
||||
],
|
||||
[
|
||||
190, 222, 173, 207, 206, 229, 90, 190, 241, 165, 109, 201, 29, 53, 196, 70, 75, 5, 222, 32,
|
||||
70, 7, 89, 239, 230, 190, 26, 212, 246, 76, 1, 27,
|
||||
],
|
||||
);
|
||||
|
||||
/// Generator used in SinsemillaHashToPoint for Merkle collision-resistant hash
|
||||
pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = (
|
||||
[
|
||||
160, 198, 41, 127, 249, 199, 185, 248, 112, 16, 141, 192, 85, 185, 190, 201, 153, 14, 137,
|
||||
239, 90, 54, 15, 160, 185, 24, 168, 99, 150, 210, 22, 22,
|
||||
],
|
||||
[
|
||||
98, 234, 242, 37, 206, 174, 233, 134, 150, 21, 116, 5, 234, 150, 28, 226, 121, 89, 163, 79,
|
||||
62, 242, 196, 45, 153, 32, 175, 227, 163, 66, 134, 53,
|
||||
],
|
||||
);
|
||||
|
||||
// Sinsemilla S generators
|
||||
|
||||
/// SWU hash-to-curve personalization for Sinsemilla $S$ generators.
|
||||
pub const S_PERSONALIZATION: &str = "z.cash:SinsemillaS";
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{CommitDomain, HashDomain};
|
||||
use super::*;
|
||||
use crate::constants::{
|
||||
COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION,
|
||||
};
|
||||
use group::{ff::PrimeField, Curve};
|
||||
use halo2::arithmetic::{CurveAffine, CurveExt};
|
||||
use halo2::pasta::pallas;
|
||||
|
||||
#[test]
|
||||
fn sinsemilla_s() {
|
||||
use super::super::sinsemilla_s::SINSEMILLA_S;
|
||||
let hasher = pallas::Point::hash_to_curve(S_PERSONALIZATION);
|
||||
|
||||
for j in 0..(1u32 << K) {
|
||||
let computed = {
|
||||
let point = hasher(&j.to_le_bytes()).to_affine().coordinates().unwrap();
|
||||
(*point.x(), *point.y())
|
||||
};
|
||||
let actual = SINSEMILLA_S[j as usize];
|
||||
assert_eq!(computed, actual);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn q_note_commitment_m() {
|
||||
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
|
||||
let point = domain.M.Q;
|
||||
let coords = point.to_affine().coordinates().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*coords.x(),
|
||||
pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
*coords.y(),
|
||||
pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn q_commit_ivk_m() {
|
||||
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
|
||||
let point = domain.M.Q;
|
||||
let coords = point.to_affine().coordinates().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*coords.x(),
|
||||
pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
*coords.y(),
|
||||
pallas::Base::from_repr(Q_COMMIT_IVK_M_GENERATOR.1).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn q_merkle_crh() {
|
||||
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
|
||||
let point = domain.Q;
|
||||
let coords = point.to_affine().coordinates().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*coords.x(),
|
||||
pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
*coords.y(),
|
||||
pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inv_two_pow_k() {
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -12,8 +12,8 @@ use subtle::{ConditionallySelectable, CtOption};
|
|||
|
||||
use crate::{
|
||||
constants::{
|
||||
util::gen_const_array, COMMIT_IVK_PERSONALIZATION, KEY_DIVERSIFICATION_PERSONALIZATION,
|
||||
L_ORCHARD_BASE,
|
||||
fixed_bases::COMMIT_IVK_PERSONALIZATION, util::gen_const_array,
|
||||
KEY_DIVERSIFICATION_PERSONALIZATION, L_ORCHARD_BASE,
|
||||
},
|
||||
primitives::{poseidon, sinsemilla},
|
||||
};
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
use crate::{
|
||||
constants::{
|
||||
util::gen_const_array_with_default, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION,
|
||||
sinsemilla::{i2lebsp_k, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
|
||||
util::gen_const_array_with_default,
|
||||
MERKLE_DEPTH_ORCHARD,
|
||||
},
|
||||
note::commitment::ExtractedNoteCommitment,
|
||||
primitives::sinsemilla::{i2lebsp_k, HashDomain},
|
||||
primitives::sinsemilla::HashDomain,
|
||||
};
|
||||
use incrementalmerkletree::{Altitude, Hashable};
|
||||
use pasta_curves::pallas;
|
||||
|
|
|
@ -51,7 +51,7 @@ use rand::RngCore;
|
|||
use subtle::CtOption;
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
constants::fixed_bases::{
|
||||
VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
|
||||
},
|
||||
primitives::redpallas::{self, Binding},
|
||||
|
|
Loading…
Reference in New Issue