mirror of https://github.com/zcash/orchard.git
Extract a `DeriveNullifier` gadget from the circuit
This introduces an `AddChip` implementing field element addition on a single row, precisely matching what the nullifier integrity constraints were relying on.
This commit is contained in:
parent
70b6eb3623
commit
dafb357dc0
|
@ -16,12 +16,15 @@ use memuse::DynamicUsage;
|
|||
use pasta_curves::{arithmetic::CurveAffine, pallas, vesta};
|
||||
use rand::RngCore;
|
||||
|
||||
use self::commit_ivk::CommitIvkConfig;
|
||||
use self::note_commit::NoteCommitConfig;
|
||||
use self::{
|
||||
commit_ivk::CommitIvkConfig,
|
||||
gadget::add_chip::{AddChip, AddConfig},
|
||||
note_commit::NoteCommitConfig,
|
||||
};
|
||||
use crate::{
|
||||
constants::{
|
||||
NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull,
|
||||
OrchardHashDomains, ValueCommitV, MERKLE_DEPTH_ORCHARD,
|
||||
OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains,
|
||||
ValueCommitV, MERKLE_DEPTH_ORCHARD,
|
||||
},
|
||||
keys::{
|
||||
CommitIvkRandomness, DiversifiedTransmissionKey, NullifierDerivingKey, SpendValidatingKey,
|
||||
|
@ -39,10 +42,10 @@ use crate::{
|
|||
use halo2_gadgets::{
|
||||
ecc::{
|
||||
chip::{EccChip, EccConfig},
|
||||
FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
|
||||
FixedPoint, FixedPointShort, NonIdentityPoint, Point,
|
||||
},
|
||||
poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig},
|
||||
primitives::poseidon::{self, ConstantLength},
|
||||
poseidon::{Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig},
|
||||
primitives::poseidon,
|
||||
sinsemilla::{
|
||||
chip::{SinsemillaChip, SinsemillaConfig},
|
||||
merkle::{
|
||||
|
@ -76,9 +79,8 @@ const ENABLE_OUTPUT: usize = 8;
|
|||
pub struct Config {
|
||||
primary: Column<InstanceColumn>,
|
||||
q_orchard: Selector,
|
||||
// Selector for the field addition gate poseidon_hash(nk, rho_old) + psi_old.
|
||||
q_add: Selector,
|
||||
advices: [Column<Advice>; 10],
|
||||
add_config: AddConfig,
|
||||
ecc_config: EccConfig<OrchardFixedBases>,
|
||||
poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
|
||||
merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
||||
|
@ -182,16 +184,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
)
|
||||
});
|
||||
|
||||
// Addition of two field elements poseidon_hash(nk, rho_old) + psi_old.
|
||||
let q_add = meta.selector();
|
||||
meta.create_gate("poseidon_hash(nk, rho_old) + psi_old", |meta| {
|
||||
let q_add = meta.query_selector(q_add);
|
||||
let sum = meta.query_advice(advices[6], Rotation::cur());
|
||||
let hash_old = meta.query_advice(advices[7], Rotation::cur());
|
||||
let psi_old = meta.query_advice(advices[8], Rotation::cur());
|
||||
|
||||
Constraints::with_selector(q_add, Some(hash_old + psi_old - sum))
|
||||
});
|
||||
// Addition of two field elements.
|
||||
let add_config = AddChip::configure(meta, advices[7], advices[8], advices[6]);
|
||||
|
||||
// Fixed columns for the Sinsemilla generator lookup table
|
||||
let table_idx = meta.lookup_table_column();
|
||||
|
@ -306,8 +300,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
Config {
|
||||
primary,
|
||||
q_orchard,
|
||||
q_add,
|
||||
advices,
|
||||
add_config,
|
||||
ecc_config,
|
||||
poseidon_config,
|
||||
merkle_config_1,
|
||||
|
@ -471,61 +465,17 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
|
||||
// Nullifier integrity
|
||||
let nf_old = {
|
||||
// hash_old = poseidon_hash(nk, rho_old)
|
||||
let hash_old = {
|
||||
let poseidon_message = [nk.clone(), rho_old.clone()];
|
||||
let poseidon_hasher =
|
||||
PoseidonHash::<_, _, poseidon::P128Pow5T3, ConstantLength<2>, 3, 2>::init(
|
||||
config.poseidon_chip(),
|
||||
layouter.namespace(|| "Poseidon init"),
|
||||
)?;
|
||||
poseidon_hasher.hash(
|
||||
layouter.namespace(|| "Poseidon hash (nk, rho_old)"),
|
||||
poseidon_message,
|
||||
)?
|
||||
};
|
||||
|
||||
// Add hash output to psi.
|
||||
// `scalar` = poseidon_hash(nk, rho_old) + psi_old.
|
||||
//
|
||||
let scalar = layouter.assign_region(
|
||||
|| " `scalar` = poseidon_hash(nk, rho_old) + psi_old",
|
||||
|mut region| {
|
||||
config.q_add.enable(&mut region, 0)?;
|
||||
|
||||
hash_old.copy_advice(|| "copy hash_old", &mut region, config.advices[7], 0)?;
|
||||
psi_old.copy_advice(|| "copy psi_old", &mut region, config.advices[8], 0)?;
|
||||
|
||||
let scalar_val = hash_old
|
||||
.value()
|
||||
.zip(psi_old.value())
|
||||
.map(|(hash_old, psi_old)| hash_old + psi_old);
|
||||
region.assign_advice(
|
||||
|| "poseidon_hash(nk, rho_old) + psi_old",
|
||||
config.advices[6],
|
||||
0,
|
||||
|| scalar_val.ok_or(plonk::Error::Synthesis),
|
||||
)
|
||||
},
|
||||
let nf_old = gadget::derive_nullifier(
|
||||
layouter.namespace(|| "nf_old = DeriveNullifier_nk(rho_old, psi_old, cm_old)"),
|
||||
config.poseidon_chip(),
|
||||
config.add_chip(),
|
||||
ecc_chip.clone(),
|
||||
rho_old.clone(),
|
||||
&psi_old,
|
||||
&cm_old,
|
||||
nk.clone(),
|
||||
)?;
|
||||
|
||||
// Multiply scalar by NullifierK
|
||||
// `product` = [poseidon_hash(nk, rho_old) + psi_old] NullifierK.
|
||||
//
|
||||
let product = {
|
||||
let nullifier_k = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
|
||||
nullifier_k.mul(
|
||||
layouter.namespace(|| "[poseidon_output + psi_old] NullifierK"),
|
||||
scalar,
|
||||
)?
|
||||
};
|
||||
|
||||
// Add cm_old to multiplied fixed base to get nf_old
|
||||
// cm_old + [poseidon_output + psi_old] NullifierK
|
||||
let nf_old = cm_old
|
||||
.add(layouter.namespace(|| "nf_old"), &product)?
|
||||
.extract_p();
|
||||
|
||||
// Constrain nf_old to equal public input
|
||||
layouter.constrain_instance(nf_old.inner().cell(), config.primary, NF_OLD)?;
|
||||
|
||||
|
|
|
@ -2,14 +2,26 @@
|
|||
|
||||
use pasta_curves::pallas;
|
||||
|
||||
use crate::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
|
||||
use crate::constants::{NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
|
||||
use halo2_gadgets::{
|
||||
ecc::chip::EccChip,
|
||||
poseidon::Pow5Chip as PoseidonChip,
|
||||
ecc::{chip::EccChip, EccInstructions, FixedPointBaseField, Point, X},
|
||||
poseidon::{Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip},
|
||||
primitives::poseidon::{self, ConstantLength},
|
||||
sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip},
|
||||
};
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
plonk,
|
||||
};
|
||||
|
||||
pub(in crate::circuit) mod add_chip;
|
||||
|
||||
impl super::Config {
|
||||
pub(super) fn add_chip(&self) -> add_chip::AddChip {
|
||||
add_chip::AddChip::construct(self.add_config.clone())
|
||||
}
|
||||
|
||||
pub(super) fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
|
||||
EccChip::construct(self.ecc_config.clone())
|
||||
}
|
||||
|
@ -42,3 +54,72 @@ impl super::Config {
|
|||
PoseidonChip::construct(self.poseidon_config.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// An instruction set for adding two circuit words (field elements).
|
||||
pub(in crate::circuit) trait AddInstruction<F: FieldExt>: Chip<F> {
|
||||
/// Constraints `a + b` and returns the sum.
|
||||
fn add(
|
||||
&self,
|
||||
layouter: impl Layouter<F>,
|
||||
a: &AssignedCell<F, F>,
|
||||
b: &AssignedCell<F, F>,
|
||||
) -> Result<AssignedCell<F, F>, plonk::Error>;
|
||||
}
|
||||
|
||||
/// `DeriveNullifier` from [Section 4.16: Note Commitments and Nullifiers].
|
||||
///
|
||||
/// [Section 4.16: Note Commitments and Nullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(in crate::circuit) fn derive_nullifier<
|
||||
PoseidonChip: PoseidonSpongeInstructions<pallas::Base, poseidon::P128Pow5T3, ConstantLength<2>, 3, 2>,
|
||||
AddChip: AddInstruction<pallas::Base>,
|
||||
EccChip: EccInstructions<
|
||||
pallas::Affine,
|
||||
FixedPoints = OrchardFixedBases,
|
||||
Var = AssignedCell<pallas::Base, pallas::Base>,
|
||||
>,
|
||||
>(
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
poseidon_chip: PoseidonChip,
|
||||
add_chip: AddChip,
|
||||
ecc_chip: EccChip,
|
||||
rho: AssignedCell<pallas::Base, pallas::Base>,
|
||||
psi: &AssignedCell<pallas::Base, pallas::Base>,
|
||||
cm: &Point<pallas::Affine, EccChip>,
|
||||
nk: AssignedCell<pallas::Base, pallas::Base>,
|
||||
) -> Result<X<pallas::Affine, EccChip>, plonk::Error> {
|
||||
// hash = poseidon_hash(nk, rho)
|
||||
let hash = {
|
||||
let poseidon_message = [nk, rho];
|
||||
let poseidon_hasher =
|
||||
PoseidonHash::init(poseidon_chip, layouter.namespace(|| "Poseidon init"))?;
|
||||
poseidon_hasher.hash(
|
||||
layouter.namespace(|| "Poseidon hash (nk, rho)"),
|
||||
poseidon_message,
|
||||
)?
|
||||
};
|
||||
|
||||
// Add hash output to psi.
|
||||
// `scalar` = poseidon_hash(nk, rho) + psi.
|
||||
let scalar = add_chip.add(
|
||||
layouter.namespace(|| "scalar = poseidon_hash(nk, rho) + psi"),
|
||||
&hash,
|
||||
psi,
|
||||
)?;
|
||||
|
||||
// Multiply scalar by NullifierK
|
||||
// `product` = [poseidon_hash(nk, rho) + psi] NullifierK.
|
||||
//
|
||||
let product = {
|
||||
let nullifier_k = FixedPointBaseField::from_inner(ecc_chip, NullifierK);
|
||||
nullifier_k.mul(
|
||||
layouter.namespace(|| "[poseidon_output + psi] NullifierK"),
|
||||
scalar,
|
||||
)?
|
||||
};
|
||||
|
||||
// Add cm to multiplied fixed base to get nf
|
||||
// cm + [poseidon_output + psi] NullifierK
|
||||
cm.add(layouter.namespace(|| "nf"), &product)
|
||||
.map(|res| res.extract_p())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
plonk::{self, Advice, Column, ConstraintSystem, Constraints, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
|
||||
use super::AddInstruction;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(in crate::circuit) struct AddConfig {
|
||||
a: Column<Advice>,
|
||||
b: Column<Advice>,
|
||||
c: Column<Advice>,
|
||||
q_add: Selector,
|
||||
}
|
||||
|
||||
/// A chip implementing a single addition constraint `c = a + b` on a single row.
|
||||
pub(in crate::circuit) struct AddChip {
|
||||
config: AddConfig,
|
||||
}
|
||||
|
||||
impl Chip<pallas::Base> for AddChip {
|
||||
type Config = AddConfig;
|
||||
type Loaded = ();
|
||||
|
||||
fn config(&self) -> &Self::Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
fn loaded(&self) -> &Self::Loaded {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
impl AddChip {
|
||||
pub(in crate::circuit) fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
a: Column<Advice>,
|
||||
b: Column<Advice>,
|
||||
c: Column<Advice>,
|
||||
) -> AddConfig {
|
||||
let q_add = meta.selector();
|
||||
meta.create_gate("Field element addition: c = a + b", |meta| {
|
||||
let q_add = meta.query_selector(q_add);
|
||||
let a = meta.query_advice(a, Rotation::cur());
|
||||
let b = meta.query_advice(b, Rotation::cur());
|
||||
let c = meta.query_advice(c, Rotation::cur());
|
||||
|
||||
Constraints::with_selector(q_add, Some(a + b - c))
|
||||
});
|
||||
|
||||
AddConfig { a, b, c, q_add }
|
||||
}
|
||||
|
||||
pub(in crate::circuit) fn construct(config: AddConfig) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
}
|
||||
|
||||
impl AddInstruction<pallas::Base> for AddChip {
|
||||
fn add(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
a: &AssignedCell<pallas::Base, pallas::Base>,
|
||||
b: &AssignedCell<pallas::Base, pallas::Base>,
|
||||
) -> Result<AssignedCell<pallas::Base, pallas::Base>, plonk::Error> {
|
||||
layouter.assign_region(
|
||||
|| "c = a + b",
|
||||
|mut region| {
|
||||
self.config.q_add.enable(&mut region, 0)?;
|
||||
|
||||
a.copy_advice(|| "copy a", &mut region, self.config.a, 0)?;
|
||||
b.copy_advice(|| "copy b", &mut region, self.config.b, 0)?;
|
||||
|
||||
let scalar_val = a.value().zip(b.value()).map(|(a, b)| a + b);
|
||||
region.assign_advice(
|
||||
|| "c",
|
||||
self.config.c,
|
||||
0,
|
||||
|| scalar_val.ok_or(plonk::Error::Synthesis),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue