ecc: Introduce FixedPoints trait with Full, Base, Short associated types.

This commit is contained in:
therealyingtong 2021-08-19 14:59:39 +08:00
parent 5f8716d66a
commit 06ad0b6925
18 changed files with 698 additions and 457 deletions

View File

@ -13,9 +13,8 @@ use rand::RngCore;
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,
@ -41,7 +40,7 @@ use gadget::{
},
poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig, SinsemillaHashDomains},
chip::{SinsemillaChip, SinsemillaConfig},
merkle::{
chip::{MerkleChip, MerkleConfig},
MerklePath,
@ -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>(
@ -397,7 +399,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
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())?
};

View File

@ -9,26 +9,31 @@ use crate::{
circuit::gadget::{
ecc::{chip::EccChip, X},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaCommitDomains, SinsemillaConfig},
chip::{SinsemillaChip, SinsemillaConfig},
CommitDomain, Message, MessagePiece,
},
utilities::{bitrange_subset, bool_check},
},
constants::T_P,
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();
@ -221,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).
//
@ -328,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)?
};
@ -635,7 +641,10 @@ mod tests {
sinsemilla::chip::SinsemillaChip,
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{COMMIT_IVK_PERSONALIZATION, L_ORCHARD_BASE, T_Q},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
COMMIT_IVK_PERSONALIZATION, L_ORCHARD_BASE, T_Q,
},
primitives::sinsemilla::CommitDomain,
};
use group::ff::{Field, PrimeFieldBits};
@ -662,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 {
@ -708,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],
@ -720,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)
}
@ -733,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 =

View File

@ -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())
}

View File

@ -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,34 @@ 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>
pub struct FixedPoint<C: CurveAffine, EccChip: EccInstructions<C>> {
chip: EccChip,
inner: <EccChip::FixedPoints as FixedPoints<C>>::FullScalar,
}
/// 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::FixedPoints,
inner: <EccChip::FixedPoints as FixedPoints<C>>::Base,
}
impl<C: CurveAffine, EccChip> FixedPoint<C, EccChip>
/// 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::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 +425,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 +450,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 +483,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 +504,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 +512,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 +548,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(

View File

@ -1,9 +1,9 @@
use super::EccInstructions;
use super::{EccInstructions, FixedPoints};
use crate::{
circuit::gadget::utilities::{
lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions,
},
constants::{self, NullifierK, OrchardFixedBasesFull, ValueCommitV},
constants,
primitives::sinsemilla,
};
use arrayvec::ArrayVec;
@ -131,7 +131,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 +145,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 +158,26 @@ 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,
/// 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 {
fn generator(&self) -> C;
fn u(&self) -> Vec<[[u8; 32]; constants::H]>;
fn z(&self) -> Vec<u64>;
fn lagrange_coeffs(&self) -> Vec<[C::Base; constants::H]>;
}
impl Chip<pallas::Base> for EccChip {
type Config = EccConfig;
/// 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 +189,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 +227,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 +239,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,
@ -310,16 +326,19 @@ 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>,
<Fixed as FixedPoints<pallas::Affine>>::FullScalar: FixedPoint<pallas::Affine>,
<Fixed as FixedPoints<pallas::Affine>>::ShortScalar: FixedPoint<pallas::Affine>,
{
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 +432,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 +446,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 +460,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,
)
}
}

View File

@ -481,11 +481,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];

View File

@ -1,12 +1,11 @@
use super::{
add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort,
add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort, FixedPoint,
NonIdentityEccPoint,
};
use crate::circuit::gadget::utilities::decompose_running_sum::RunningSumConfig;
use crate::constants::{
self,
load::{NullifierK, OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV, WindowUs},
};
use crate::constants;
use std::marker::PhantomData;
use group::{ff::PrimeField, Curve};
use halo2::{
@ -31,53 +30,8 @@ lazy_static! {
static ref H_BASE: pallas::Base = pallas::Base::from(constants::H as u64);
}
// A sum type for both full-width and short bases. This enables us to use the
// 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 {
pub struct Config<FixedPoints: super::FixedPoints<pallas::Affine>> {
running_sum_config: RunningSumConfig<pallas::Base, { constants::FIXED_BASE_WINDOW_SIZE }>,
// The fixed Lagrange interpolation coefficients for `x_p`.
lagrange_coeffs: [Column<Fixed>; constants::H],
@ -96,9 +50,10 @@ 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>,
@ -126,6 +81,7 @@ impl Config {
u,
add_config,
add_incomplete_config,
_marker: PhantomData,
};
// Check relationships between this config and `add_config`.
@ -231,64 +187,41 @@ 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 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);
// Assign fixed columns for given fixed base
for window in 0..NUM_WINDOWS {
@ -305,13 +238,7 @@ impl Config {
},
self.lagrange_coeffs[k],
window + offset,
|| {
if constants.as_ref().is_none() {
constants = Some(build_constants());
}
let lagrange_coeffs = &constants.as_ref().unwrap().0;
Ok(lagrange_coeffs[window].0[k])
},
|| Ok(lagrange_coeffs[window][k]),
)?;
}
@ -320,27 +247,25 @@ impl Config {
|| format!("z-value for window: {:?}", window),
self.fixed_z,
window + offset,
|| {
let z = &constants.as_ref().unwrap().1;
Ok(z[window])
},
|| Ok(pallas::Base::from_u64(z[window])),
)?;
}
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 +301,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 +320,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 +342,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 +353,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,

View File

@ -1,4 +1,4 @@
use super::super::{EccBaseFieldElemFixed, EccPoint, NullifierK};
use super::super::{EccBaseFieldElemFixed, EccPoint, FixedPoints};
use super::H_BASE;
use crate::{
@ -19,19 +19,19 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
use std::convert::TryInto;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Config {
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| {
@ -186,11 +189,11 @@ impl Config {
let (acc, mul_b) = self
.super_config
.assign_region_inner::<{ constants::NUM_WINDOWS }>(
.assign_region_inner::<_, { constants::NUM_WINDOWS }>(
&mut region,
offset,
&(&scalar).into(),
base.into(),
base,
self.super_config.running_sum_config.q_range_check,
)?;
@ -214,9 +217,9 @@ impl Config {
#[cfg(test)]
// Check that the correct multiple is obtained.
{
use super::super::FixedPoint;
use group::{ff::PrimeField, Curve};
let base: super::OrchardFixedBases = base.into();
let scalar = &scalar
.base_field_elem()
.value()
@ -387,32 +390,31 @@ pub mod tests {
use crate::circuit::gadget::{
ecc::{
chip::{EccChip, NullifierK},
chip::{EccChip, FixedPoint},
FixedPointBaseField, NonIdentityPoint, Point,
},
utilities::UtilitiesInstructions,
};
use crate::constants;
use crate::constants::{self, 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 +422,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();

View File

@ -1,4 +1,4 @@
use super::super::{EccPoint, EccScalarFixed, OrchardFixedBasesFull};
use super::super::{EccPoint, EccScalarFixed, FixedPoints};
use crate::{
circuit::gadget::utilities::range_check,
@ -13,15 +13,15 @@ use halo2::{
use pasta_curves::pallas;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Config {
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(),
@ -122,8 +122,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| {
@ -133,11 +137,11 @@ impl Config {
let (acc, mul_b) = self
.super_config
.assign_region_inner::<{ constants::NUM_WINDOWS }>(
.assign_region_inner::<_, { constants::NUM_WINDOWS }>(
&mut region,
offset,
&(&scalar).into(),
base.into(),
base,
self.q_mul_fixed_full,
)?;
@ -161,9 +165,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 +188,13 @@ pub mod tests {
use rand::rngs::OsRng;
use crate::circuit::gadget::ecc::{
chip::{EccChip, OrchardFixedBasesFull},
chip::{EccChip, FixedPoint as FixedPointTrait},
FixedPoint, NonIdentityPoint, Point,
};
use crate::constants;
use crate::constants::{self, 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 +238,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,

View File

@ -1,9 +1,9 @@
use std::{array, convert::TryInto};
use super::super::{EccPoint, EccScalarFixedShort};
use super::super::{EccPoint, EccScalarFixedShort, FixedPoints};
use crate::{
circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check},
constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT},
constants::{L_VALUE, NUM_WINDOWS_SHORT},
};
use halo2::{
@ -14,16 +14,16 @@ use halo2::{
use pasta_curves::pallas;
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
pub struct Config {
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(),
@ -99,8 +99,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 +113,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 +193,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 +242,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 +273,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 +374,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 +397,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 +434,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 +456,7 @@ pub mod tests {
(magnitude, sign)
};
short_config.assign(layouter, magnitude_sign, &ValueCommitV::get())?;
short_config.assign(layouter, magnitude_sign, &ValueCommitV)?;
Ok(())
}
@ -563,7 +570,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()

View File

@ -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};
@ -47,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>;
@ -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,
// 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::{
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains,
COMMIT_IVK_PERSONALIZATION, MERKLE_CRH_PERSONALIZATION,
},
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,7 +543,7 @@ 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
@ -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>> =

View File

@ -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,10 +67,16 @@ 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]
@ -81,12 +89,22 @@ impl SinsemillaConfig {
}
#[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 {
@ -98,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.
@ -129,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(),
@ -146,6 +169,7 @@ impl SinsemillaChip {
table_y: lookup.2,
},
lookup_config: range_check,
_marker: PhantomData,
};
// Set up lookup argument
@ -241,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>;
@ -253,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,
@ -298,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,
}
}
}

View File

@ -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,

View File

@ -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(

View File

@ -138,10 +138,12 @@ 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, MERKLE_DEPTH_ORCHARD,
},
note::commitment::ExtractedNoteCommitment,
tree,
};
@ -165,7 +167,10 @@ pub mod tests {
}
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,
};

View File

@ -5,17 +5,20 @@ 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,
@ -23,20 +26,35 @@ use crate::{
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,13 +175,18 @@ 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>
MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { 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(
@ -353,11 +381,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,
@ -371,51 +409,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 },
@ -428,7 +472,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)
}
@ -441,11 +485,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)
}
}

View File

@ -12,12 +12,12 @@ use crate::{
Point,
},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaCommitDomains, SinsemillaConfig},
chip::{SinsemillaChip, SinsemillaConfig},
CommitDomain, Message, MessagePiece,
},
utilities::{bitrange_subset, bool_check},
},
constants::T_P,
constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P},
};
/// The values of the running sum at the start and end of the range being used for a
@ -55,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 {
@ -64,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();
@ -527,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
@ -536,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());
@ -739,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,
@ -1459,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::{
OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, L_ORCHARD_BASE, L_VALUE,
NOTE_COMMITMENT_PERSONALIZATION, T_Q,
},
primitives::sinsemilla::CommitDomain,
};
@ -1495,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 {
@ -1542,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],
@ -1553,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)
}
@ -1566,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 =

View File

@ -1,4 +1,12 @@
//! Constants used in the Orchard protocol.
use crate::circuit::gadget::{
ecc::{chip::FixedPoint, FixedPoints},
sinsemilla::{CommitDomains, HashDomains},
};
use crate::primitives::sinsemilla::{
Q_COMMIT_IVK_M_GENERATOR, Q_MERKLE_CRH, Q_NOTE_COMMITMENT_M_GENERATOR,
};
use arrayvec::ArrayVec;
use ff::{Field, PrimeField};
use group::Curve;
@ -15,11 +23,8 @@ pub mod spend_auth_g;
pub mod value_commit_r;
pub mod value_commit_v;
pub mod load;
pub mod util;
pub use load::{NullifierK, OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV};
/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
/// <https://github.com/zcash/pasta>
pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113;
@ -196,6 +201,177 @@ fn find_zs_and_us<C: CurveAffine>(base: C, num_windows: usize) -> Option<Vec<(u6
.collect()
}
#[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 {
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(),
}
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
impl FixedPoint<pallas::Affine> for NullifierK {
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()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS)
}
}
impl FixedPoint<pallas::Affine> for ValueCommitV {
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()
}
fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> {
compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT)
}
}
#[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)]
// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate
// for each fixed-base multiple in each window.