mirror of https://github.com/zcash/halo2.git
circuit.rs: Constrain derived circuit values to equal public inputs.
This commit is contained in:
parent
059af49f46
commit
6f4b5b0340
218
src/circuit.rs
218
src/circuit.rs
|
@ -9,7 +9,10 @@ use halo2::{
|
|||
poly::Rotation,
|
||||
transcript::{Blake2bRead, Blake2bWrite},
|
||||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas, vesta};
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
pallas, vesta,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
|
@ -66,12 +69,23 @@ pub(crate) mod gadget;
|
|||
// FIXME: This circuit should fit within 2^11 rows.
|
||||
const K: u32 = 12;
|
||||
|
||||
// Absolute offsets for public inputs.
|
||||
const ZERO: usize = 0;
|
||||
const ANCHOR: usize = 1;
|
||||
const NF_OLD: usize = 2;
|
||||
const CV_NET_X: usize = 3;
|
||||
const CV_NET_Y: usize = 4;
|
||||
const RK_X: usize = 5;
|
||||
const RK_Y: usize = 6;
|
||||
const CMX: usize = 7;
|
||||
const ENABLE_SPEND: usize = 8;
|
||||
const ENABLE_OUTPUT: usize = 9;
|
||||
|
||||
/// Configuration needed to use the Orchard Action circuit.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
q_primary: Selector,
|
||||
primary: Column<InstanceColumn>,
|
||||
q_v_net: Selector,
|
||||
q_orchard: Selector,
|
||||
advices: [Column<Advice>; 10],
|
||||
enable_flag_config: EnableFlagConfig,
|
||||
ecc_config: EccConfig,
|
||||
|
@ -138,15 +152,29 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
];
|
||||
|
||||
// Constrain v_old - v_new = magnitude * sign
|
||||
let q_v_net = meta.selector();
|
||||
meta.create_gate("v_old - v_new = magnitude * sign", |meta| {
|
||||
let q_v_net = meta.query_selector(q_v_net);
|
||||
// Either v_old = 0, or anchor equals public input
|
||||
let q_orchard = meta.selector();
|
||||
meta.create_gate("Orchard circuit checks", |meta| {
|
||||
let q_orchard = meta.query_selector(q_orchard);
|
||||
let v_old = meta.query_advice(advices[0], Rotation::cur());
|
||||
let v_new = meta.query_advice(advices[1], Rotation::cur());
|
||||
let magnitude = meta.query_advice(advices[2], Rotation::cur());
|
||||
let sign = meta.query_advice(advices[3], Rotation::cur());
|
||||
|
||||
vec![q_v_net * (v_old - v_new - magnitude * sign)]
|
||||
let anchor = meta.query_advice(advices[4], Rotation::cur());
|
||||
let pub_input_anchor = meta.query_advice(advices[5], Rotation::cur());
|
||||
|
||||
std::array::IntoIter::new([
|
||||
(
|
||||
"v_old - v_new = magnitude * sign",
|
||||
v_old.clone() - v_new - magnitude * sign,
|
||||
),
|
||||
(
|
||||
"Either v_old = 0, or anchor equals public input",
|
||||
v_old * (anchor - pub_input_anchor),
|
||||
),
|
||||
])
|
||||
.map(move |(name, poly)| (name, q_orchard.clone() * poly))
|
||||
});
|
||||
|
||||
// Fixed columns for the Sinsemilla generator lookup table
|
||||
|
@ -173,6 +201,11 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
meta.fixed_column(),
|
||||
];
|
||||
|
||||
// Instance column.
|
||||
let primary = meta.instance_column();
|
||||
|
||||
meta.enable_equality(primary.into());
|
||||
|
||||
// Permutation over all advice columns and `constants` columns.
|
||||
// TODO: Replace `*_constants` with public inputs API.
|
||||
for advice in advices.iter() {
|
||||
|
@ -254,27 +287,9 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
let new_note_commit_config =
|
||||
NoteCommitConfig::configure(meta, advices, sinsemilla_config_2.clone());
|
||||
|
||||
// TODO: Infrastructure to handle public inputs.
|
||||
let q_primary = meta.selector();
|
||||
let primary = meta.instance_column();
|
||||
|
||||
// Placeholder gate so there is something for the prover to operate on.
|
||||
// We need a selector so that the gate is disabled by default, and doesn't
|
||||
// interfere with the blinding factors.
|
||||
let advice = meta.advice_column();
|
||||
let selector = meta.selector();
|
||||
|
||||
meta.create_gate("TODO", |meta| {
|
||||
let a = meta.query_advice(advice, Rotation::cur());
|
||||
let s = meta.query_selector(selector);
|
||||
|
||||
vec![s * a]
|
||||
});
|
||||
|
||||
Config {
|
||||
q_primary,
|
||||
primary,
|
||||
q_v_net,
|
||||
q_orchard,
|
||||
advices,
|
||||
enable_flag_config,
|
||||
ecc_config,
|
||||
|
@ -366,8 +381,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// Merkle path validity check.
|
||||
// TODO: constrain output to equal public input
|
||||
let _anchor = {
|
||||
let anchor = {
|
||||
let merkle_inputs = MerklePath {
|
||||
chip_1: config.merkle_chip_1(),
|
||||
chip_2: config.merkle_chip_2(),
|
||||
|
@ -380,8 +394,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// Value commitment integrity.
|
||||
// TODO: constrain to equal public input cv_net
|
||||
let _cv_net = {
|
||||
let v_net = {
|
||||
// v_net = v_old - v_new
|
||||
let v_net = {
|
||||
let v_net_val = self.v_old.zip(self.v_new).map(|(v_old, v_new)| {
|
||||
|
@ -426,26 +439,6 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
(magnitude, sign)
|
||||
};
|
||||
|
||||
// Constrain v_old - v_new = magnitude * sign
|
||||
layouter.assign_region(
|
||||
|| "v_old - v_new = magnitude * sign",
|
||||
|mut region| {
|
||||
copy(&mut region, || "v_old", config.advices[0], 0, &v_old)?;
|
||||
copy(&mut region, || "v_new", config.advices[1], 0, &v_new)?;
|
||||
let (magnitude, sign) = v_net;
|
||||
copy(
|
||||
&mut region,
|
||||
|| "v_net magnitude",
|
||||
config.advices[2],
|
||||
0,
|
||||
&magnitude,
|
||||
)?;
|
||||
copy(&mut region, || "v_net sign", config.advices[3], 0, &sign)?;
|
||||
|
||||
config.q_v_net.enable(&mut region, 0)
|
||||
},
|
||||
)?;
|
||||
|
||||
// commitment = [v_net] ValueCommitV
|
||||
let (commitment, _) = {
|
||||
let value_commit_v = ValueCommitV::get();
|
||||
|
@ -464,11 +457,16 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// [v_net] ValueCommitV + [rcv] ValueCommitR
|
||||
commitment.add(layouter.namespace(|| "cv_net"), &blind)?
|
||||
let cv_net = commitment.add(layouter.namespace(|| "cv_net"), &blind)?;
|
||||
|
||||
// Constrain cv_net to equal public input
|
||||
layouter.constrain_instance(cv_net.inner().x().cell(), config.primary, CV_NET_X)?;
|
||||
layouter.constrain_instance(cv_net.inner().y().cell(), config.primary, CV_NET_Y)?;
|
||||
|
||||
v_net
|
||||
};
|
||||
|
||||
// Nullifier integrity
|
||||
// TODO: constrain to equal public input nf_old
|
||||
let nf_old = {
|
||||
// nk_rho_old = poseidon_hash(nk, rho_old)
|
||||
let nk_rho_old = {
|
||||
|
@ -548,14 +546,18 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
|
||||
// Add cm_old to multiplied fixed base to get nf_old
|
||||
// cm_old + [poseidon_output + psi_old] NullifierK
|
||||
cm_old
|
||||
let nf_old = cm_old
|
||||
.add(layouter.namespace(|| "nf_old"), &product)?
|
||||
.extract_p()
|
||||
.extract_p();
|
||||
|
||||
// Constrain nf_old to equal public input
|
||||
layouter.constrain_instance(nf_old.inner().cell(), config.primary, NF_OLD)?;
|
||||
|
||||
nf_old
|
||||
};
|
||||
|
||||
// Spend authority
|
||||
// TODO: constrain to equal public input rk
|
||||
let _rk = {
|
||||
{
|
||||
// alpha_commitment = [alpha] SpendAuthG
|
||||
let (alpha_commitment, _) = {
|
||||
let spend_auth_g = OrchardFixedBasesFull::SpendAuthG;
|
||||
|
@ -564,11 +566,15 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// [alpha] SpendAuthG + ak
|
||||
alpha_commitment.add(layouter.namespace(|| "rk"), &ak)?
|
||||
};
|
||||
let rk = alpha_commitment.add(layouter.namespace(|| "rk"), &ak)?;
|
||||
|
||||
// Constrain rk to equal public input
|
||||
layouter.constrain_instance(rk.inner().x().cell(), config.primary, RK_X)?;
|
||||
layouter.constrain_instance(rk.inner().y().cell(), config.primary, RK_Y)?;
|
||||
}
|
||||
|
||||
// Diversified address integrity.
|
||||
let (pk_d_old, _) = {
|
||||
let pk_d_old = {
|
||||
let commit_ivk_config = config.commit_ivk_config.clone();
|
||||
|
||||
let ivk = {
|
||||
|
@ -585,17 +591,31 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
};
|
||||
|
||||
// [ivk] g_d_old
|
||||
g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk.inner())?
|
||||
let (derived_pk_d_old, _) =
|
||||
g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk.inner())?;
|
||||
|
||||
// Constrain derived pk_d_old to equal witnessed pk_d_old
|
||||
// This addresses the case where ivk = ⊥ , since ⊥ maps to 0 and variable-base
|
||||
// scalar multiplication maps [0]B to (0, 0).
|
||||
let pk_d_old = Point::new(
|
||||
ecc_chip.clone(),
|
||||
layouter.namespace(|| "witness pk_d_old"),
|
||||
self.pk_d_old.map(|pk_d_old| (*pk_d_old).to_affine()),
|
||||
)?;
|
||||
derived_pk_d_old
|
||||
.constrain_equal(layouter.namespace(|| "pk_d_old equality"), &pk_d_old)?;
|
||||
|
||||
pk_d_old
|
||||
};
|
||||
|
||||
// Old note commitment integrity.
|
||||
let _cm_old = {
|
||||
{
|
||||
let old_note_commit_config = config.old_note_commit_config.clone();
|
||||
|
||||
let rcm_old = self.rcm_old.as_ref().map(|rcm_old| **rcm_old);
|
||||
|
||||
// g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)
|
||||
old_note_commit_config.assign_region(
|
||||
let derived_cm_old = old_note_commit_config.assign_region(
|
||||
layouter.namespace(|| {
|
||||
"g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)"
|
||||
}),
|
||||
|
@ -607,11 +627,14 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
rho_old,
|
||||
psi_old,
|
||||
rcm_old,
|
||||
)?
|
||||
};
|
||||
)?;
|
||||
|
||||
// new note commitment integrity.
|
||||
let _cmx = {
|
||||
// Constrain derived cm_old to equal witnessed cm_old
|
||||
derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?;
|
||||
}
|
||||
|
||||
// New note commitment integrity.
|
||||
{
|
||||
let new_note_commit_config = config.new_note_commit_config.clone();
|
||||
|
||||
// Witness g_d_new_star
|
||||
|
@ -662,8 +685,41 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
rcm_new,
|
||||
)?;
|
||||
|
||||
cm_new.extract_p()
|
||||
};
|
||||
let cmx = cm_new.extract_p();
|
||||
|
||||
// Constrain cmx to equal public input
|
||||
layouter.constrain_instance(cmx.inner().cell(), config.primary, CMX)?;
|
||||
}
|
||||
|
||||
// Constrain v_old - v_new = magnitude * sign
|
||||
// Either v_old = 0, or anchor equals public input
|
||||
layouter.assign_region(
|
||||
|| "v_old - v_new = magnitude * sign",
|
||||
|mut region| {
|
||||
copy(&mut region, || "v_old", config.advices[0], 0, &v_old)?;
|
||||
copy(&mut region, || "v_new", config.advices[1], 0, &v_new)?;
|
||||
let (magnitude, sign) = v_net;
|
||||
copy(
|
||||
&mut region,
|
||||
|| "v_net magnitude",
|
||||
config.advices[2],
|
||||
0,
|
||||
&magnitude,
|
||||
)?;
|
||||
copy(&mut region, || "v_net sign", config.advices[3], 0, &sign)?;
|
||||
|
||||
copy(&mut region, || "anchor", config.advices[4], 0, &anchor)?;
|
||||
region.assign_advice_from_instance(
|
||||
|| "pub input anchor",
|
||||
config.primary,
|
||||
ANCHOR,
|
||||
config.advices[5],
|
||||
0,
|
||||
)?;
|
||||
|
||||
config.q_orchard.enable(&mut region, 0)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -721,9 +777,28 @@ pub struct Instance {
|
|||
}
|
||||
|
||||
impl Instance {
|
||||
fn to_halo2_instance(&self) -> [[vesta::Scalar; 0]; 1] {
|
||||
// TODO
|
||||
[[]]
|
||||
fn to_halo2_instance(&self) -> [[vesta::Scalar; 10]; 1] {
|
||||
let mut instance = [vesta::Scalar::zero(); 10];
|
||||
|
||||
// instance[0] is left as 0.
|
||||
instance[ANCHOR] = *self.anchor;
|
||||
instance[CV_NET_X] = self.cv_net.x();
|
||||
instance[CV_NET_Y] = self.cv_net.y();
|
||||
instance[NF_OLD] = *self.nf_old;
|
||||
|
||||
let rk = pallas::Point::from_bytes(&self.rk.clone().into())
|
||||
.unwrap()
|
||||
.to_affine()
|
||||
.coordinates()
|
||||
.unwrap();
|
||||
|
||||
instance[RK_X] = *rk.x();
|
||||
instance[RK_Y] = *rk.y();
|
||||
instance[CMX] = *self.cmx;
|
||||
instance[ENABLE_SPEND] = vesta::Scalar::from_u64(self.enable_spend.into());
|
||||
instance[ENABLE_OUTPUT] = vesta::Scalar::from_u64(self.enable_output.into());
|
||||
|
||||
[instance]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -814,6 +889,7 @@ mod tests {
|
|||
let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(())
|
||||
.map(|()| {
|
||||
let (_, fvk, spent_note) = Note::dummy(&mut rng, None);
|
||||
|
||||
let sender_address = fvk.default_address();
|
||||
let nk = *fvk.nk();
|
||||
let rivk = *fvk.rivk();
|
||||
|
|
|
@ -466,6 +466,14 @@ impl AsRef<[u8; 32]> for OutgoingViewingKey {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
|
||||
|
||||
impl std::ops::Deref for DiversifiedTransmissionKey {
|
||||
type Target = pallas::Point;
|
||||
|
||||
fn deref(&self) -> &pallas::Point {
|
||||
&(*self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl DiversifiedTransmissionKey {
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
|
|
|
@ -14,6 +14,14 @@ use crate::{
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Nullifier(pub(crate) pallas::Base);
|
||||
|
||||
impl std::ops::Deref for Nullifier {
|
||||
type Target = pallas::Base;
|
||||
|
||||
fn deref(&self) -> &pallas::Base {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Nullifier {
|
||||
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
|
||||
///
|
||||
|
|
|
@ -53,6 +53,14 @@ impl From<pallas::Base> for Anchor {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Anchor {
|
||||
type Target = pallas::Base;
|
||||
|
||||
fn deref(&self) -> &pallas::Base {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Anchor {
|
||||
/// Parses an Orchard anchor from a byte encoding.
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> Option<Anchor> {
|
||||
|
|
22
src/value.rs
22
src/value.rs
|
@ -21,9 +21,9 @@ use std::ops::{Add, Sub};
|
|||
|
||||
use bitvec::{array::BitArray, order::Lsb0};
|
||||
use ff::{Field, PrimeField};
|
||||
use group::{Group, GroupEncoding};
|
||||
use group::{Curve, Group, GroupEncoding};
|
||||
use pasta_curves::{
|
||||
arithmetic::{CurveExt, FieldExt},
|
||||
arithmetic::{CurveAffine, CurveExt, FieldExt},
|
||||
pallas,
|
||||
};
|
||||
use rand::RngCore;
|
||||
|
@ -275,6 +275,24 @@ impl ValueCommitment {
|
|||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
/// x-coordinate of this value commitment.
|
||||
pub fn x(&self) -> pallas::Base {
|
||||
if self.0 == pallas::Point::identity() {
|
||||
pallas::Base::zero()
|
||||
} else {
|
||||
*self.0.to_affine().coordinates().unwrap().x()
|
||||
}
|
||||
}
|
||||
|
||||
/// y-coordinate of this value commitment.
|
||||
pub fn y(&self) -> pallas::Base {
|
||||
if self.0 == pallas::Point::identity() {
|
||||
pallas::Base::zero()
|
||||
} else {
|
||||
*self.0.to_affine().coordinates().unwrap().y()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
|
|
Loading…
Reference in New Issue