Merge branch 'zsa1' into zsa-builder

This commit is contained in:
Paul 2022-07-18 13:19:47 +03:00
commit 60a17a2179
14 changed files with 467 additions and 361 deletions

View File

@ -7,6 +7,17 @@ and this project adheres to Rust's notion of
## [Unreleased] ## [Unreleased]
## [0.2.0] - 2022-06-24
### Added
- `orchard::bundle::BatchValidator`
- `orchard::note_encryption`:
- `CompactAction::from_parts`
- `CompactAction::nullifier`
- `OrchardDomain::for_nullifier`
### Changed
- Migrated to `halo2_proofs 0.2`.
## [0.1.0] - 2022-05-10 ## [0.1.0] - 2022-05-10
### Changed ### Changed
- Migrated to `bitvec 1`, `ff 0.12`, `group 0.12`, `incrementalmerkletree 0.3`, - Migrated to `bitvec 1`, `ff 0.12`, `group 0.12`, `incrementalmerkletree 0.3`,

View File

@ -1,6 +1,6 @@
[package] [package]
name = "orchard" name = "orchard"
version = "0.1.0" version = "0.2.0"
authors = [ authors = [
"Sean Bowe <sean@electriccoin.co>", "Sean Bowe <sean@electriccoin.co>",
"Jack Grigg <jack@electriccoin.co>", "Jack Grigg <jack@electriccoin.co>",
@ -29,8 +29,8 @@ blake2b_simd = "1"
ff = "0.12" ff = "0.12"
fpe = "0.5" fpe = "0.5"
group = "0.12" group = "0.12"
halo2_gadgets = "0.1" halo2_gadgets = "0.2"
halo2_proofs = "0.1" halo2_proofs = "0.2"
hex = "0.4" hex = "0.4"
lazy_static = "1" lazy_static = "1"
memuse = { version = "0.2", features = ["nonempty"] } memuse = { version = "0.2", features = ["nonempty"] }
@ -44,18 +44,21 @@ subtle = "2.3"
zcash_note_encryption = "0.1" zcash_note_encryption = "0.1"
incrementalmerkletree = "0.3" incrementalmerkletree = "0.3"
# Logging
tracing = "0.1"
# Developer tooling dependencies # Developer tooling dependencies
plotters = { version = "0.3.0", optional = true } plotters = { version = "0.3.0", optional = true }
[dev-dependencies] [dev-dependencies]
criterion = "0.3" criterion = "0.3"
halo2_gadgets = { version = "0.1", features = ["test-dependencies"] } halo2_gadgets = { version = "0.2", features = ["test-dependencies"] }
hex = "0.4" hex = "0.4"
proptest = "1.0.0" proptest = "1.0.0"
zcash_note_encryption = { version = "0.1", features = ["pre-zip-212"] } zcash_note_encryption = { version = "0.1", features = ["pre-zip-212"] }
[target.'cfg(unix)'.dev-dependencies] [target.'cfg(unix)'.dev-dependencies]
pprof = { version = "0.8", features = ["criterion", "flamegraph"] } # MSRV 1.56 pprof = { version = "0.9", features = ["criterion", "flamegraph"] } # MSRV 1.56
[lib] [lib]
bench = false bench = false

View File

@ -161,8 +161,8 @@ of such entity.
restricted or conditioned by this License or by law, and Licensor promises not restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You. to interfere with or be responsible for such uses by You.
16. **Modification of This License.** This License is Copyright © 2007 Zooko 16. **Modification of This License.** This License is Copyright © 2021-2022 Electric Coin Company.
Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate this Permission is granted to copy, distribute, or communicate this
License without modification. Nothing in this License permits You to modify License without modification. Nothing in this License permits You to modify
this License as applied to the Original Work or to Derivative Works. However, this License as applied to the Original Work or to Derivative Works. However,
You may modify the text of this License and copy, distribute or communicate You may modify the text of this License and copy, distribute or communicate
@ -173,4 +173,4 @@ Licence" or "BOSL" and you may not use those names in the name of your Modified
License; and (ii) You must replace the notice specified in the first paragraph License; and (ii) You must replace the notice specified in the first paragraph
above with the notice "Licensed under <insert your license name here>" or with above with the notice "Licensed under <insert your license name here>" or with
a notice of your own that is not confusingly similar to the notice in this a notice of your own that is not confusingly similar to the notice in this
License. License.

View File

@ -246,7 +246,8 @@ The $\mathit{Commit}^{\mathsf{nf}}$ variants were considered to avoid directly d
$\mathsf{cm}$ (which in its native type is a base field element, not a group element). We $\mathsf{cm}$ (which in its native type is a base field element, not a group element). We
decided instead to follow Sapling by defining an intermediate representation of decided instead to follow Sapling by defining an intermediate representation of
$\mathsf{cm}$ as a group element, that is only used in nullifier computation. The circuit $\mathsf{cm}$ as a group element, that is only used in nullifier computation. The circuit
already needs to compute $\mathsf{cm}$, so this improves performance by removing already needs to compute $\mathsf{cm}$, so this improves performance by removing an
additional commitment calculation from the circuit.
We also considered variants that used a choice of fixed bases $\mathcal{G_v}$ to provide We also considered variants that used a choice of fixed bases $\mathcal{G_v}$ to provide
domain separation for zero-valued notes. The most performant design (similar to the chosen domain separation for zero-valued notes. The most performant design (similar to the chosen

View File

@ -5,6 +5,7 @@ use core::iter;
use std::collections::HashMap; use std::collections::HashMap;
use ff::Field; use ff::Field;
use halo2_proofs::circuit::Value;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use pasta_curves::pallas; use pasta_curves::pallas;
use rand::{prelude::SliceRandom, CryptoRng, RngCore}; use rand::{prelude::SliceRandom, CryptoRng, RngCore};
@ -181,6 +182,7 @@ impl ActionInfo {
let ak: SpendValidatingKey = self.spend.fvk.clone().into(); let ak: SpendValidatingKey = self.spend.fvk.clone().into();
let alpha = pallas::Scalar::random(&mut rng); let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha); let rk = ak.randomize(&alpha);
let note_type = self.spend.note.note_type();
let note = Note::new( let note = Note::new(
self.output.recipient, self.output.recipient,
@ -225,26 +227,25 @@ impl ActionInfo {
}, },
), ),
Circuit { Circuit {
path: Some(self.spend.merkle_path.auth_path()), path: Value::known(self.spend.merkle_path.auth_path()),
pos: Some(self.spend.merkle_path.position()), pos: Value::known(self.spend.merkle_path.position()),
g_d_old: Some(sender_address.g_d()), g_d_old: Value::known(sender_address.g_d()),
pk_d_old: Some(*sender_address.pk_d()), pk_d_old: Value::known(*sender_address.pk_d()),
v_old: Some(self.spend.note.value()), v_old: Value::known(self.spend.note.value()),
//split: Some(self.spend.split_flag), rho_old: Value::known(rho_old),
rho_old: Some(rho_old), psi_old: Value::known(psi_old),
psi_old: Some(psi_old), rcm_old: Value::known(rcm_old),
rcm_old: Some(rcm_old), cm_old: Value::known(self.spend.note.commitment()),
cm_old: Some(self.spend.note.commitment()), alpha: Value::known(alpha),
alpha: Some(alpha), ak: Value::known(ak),
ak: Some(ak), nk: Value::known(*self.spend.fvk.nk()),
nk: Some(*self.spend.fvk.nk()), rivk: Value::known(self.spend.fvk.rivk(self.spend.scope)),
rivk: Some(self.spend.fvk.rivk(self.spend.scope)), g_d_new: Value::known(note.recipient().g_d()),
g_d_new: Some(note.recipient().g_d()), pk_d_new: Value::known(*note.recipient().pk_d()),
pk_d_new: Some(*note.recipient().pk_d()), v_new: Value::known(note.value()),
v_new: Some(note.value()), psi_new: Value::known(note.rseed().psi(&note.rho())),
psi_new: Some(note.rseed().psi(&note.rho())), rcm_new: Value::known(note.rseed().rcm(&note.rho())),
rcm_new: Some(note.rseed().rcm(&note.rho())), rcv: Value::known(self.rcv),
rcv: Some(self.rcv),
}, },
) )
} }
@ -273,8 +274,16 @@ impl Builder {
/// Adds a note to be spent in this transaction. /// Adds a note to be spent in this transaction.
/// ///
/// - `note` is a spendable note, obtained by trial-decrypting an [`Action`] using the
/// [`zcash_note_encryption`] crate instantiated with [`OrchardDomain`].
/// - `merkle_path` can be obtained using the [`incrementalmerkletree`] crate
/// instantiated with [`MerkleHashOrchard`].
///
/// Returns an error if the given Merkle path does not have the required anchor for /// Returns an error if the given Merkle path does not have the required anchor for
/// the given note. /// the given note.
///
/// [`OrchardDomain`]: crate::note_encryption::OrchardDomain
/// [`MerkleHashOrchard`]: crate::tree::MerkleHashOrchard
pub fn add_spend( pub fn add_spend(
&mut self, &mut self,
fvk: FullViewingKey, fvk: FullViewingKey,

View File

@ -1,7 +1,10 @@
//! Structs related to bundles of Orchard actions. //! Structs related to bundles of Orchard actions.
mod batch;
pub mod commitments; pub mod commitments;
pub use batch::BatchValidator;
use core::fmt; use core::fmt;
use blake2b_simd::Hash as Blake2bHash; use blake2b_simd::Hash as Blake2bHash;

91
src/bundle/batch.rs Normal file
View File

@ -0,0 +1,91 @@
use halo2_proofs::plonk;
use pasta_curves::vesta;
use rand::{CryptoRng, RngCore};
use tracing::debug;
use super::{Authorized, Bundle};
use crate::{
circuit::VerifyingKey,
primitives::redpallas::{self, Binding, SpendAuth},
};
/// A signature within an authorized Orchard bundle.
#[derive(Debug)]
struct BundleSignature {
/// The signature item for validation.
signature: redpallas::batch::Item<SpendAuth, Binding>,
}
/// Batch validation context for Orchard.
///
/// This batch-validates proofs and RedPallas signatures.
#[derive(Debug, Default)]
pub struct BatchValidator {
proofs: plonk::BatchVerifier<vesta::Affine>,
signatures: Vec<BundleSignature>,
}
impl BatchValidator {
/// Constructs a new batch validation context.
pub fn new() -> Self {
BatchValidator {
proofs: plonk::BatchVerifier::new(),
signatures: vec![],
}
}
/// Adds the proof and RedPallas signatures from the given bundle to the validator.
pub fn add_bundle<V: Copy + Into<i64>>(
&mut self,
bundle: &Bundle<Authorized, V>,
sighash: [u8; 32],
) {
for action in bundle.actions().iter() {
self.signatures.push(BundleSignature {
signature: action
.rk()
.create_batch_item(action.authorization().clone(), &sighash),
});
}
self.signatures.push(BundleSignature {
signature: bundle
.binding_validating_key()
.create_batch_item(bundle.authorization().binding_signature().clone(), &sighash),
});
bundle
.authorization()
.proof()
.add_to_batch(&mut self.proofs, bundle.to_instances());
}
/// Batch-validates the accumulated bundles.
///
/// Returns `true` if every proof and signature in every bundle added to the batch
/// validator is valid, or `false` if one or more are invalid. No attempt is made to
/// figure out which of the accumulated bundles might be invalid; if that information
/// is desired, construct separate [`BatchValidator`]s for sub-batches of the bundles.
pub fn validate<R: RngCore + CryptoRng>(self, vk: &VerifyingKey, rng: R) -> bool {
if self.signatures.is_empty() {
// An empty batch is always valid, but is not free to run; skip it.
// Note that a transaction has at least a binding signature, so if
// there are no signatures, there are also no proofs.
return true;
}
let mut validator = redpallas::batch::Verifier::new();
for sig in self.signatures.iter() {
validator.queue(sig.signature.clone());
}
match validator.verify(rng) {
// If signatures are valid, check the proofs.
Ok(()) => self.proofs.finalize(&vk.params, &vk.vk),
Err(e) => {
debug!("RedPallas batch validation failed: {}", e);
false
}
}
}
}

View File

@ -4,10 +4,10 @@ use core::fmt;
use group::{Curve, GroupEncoding}; use group::{Curve, GroupEncoding};
use halo2_proofs::{ use halo2_proofs::{
circuit::{floor_planner, Layouter}, circuit::{floor_planner, Layouter, Value},
plonk::{ plonk::{
self, Advice, Column, Constraints, Expression, Instance as InstanceColumn, Selector, self, Advice, BatchVerifier, Column, Constraints, Expression, Instance as InstanceColumn,
SingleVerifier, Selector, SingleVerifier,
}, },
poly::Rotation, poly::Rotation,
transcript::{Blake2bRead, Blake2bWrite}, transcript::{Blake2bRead, Blake2bWrite},
@ -99,26 +99,25 @@ pub struct Config {
/// The Orchard Action circuit. /// The Orchard Action circuit.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct Circuit { pub struct Circuit {
pub(crate) path: Option<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, pub(crate) path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>,
pub(crate) pos: Option<u32>, pub(crate) pos: Value<u32>,
pub(crate) g_d_old: Option<NonIdentityPallasPoint>, pub(crate) g_d_old: Value<NonIdentityPallasPoint>,
pub(crate) pk_d_old: Option<DiversifiedTransmissionKey>, pub(crate) pk_d_old: Value<DiversifiedTransmissionKey>,
pub(crate) v_old: Option<NoteValue>, pub(crate) v_old: Value<NoteValue>,
// pub(crate) split: Option<bool>, pub(crate) rho_old: Value<Nullifier>,
pub(crate) rho_old: Option<Nullifier>, pub(crate) psi_old: Value<pallas::Base>,
pub(crate) psi_old: Option<pallas::Base>, pub(crate) rcm_old: Value<NoteCommitTrapdoor>,
pub(crate) rcm_old: Option<NoteCommitTrapdoor>, pub(crate) cm_old: Value<NoteCommitment>,
pub(crate) cm_old: Option<NoteCommitment>, pub(crate) alpha: Value<pallas::Scalar>,
pub(crate) alpha: Option<pallas::Scalar>, pub(crate) ak: Value<SpendValidatingKey>,
pub(crate) ak: Option<SpendValidatingKey>, pub(crate) nk: Value<NullifierDerivingKey>,
pub(crate) nk: Option<NullifierDerivingKey>, pub(crate) rivk: Value<CommitIvkRandomness>,
pub(crate) rivk: Option<CommitIvkRandomness>, pub(crate) g_d_new: Value<NonIdentityPallasPoint>,
pub(crate) g_d_new: Option<NonIdentityPallasPoint>, pub(crate) pk_d_new: Value<DiversifiedTransmissionKey>,
pub(crate) pk_d_new: Option<DiversifiedTransmissionKey>, pub(crate) v_new: Value<NoteValue>,
pub(crate) v_new: Option<NoteValue>, pub(crate) psi_new: Value<pallas::Base>,
pub(crate) psi_new: Option<pallas::Base>, pub(crate) rcm_new: Value<NoteCommitTrapdoor>,
pub(crate) rcm_new: Option<NoteCommitTrapdoor>, pub(crate) rcv: Value<ValueCommitTrapdoor>,
pub(crate) rcv: Option<ValueCommitTrapdoor>,
} }
impl plonk::Circuit<pallas::Base> for Circuit { impl plonk::Circuit<pallas::Base> for Circuit {
@ -359,7 +358,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
)?; )?;
// Witness ak_P. // Witness ak_P.
let ak_P: Option<pallas::Point> = self.ak.as_ref().map(|ak| ak.into()); let ak_P: Value<pallas::Point> = self.ak.as_ref().map(|ak| ak.into());
let ak_P = NonIdentityPoint::new( let ak_P = NonIdentityPoint::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "witness ak_P"), layouter.namespace(|| "witness ak_P"),
@ -409,8 +408,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let v_net_magnitude_sign = { let v_net_magnitude_sign = {
// Witness the magnitude and sign of v_net = v_old - v_new // Witness the magnitude and sign of v_net = v_old - v_new
let v_net_magnitude_sign = { let v_net_magnitude_sign = {
let magnitude_sign = self.v_old.zip(self.v_new).map(|(v_old, v_new)| { let v_net = self.v_old - self.v_new;
let v_net = v_old - v_new; let magnitude_sign = v_net.map(|v_net| {
let (magnitude, sign) = v_net.magnitude_sign(); let (magnitude, sign) = v_net.magnitude_sign();
( (
@ -508,7 +507,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let ak = ak_P.extract_p().inner().clone(); let ak = ak_P.extract_p().inner().clone();
let rivk = ScalarFixed::new( let rivk = ScalarFixed::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "rcv"), layouter.namespace(|| "rivk"),
self.rivk.map(|rivk| rivk.inner()), self.rivk.map(|rivk| rivk.inner()),
)?; )?;
@ -610,7 +609,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let rcm_new = ScalarFixed::new( let rcm_new = ScalarFixed::new(
ecc_chip, ecc_chip,
layouter.namespace(|| "rcm_old"), layouter.namespace(|| "rcm_new"),
self.rcm_new.as_ref().map(|rcm_new| rcm_new.inner()), self.rcm_new.as_ref().map(|rcm_new| rcm_new.inner()),
)?; )?;
@ -691,8 +690,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
/// The verifying key for the Orchard Action circuit. /// The verifying key for the Orchard Action circuit.
#[derive(Debug)] #[derive(Debug)]
pub struct VerifyingKey { pub struct VerifyingKey {
params: halo2_proofs::poly::commitment::Params<vesta::Affine>, pub(crate) params: halo2_proofs::poly::commitment::Params<vesta::Affine>,
vk: plonk::VerifyingKey<vesta::Affine>, pub(crate) vk: plonk::VerifyingKey<vesta::Affine>,
} }
impl VerifyingKey { impl VerifyingKey {
@ -867,6 +866,24 @@ impl Proof {
plonk::verify_proof(&vk.params, &vk.vk, strategy, &instances, &mut transcript) plonk::verify_proof(&vk.params, &vk.vk, strategy, &instances, &mut transcript)
} }
pub(crate) fn add_to_batch(
&self,
batch: &mut BatchVerifier<vesta::Affine>,
instances: Vec<Instance>,
) {
let instances = instances
.iter()
.map(|i| {
i.to_halo2_instance()
.into_iter()
.map(|c| c.into_iter().collect())
.collect()
})
.collect();
batch.add_proof(instances, self.0.clone());
}
/// Constructs a new Proof value. /// Constructs a new Proof value.
pub fn new(bytes: Vec<u8>) -> Self { pub fn new(bytes: Vec<u8>) -> Self {
Proof(bytes) Proof(bytes)
@ -878,7 +895,7 @@ mod tests {
use core::iter; use core::iter;
use ff::Field; use ff::Field;
use halo2_proofs::dev::MockProver; use halo2_proofs::{circuit::Value, dev::MockProver};
use pasta_curves::pallas; use pasta_curves::pallas;
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::OsRng, RngCore};
@ -914,26 +931,25 @@ mod tests {
( (
Circuit { Circuit {
path: Some(path.auth_path()), path: Value::known(path.auth_path()),
pos: Some(path.position()), pos: Value::known(path.position()),
g_d_old: Some(sender_address.g_d()), g_d_old: Value::known(sender_address.g_d()),
pk_d_old: Some(*sender_address.pk_d()), pk_d_old: Value::known(*sender_address.pk_d()),
v_old: Some(spent_note.value()), v_old: Value::known(spent_note.value()),
// split: Some(false), rho_old: Value::known(spent_note.rho()),
rho_old: Some(spent_note.rho()), psi_old: Value::known(spent_note.rseed().psi(&spent_note.rho())),
psi_old: Some(spent_note.rseed().psi(&spent_note.rho())), rcm_old: Value::known(spent_note.rseed().rcm(&spent_note.rho())),
rcm_old: Some(spent_note.rseed().rcm(&spent_note.rho())), cm_old: Value::known(spent_note.commitment()),
cm_old: Some(spent_note.commitment()), alpha: Value::known(alpha),
alpha: Some(alpha), ak: Value::known(ak),
ak: Some(ak), nk: Value::known(nk),
nk: Some(nk), rivk: Value::known(rivk),
rivk: Some(rivk), g_d_new: Value::known(output_note.recipient().g_d()),
g_d_new: Some(output_note.recipient().g_d()), pk_d_new: Value::known(*output_note.recipient().pk_d()),
pk_d_new: Some(*output_note.recipient().pk_d()), v_new: Value::known(output_note.value()),
v_new: Some(output_note.value()), psi_new: Value::known(output_note.rseed().psi(&output_note.rho())),
psi_new: Some(output_note.rseed().psi(&output_note.rho())), rcm_new: Value::known(output_note.rseed().rcm(&output_note.rho())),
rcm_new: Some(output_note.rseed().rcm(&output_note.rho())), rcv: Value::known(rcv),
rcv: Some(rcv),
}, },
Instance { Instance {
anchor, anchor,
@ -1101,26 +1117,25 @@ mod tests {
.unwrap(); .unwrap();
let circuit = Circuit { let circuit = Circuit {
path: None, path: Value::unknown(),
pos: None, pos: Value::unknown(),
g_d_old: None, g_d_old: Value::unknown(),
pk_d_old: None, pk_d_old: Value::unknown(),
v_old: None, v_old: Value::unknown(),
rho_old: None, rho_old: Value::unknown(),
psi_old: None, psi_old: Value::unknown(),
rcm_old: None, rcm_old: Value::unknown(),
cm_old: None, cm_old: Value::unknown(),
alpha: None, alpha: Value::unknown(),
ak: None, ak: Value::unknown(),
nk: None, nk: Value::unknown(),
rivk: None, rivk: Value::unknown(),
g_d_new: None, g_d_new: Value::unknown(),
pk_d_new: None, pk_d_new: Value::unknown(),
v_new: None, v_new: Value::unknown(),
psi_new: None, psi_new: Value::unknown(),
rcm_new: None, rcm_new: Value::unknown(),
rcv: None, rcv: Value::unknown(),
// split: None,
}; };
halo2_proofs::dev::CircuitLayout::default() halo2_proofs::dev::CircuitLayout::default()
.show_labels(false) .show_labels(false)

View File

@ -1,7 +1,7 @@
use core::iter; use core::iter;
use halo2_proofs::{ use halo2_proofs::{
circuit::{AssignedCell, Layouter}, circuit::{AssignedCell, Layouter, Value},
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector}, plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector},
poly::Rotation, poly::Rotation,
}; };
@ -417,11 +417,11 @@ pub(in crate::circuit) mod gadgets {
// Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output // Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output
// the running sum at the end of it. If a_prime < 2^130, the running sum // the running sum at the end of it. If a_prime < 2^130, the running sum
// will be 0. // will be 0.
let a_prime = a.value().map(|a| { let a_prime = {
let two_pow_130 = pallas::Base::from_u128(1u128 << 65).square(); let two_pow_130 = Value::known(pallas::Base::from_u128(1u128 << 65).square());
let t_p = pallas::Base::from_u128(T_P); let t_p = Value::known(pallas::Base::from_u128(T_P));
a + two_pow_130 - t_p a.value() + two_pow_130 - t_p
}); };
let zs = lookup_config.witness_check( let zs = lookup_config.witness_check(
layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"), layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"),
a_prime, a_prime,
@ -461,12 +461,12 @@ pub(in crate::circuit) mod gadgets {
// Decompose the low 140 bits of b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P, and output // Decompose the low 140 bits of b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P, and output
// the running sum at the end of it. If b2_c_prime < 2^140, the running sum will be 0. // the running sum at the end of it. If b2_c_prime < 2^140, the running sum will be 0.
let b2_c_prime = b_2.inner().value().zip(c.value()).map(|(b_2, c)| { let b2_c_prime = {
let two_pow_5 = pallas::Base::from(1 << 5); let two_pow_5 = Value::known(pallas::Base::from(1 << 5));
let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square(); let two_pow_140 = Value::known(pallas::Base::from_u128(1u128 << 70).square());
let t_p = pallas::Base::from_u128(T_P); let t_p = Value::known(pallas::Base::from_u128(T_P));
b_2 + c * two_pow_5 + two_pow_140 - t_p b_2.inner().value() + c.value() * two_pow_5 + two_pow_140 - t_p
}); };
let zs = lookup_config.witness_check( let zs = lookup_config.witness_check(
layouter.namespace(|| "Decompose low 140 bits of (b_2 + c * 2^5 + 2^140 - t_P)"), layouter.namespace(|| "Decompose low 140 bits of (b_2 + c * 2^5 + 2^140 - t_P)"),
b2_c_prime, b2_c_prime,
@ -535,7 +535,7 @@ impl CommitIvkConfig {
|| "Witness b_1", || "Witness b_1",
self.advices[4], self.advices[4],
offset, offset,
|| gate_cells.b_1.inner().ok_or(Error::Synthesis), || *gate_cells.b_1.inner(),
)?; )?;
// Copy in `b_2` // Copy in `b_2`
@ -603,7 +603,7 @@ impl CommitIvkConfig {
|| "Witness d_1", || "Witness d_1",
self.advices[4], self.advices[4],
offset, offset,
|| gate_cells.d_1.inner().ok_or(Error::Synthesis), || *gate_cells.d_1.inner(),
)?; )?;
// Copy in z13_c // Copy in z13_c
@ -646,10 +646,10 @@ struct GateCells {
ak: AssignedCell<pallas::Base, pallas::Base>, ak: AssignedCell<pallas::Base, pallas::Base>,
nk: AssignedCell<pallas::Base, pallas::Base>, nk: AssignedCell<pallas::Base, pallas::Base>,
b_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, b_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
b_1: RangeConstrained<pallas::Base, Option<pallas::Base>>, b_1: RangeConstrained<pallas::Base, Value<pallas::Base>>,
b_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, b_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
d_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, d_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
d_1: RangeConstrained<pallas::Base, Option<pallas::Base>>, d_1: RangeConstrained<pallas::Base, Value<pallas::Base>>,
z13_a: AssignedCell<pallas::Base, pallas::Base>, z13_a: AssignedCell<pallas::Base, pallas::Base>,
a_prime: AssignedCell<pallas::Base, pallas::Base>, a_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_a_prime: AssignedCell<pallas::Base, pallas::Base>, z13_a_prime: AssignedCell<pallas::Base, pallas::Base>,
@ -680,7 +680,7 @@ mod tests {
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions}, utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
}; };
use halo2_proofs::{ use halo2_proofs::{
circuit::{AssignedCell, Layouter, SimpleFloorPlanner}, circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value},
dev::MockProver, dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error}, plonk::{Circuit, ConstraintSystem, Error},
}; };
@ -691,8 +691,8 @@ mod tests {
fn commit_ivk() { fn commit_ivk() {
#[derive(Default)] #[derive(Default)]
struct MyCircuit { struct MyCircuit {
ak: Option<pallas::Base>, ak: Value<pallas::Base>,
nk: Option<pallas::Base>, nk: Value<pallas::Base>,
} }
impl UtilitiesInstructions<pallas::Base> for MyCircuit { impl UtilitiesInstructions<pallas::Base> for MyCircuit {
@ -809,8 +809,11 @@ mod tests {
// Use a random scalar for rivk // Use a random scalar for rivk
let rivk = pallas::Scalar::random(OsRng); let rivk = pallas::Scalar::random(OsRng);
let rivk_gadget = let rivk_gadget = ScalarFixed::new(
ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "rivk"), Some(rivk))?; ecc_chip.clone(),
layouter.namespace(|| "rivk"),
Value::known(rivk),
)?;
let ivk = gadgets::commit_ivk( let ivk = gadgets::commit_ivk(
sinsemilla_chip, sinsemilla_chip,
@ -822,34 +825,29 @@ mod tests {
rivk_gadget, rivk_gadget,
)?; )?;
let expected_ivk = { self.ak
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION); .zip(self.nk)
// Hash ak || nk .zip(ivk.inner().value())
domain .assert_if_known(|((ak, nk), ivk)| {
.short_commit( let expected_ivk = {
iter::empty() let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
.chain( // Hash ak || nk
self.ak domain
.unwrap() .short_commit(
.to_le_bits() iter::empty()
.iter() .chain(
.by_vals() ak.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
.take(L_ORCHARD_BASE), )
.chain(
nk.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
),
&rivk,
) )
.chain( .unwrap()
self.nk };
.unwrap()
.to_le_bits()
.iter()
.by_vals()
.take(L_ORCHARD_BASE),
),
&rivk,
)
.unwrap()
};
assert_eq!(&expected_ivk, ivk.inner().value().unwrap()); &&expected_ivk == ivk
});
Ok(()) Ok(())
} }
@ -860,38 +858,38 @@ mod tests {
let circuits = [ let circuits = [
// `ak` = 0, `nk` = 0 // `ak` = 0, `nk` = 0
MyCircuit { MyCircuit {
ak: Some(pallas::Base::zero()), ak: Value::known(pallas::Base::zero()),
nk: Some(pallas::Base::zero()), nk: Value::known(pallas::Base::zero()),
}, },
// `ak` = T_Q - 1, `nk` = T_Q - 1 // `ak` = T_Q - 1, `nk` = T_Q - 1
MyCircuit { MyCircuit {
ak: Some(pallas::Base::from_u128(T_Q - 1)), ak: Value::known(pallas::Base::from_u128(T_Q - 1)),
nk: Some(pallas::Base::from_u128(T_Q - 1)), nk: Value::known(pallas::Base::from_u128(T_Q - 1)),
}, },
// `ak` = T_Q, `nk` = T_Q // `ak` = T_Q, `nk` = T_Q
MyCircuit { MyCircuit {
ak: Some(pallas::Base::from_u128(T_Q)), ak: Value::known(pallas::Base::from_u128(T_Q)),
nk: Some(pallas::Base::from_u128(T_Q)), nk: Value::known(pallas::Base::from_u128(T_Q)),
}, },
// `ak` = 2^127 - 1, `nk` = 2^127 - 1 // `ak` = 2^127 - 1, `nk` = 2^127 - 1
MyCircuit { MyCircuit {
ak: Some(pallas::Base::from_u128((1 << 127) - 1)), ak: Value::known(pallas::Base::from_u128((1 << 127) - 1)),
nk: Some(pallas::Base::from_u128((1 << 127) - 1)), nk: Value::known(pallas::Base::from_u128((1 << 127) - 1)),
}, },
// `ak` = 2^127, `nk` = 2^127 // `ak` = 2^127, `nk` = 2^127
MyCircuit { MyCircuit {
ak: Some(pallas::Base::from_u128(1 << 127)), ak: Value::known(pallas::Base::from_u128(1 << 127)),
nk: Some(pallas::Base::from_u128(1 << 127)), nk: Value::known(pallas::Base::from_u128(1 << 127)),
}, },
// `ak` = 2^254 - 1, `nk` = 2^254 - 1 // `ak` = 2^254 - 1, `nk` = 2^254 - 1
MyCircuit { MyCircuit {
ak: Some(two_pow_254 - pallas::Base::one()), ak: Value::known(two_pow_254 - pallas::Base::one()),
nk: Some(two_pow_254 - pallas::Base::one()), nk: Value::known(two_pow_254 - pallas::Base::one()),
}, },
// `ak` = 2^254, `nk` = 2^254 // `ak` = 2^254, `nk` = 2^254
MyCircuit { MyCircuit {
ak: Some(two_pow_254), ak: Value::known(two_pow_254),
nk: Some(two_pow_254), nk: Value::known(two_pow_254),
}, },
]; ];

View File

@ -21,7 +21,7 @@ use halo2_gadgets::{
}; };
use halo2_proofs::{ use halo2_proofs::{
arithmetic::FieldExt, arithmetic::FieldExt,
circuit::{AssignedCell, Chip, Layouter}, circuit::{AssignedCell, Chip, Layouter, Value},
plonk::{self, Advice, Assigned, Column}, plonk::{self, Advice, Assigned, Column},
}; };
@ -96,21 +96,14 @@ pub(in crate::circuit) trait AddInstruction<F: FieldExt>: Chip<F> {
pub(in crate::circuit) fn assign_free_advice<F: Field, V: Copy>( pub(in crate::circuit) fn assign_free_advice<F: Field, V: Copy>(
mut layouter: impl Layouter<F>, mut layouter: impl Layouter<F>,
column: Column<Advice>, column: Column<Advice>,
value: Option<V>, value: Value<V>,
) -> Result<AssignedCell<V, F>, plonk::Error> ) -> Result<AssignedCell<V, F>, plonk::Error>
where where
for<'v> Assigned<F>: From<&'v V>, for<'v> Assigned<F>: From<&'v V>,
{ {
layouter.assign_region( layouter.assign_region(
|| "load private", || "load private",
|mut region| { |mut region| region.assign_advice(|| "load private", column, 0, || value),
region.assign_advice(
|| "load private",
column,
0,
|| value.ok_or(plonk::Error::Synthesis),
)
},
) )
} }

View File

@ -74,12 +74,7 @@ impl AddInstruction<pallas::Base> for AddChip {
b.copy_advice(|| "copy b", &mut region, self.config.b, 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); let scalar_val = a.value().zip(b.value()).map(|(a, b)| a + b);
region.assign_advice( region.assign_advice(|| "c", self.config.c, 0, || scalar_val)
|| "c",
self.config.c,
0,
|| scalar_val.ok_or(plonk::Error::Synthesis),
)
}, },
) )
} }

View File

@ -1,7 +1,7 @@
use core::iter; use core::iter;
use halo2_proofs::{ use halo2_proofs::{
circuit::{AssignedCell, Layouter}, circuit::{AssignedCell, Layouter, Value},
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector}, plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector},
poly::Rotation, poly::Rotation,
}; };
@ -128,8 +128,8 @@ impl DecomposeB {
( (
NoteCommitPiece, NoteCommitPiece,
RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
RangeConstrained<pallas::Base, Option<pallas::Base>>, RangeConstrained<pallas::Base, Value<pallas::Base>>,
RangeConstrained<pallas::Base, Option<pallas::Base>>, RangeConstrained<pallas::Base, Value<pallas::Base>>,
RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
), ),
Error, Error,
@ -170,7 +170,7 @@ impl DecomposeB {
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
b: NoteCommitPiece, b: NoteCommitPiece,
b_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, b_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
b_1: RangeConstrained<pallas::Base, Option<pallas::Base>>, b_1: RangeConstrained<pallas::Base, Value<pallas::Base>>,
b_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, b_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
b_3: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, b_3: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> { ) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> {
@ -184,12 +184,7 @@ impl DecomposeB {
.copy_advice(|| "b", &mut region, self.col_l, 0)?; .copy_advice(|| "b", &mut region, self.col_l, 0)?;
b_0.inner() b_0.inner()
.copy_advice(|| "b_0", &mut region, self.col_m, 0)?; .copy_advice(|| "b_0", &mut region, self.col_m, 0)?;
let b_1 = region.assign_advice( let b_1 = region.assign_advice(|| "b_1", self.col_r, 0, || *b_1.inner())?;
|| "b_1",
self.col_r,
0,
|| b_1.inner().ok_or(Error::Synthesis),
)?;
b_2.inner() b_2.inner()
.copy_advice(|| "b_2", &mut region, self.col_m, 1)?; .copy_advice(|| "b_2", &mut region, self.col_m, 1)?;
@ -277,8 +272,8 @@ impl DecomposeD {
) -> Result< ) -> Result<
( (
NoteCommitPiece, NoteCommitPiece,
RangeConstrained<pallas::Base, Option<pallas::Base>>, RangeConstrained<pallas::Base, Value<pallas::Base>>,
RangeConstrained<pallas::Base, Option<pallas::Base>>, RangeConstrained<pallas::Base, Value<pallas::Base>>,
RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
), ),
Error, Error,
@ -313,7 +308,7 @@ impl DecomposeD {
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
d: NoteCommitPiece, d: NoteCommitPiece,
d_0: RangeConstrained<pallas::Base, Option<pallas::Base>>, d_0: RangeConstrained<pallas::Base, Value<pallas::Base>>,
d_1: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, d_1: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
d_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, d_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
z1_d: AssignedCell<pallas::Base, pallas::Base>, z1_d: AssignedCell<pallas::Base, pallas::Base>,
@ -326,12 +321,7 @@ impl DecomposeD {
d.inner() d.inner()
.cell_value() .cell_value()
.copy_advice(|| "d", &mut region, self.col_l, 0)?; .copy_advice(|| "d", &mut region, self.col_l, 0)?;
let d_0 = region.assign_advice( let d_0 = region.assign_advice(|| "d_0", self.col_m, 0, || *d_0.inner())?;
|| "d_0",
self.col_m,
0,
|| d_0.inner().ok_or(Error::Synthesis),
)?;
d_1.inner() d_1.inner()
.copy_advice(|| "d_1", &mut region, self.col_r, 0)?; .copy_advice(|| "d_1", &mut region, self.col_r, 0)?;
@ -529,7 +519,7 @@ impl DecomposeG {
) -> Result< ) -> Result<
( (
NoteCommitPiece, NoteCommitPiece,
RangeConstrained<pallas::Base, Option<pallas::Base>>, RangeConstrained<pallas::Base, Value<pallas::Base>>,
RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
), ),
Error, Error,
@ -561,7 +551,7 @@ impl DecomposeG {
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
g: NoteCommitPiece, g: NoteCommitPiece,
g_0: RangeConstrained<pallas::Base, Option<pallas::Base>>, g_0: RangeConstrained<pallas::Base, Value<pallas::Base>>,
g_1: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, g_1: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
z1_g: AssignedCell<pallas::Base, pallas::Base>, z1_g: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> { ) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> {
@ -573,12 +563,7 @@ impl DecomposeG {
g.inner() g.inner()
.cell_value() .cell_value()
.copy_advice(|| "g", &mut region, self.col_l, 0)?; .copy_advice(|| "g", &mut region, self.col_l, 0)?;
let g_0 = region.assign_advice( let g_0 = region.assign_advice(|| "g_0", self.col_m, 0, || *g_0.inner())?;
|| "g_0",
self.col_m,
0,
|| g_0.inner().ok_or(Error::Synthesis),
)?;
g_1.inner() g_1.inner()
.copy_advice(|| "g_1", &mut region, self.col_l, 1)?; .copy_advice(|| "g_1", &mut region, self.col_l, 1)?;
@ -656,7 +641,7 @@ impl DecomposeH {
( (
NoteCommitPiece, NoteCommitPiece,
RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
RangeConstrained<pallas::Base, Option<pallas::Base>>, RangeConstrained<pallas::Base, Value<pallas::Base>>,
), ),
Error, Error,
> { > {
@ -677,7 +662,7 @@ impl DecomposeH {
[ [
h_0.value(), h_0.value(),
h_1, h_1,
RangeConstrained::bitrange_of(Some(&pallas::Base::zero()), 0..4), RangeConstrained::bitrange_of(Value::known(&pallas::Base::zero()), 0..4),
], ],
)?; )?;
@ -689,7 +674,7 @@ impl DecomposeH {
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
h: NoteCommitPiece, h: NoteCommitPiece,
h_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, h_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
h_1: RangeConstrained<pallas::Base, Option<pallas::Base>>, h_1: RangeConstrained<pallas::Base, Value<pallas::Base>>,
) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> { ) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> {
layouter.assign_region( layouter.assign_region(
|| "NoteCommit MessagePiece h", || "NoteCommit MessagePiece h",
@ -701,12 +686,7 @@ impl DecomposeH {
.copy_advice(|| "h", &mut region, self.col_l, 0)?; .copy_advice(|| "h", &mut region, self.col_l, 0)?;
h_0.inner() h_0.inner()
.copy_advice(|| "h_0", &mut region, self.col_m, 0)?; .copy_advice(|| "h_0", &mut region, self.col_m, 0)?;
let h_1 = region.assign_advice( let h_1 = region.assign_advice(|| "h_1", self.col_r, 0, || *h_1.inner())?;
|| "h_1",
self.col_r,
0,
|| h_1.inner().ok_or(Error::Synthesis),
)?;
Ok(h_1) Ok(h_1)
}, },
@ -1357,10 +1337,10 @@ impl YCanonicity {
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>, y: AssignedCell<pallas::Base, pallas::Base>,
lsb: RangeConstrained<pallas::Base, Option<pallas::Base>>, lsb: RangeConstrained<pallas::Base, Value<pallas::Base>>,
k_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, k_0: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
k_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, k_2: RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>,
k_3: RangeConstrained<pallas::Base, Option<pallas::Base>>, k_3: RangeConstrained<pallas::Base, Value<pallas::Base>>,
j: AssignedCell<pallas::Base, pallas::Base>, j: AssignedCell<pallas::Base, pallas::Base>,
z1_j: AssignedCell<pallas::Base, pallas::Base>, z1_j: AssignedCell<pallas::Base, pallas::Base>,
z13_j: AssignedCell<pallas::Base, pallas::Base>, z13_j: AssignedCell<pallas::Base, pallas::Base>,
@ -1381,12 +1361,7 @@ impl YCanonicity {
y.copy_advice(|| "copy y", &mut region, self.advices[5], offset)?; y.copy_advice(|| "copy y", &mut region, self.advices[5], offset)?;
// Witness LSB. // Witness LSB.
let lsb = region let lsb = region
.assign_advice( .assign_advice(|| "witness LSB", self.advices[6], offset, || *lsb.inner())
|| "witness LSB",
self.advices[6],
offset,
|| lsb.inner().ok_or(Error::Synthesis),
)
// SAFETY: This is sound because we just assigned this cell from a // SAFETY: This is sound because we just assigned this cell from a
// range-constrained value. // range-constrained value.
.map(|cell| RangeConstrained::unsound_unchecked(cell, lsb.num_bits()))?; .map(|cell| RangeConstrained::unsound_unchecked(cell, lsb.num_bits()))?;
@ -1401,7 +1376,7 @@ impl YCanonicity {
|| "witness k_3", || "witness k_3",
self.advices[9], self.advices[9],
offset, offset,
|| k_3.inner().ok_or(Error::Synthesis), || *k_3.inner(),
)?; )?;
lsb lsb
@ -1586,7 +1561,7 @@ impl NoteCommitChip {
} }
pub(in crate::circuit) mod gadgets { pub(in crate::circuit) mod gadgets {
use halo2_proofs::circuit::Chip; use halo2_proofs::circuit::{Chip, Value};
use super::*; use super::*;
@ -1817,11 +1792,11 @@ pub(in crate::circuit) mod gadgets {
// Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output // Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output
// the running sum at the end of it. If a_prime < 2^130, the running sum // the running sum at the end of it. If a_prime < 2^130, the running sum
// will be 0. // will be 0.
let a_prime = a.value().map(|a| { let a_prime = {
let two_pow_130 = pallas::Base::from_u128(1u128 << 65).square(); let two_pow_130 = Value::known(pallas::Base::from_u128(1u128 << 65).square());
let t_p = pallas::Base::from_u128(T_P); let t_p = Value::known(pallas::Base::from_u128(T_P));
a + two_pow_130 - t_p a.value() + two_pow_130 - t_p
}); };
let zs = lookup_config.witness_check( let zs = lookup_config.witness_check(
layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"), layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"),
a_prime, a_prime,
@ -1856,12 +1831,12 @@ pub(in crate::circuit) mod gadgets {
// Decompose the low 140 bits of b3_c_prime = b_3 + 2^4 c + 2^140 - t_P, // Decompose the low 140 bits of b3_c_prime = b_3 + 2^4 c + 2^140 - t_P,
// and output the running sum at the end of it. // and output the running sum at the end of it.
// If b3_c_prime < 2^140, the running sum will be 0. // If b3_c_prime < 2^140, the running sum will be 0.
let b3_c_prime = b_3.inner().value().zip(c.value()).map(|(b_3, c)| { let b3_c_prime = {
let two_pow_4 = pallas::Base::from(1u64 << 4); let two_pow_4 = Value::known(pallas::Base::from(1u64 << 4));
let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square(); let two_pow_140 = Value::known(pallas::Base::from_u128(1u128 << 70).square());
let t_p = pallas::Base::from_u128(T_P); let t_p = Value::known(pallas::Base::from_u128(T_P));
b_3 + (two_pow_4 * c) + two_pow_140 - t_p b_3.inner().value() + (two_pow_4 * c.value()) + two_pow_140 - t_p
}); };
let zs = lookup_config.witness_check( let zs = lookup_config.witness_check(
layouter.namespace(|| "Decompose low 140 bits of (b_3 + 2^4 c + 2^140 - t_P)"), layouter.namespace(|| "Decompose low 140 bits of (b_3 + 2^4 c + 2^140 - t_P)"),
@ -1894,12 +1869,12 @@ pub(in crate::circuit) mod gadgets {
// to 130 bits. z13_f == 0 is directly checked in the gate. // to 130 bits. z13_f == 0 is directly checked in the gate.
// - 0 ≤ e_1 + 2^4 f + 2^140 - t_P < 2^140 (14 ten-bit lookups) // - 0 ≤ e_1 + 2^4 f + 2^140 - t_P < 2^140 (14 ten-bit lookups)
let e1_f_prime = e_1.inner().value().zip(f.value()).map(|(e_1, f)| { let e1_f_prime = {
let two_pow_4 = pallas::Base::from(1u64 << 4); let two_pow_4 = Value::known(pallas::Base::from(1u64 << 4));
let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square(); let two_pow_140 = Value::known(pallas::Base::from_u128(1u128 << 70).square());
let t_p = pallas::Base::from_u128(T_P); let t_p = Value::known(pallas::Base::from_u128(T_P));
e_1 + (two_pow_4 * f) + two_pow_140 - t_p e_1.inner().value() + (two_pow_4 * f.value()) + two_pow_140 - t_p
}); };
// Decompose the low 140 bits of e1_f_prime = e_1 + 2^4 f + 2^140 - t_P, // Decompose the low 140 bits of e1_f_prime = e_1 + 2^4 f + 2^140 - t_P,
// and output the running sum at the end of it. // and output the running sum at the end of it.
@ -1936,12 +1911,12 @@ pub(in crate::circuit) mod gadgets {
// Decompose the low 130 bits of g1_g2_prime = g_1 + (2^9)g_2 + 2^130 - t_P, // Decompose the low 130 bits of g1_g2_prime = g_1 + (2^9)g_2 + 2^130 - t_P,
// and output the running sum at the end of it. // and output the running sum at the end of it.
// If g1_g2_prime < 2^130, the running sum will be 0. // If g1_g2_prime < 2^130, the running sum will be 0.
let g1_g2_prime = g_1.inner().value().zip(g_2.value()).map(|(g_1, g_2)| { let g1_g2_prime = {
let two_pow_9 = pallas::Base::from(1u64 << 9); let two_pow_9 = Value::known(pallas::Base::from(1u64 << 9));
let two_pow_130 = pallas::Base::from_u128(1u128 << 65).square(); let two_pow_130 = Value::known(pallas::Base::from_u128(1u128 << 65).square());
let t_p = pallas::Base::from_u128(T_P); let t_p = Value::known(pallas::Base::from_u128(T_P));
g_1 + (two_pow_9 * g_2) + two_pow_130 - t_p g_1.inner().value() + (two_pow_9 * g_2.value()) + two_pow_130 - t_p
}); };
let zs = lookup_config.witness_check( let zs = lookup_config.witness_check(
layouter.namespace(|| "Decompose low 130 bits of (g_1 + (2^9)g_2 + 2^130 - t_P)"), layouter.namespace(|| "Decompose low 130 bits of (g_1 + (2^9)g_2 + 2^130 - t_P)"),
@ -1966,7 +1941,7 @@ pub(in crate::circuit) mod gadgets {
y_canon: &YCanonicity, y_canon: &YCanonicity,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>, y: AssignedCell<pallas::Base, pallas::Base>,
lsb: RangeConstrained<pallas::Base, Option<pallas::Base>>, lsb: RangeConstrained<pallas::Base, Value<pallas::Base>>,
) -> Result<RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, Error> ) -> Result<RangeConstrained<pallas::Base, AssignedCell<pallas::Base, pallas::Base>>, Error>
{ {
// Decompose the field element // Decompose the field element
@ -1997,16 +1972,11 @@ pub(in crate::circuit) mod gadgets {
// Decompose j = LSB + (2)k_0 + (2^10)k_1 using 25 ten-bit lookups. // Decompose j = LSB + (2)k_0 + (2^10)k_1 using 25 ten-bit lookups.
let (j, z1_j, z13_j) = { let (j, z1_j, z13_j) = {
let j = lsb let j = {
.inner() let two = Value::known(pallas::Base::from(2));
.value() let two_pow_10 = Value::known(pallas::Base::from(1 << 10));
.zip(k_0.inner().value()) lsb.inner().value() + two * k_0.inner().value() + two_pow_10 * k_1.inner().value()
.zip(k_1.inner().value()) };
.map(|((lsb, k_0), k_1)| {
let two = pallas::Base::from(2);
let two_pow_10 = pallas::Base::from(1 << 10);
lsb + two * k_0 + two_pow_10 * k_1
});
let zs = lookup_config.witness_check( let zs = lookup_config.witness_check(
layouter.namespace(|| "Decompose j = LSB + (2)k_0 + (2^10)k_1"), layouter.namespace(|| "Decompose j = LSB + (2)k_0 + (2^10)k_1"),
j, j,
@ -2069,7 +2039,7 @@ mod tests {
use ff::{Field, PrimeField, PrimeFieldBits}; use ff::{Field, PrimeField, PrimeFieldBits};
use group::Curve; use group::Curve;
use halo2_proofs::{ use halo2_proofs::{
circuit::{Layouter, SimpleFloorPlanner}, circuit::{Layouter, SimpleFloorPlanner, Value},
dev::MockProver, dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error}, plonk::{Circuit, ConstraintSystem, Error},
}; };
@ -2084,12 +2054,12 @@ mod tests {
fn note_commit() { fn note_commit() {
#[derive(Default)] #[derive(Default)]
struct MyCircuit { struct MyCircuit {
gd_x: Option<pallas::Base>, gd_x: Value<pallas::Base>,
gd_y_lsb: Option<pallas::Base>, gd_y_lsb: Value<pallas::Base>,
pkd_x: Option<pallas::Base>, pkd_x: Value<pallas::Base>,
pkd_y_lsb: Option<pallas::Base>, pkd_y_lsb: Value<pallas::Base>,
rho: Option<pallas::Base>, rho: Value<pallas::Base>,
psi: Option<pallas::Base>, psi: Value<pallas::Base>,
} }
impl Circuit<pallas::Base> for MyCircuit { impl Circuit<pallas::Base> for MyCircuit {
@ -2235,7 +2205,7 @@ mod tests {
assign_free_advice( assign_free_advice(
layouter.namespace(|| "witness value"), layouter.namespace(|| "witness value"),
note_commit_config.advices[0], note_commit_config.advices[0],
Some(value), Value::known(value),
)? )?
}; };
@ -2254,8 +2224,11 @@ mod tests {
)?; )?;
let rcm = pallas::Scalar::random(OsRng); let rcm = pallas::Scalar::random(OsRng);
let rcm_gadget = let rcm_gadget = ScalarFixed::new(
ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "rcm"), Some(rcm))?; ecc_chip.clone(),
layouter.namespace(|| "rcm"),
Value::known(rcm),
)?;
let cm = gadgets::note_commit( let cm = gadgets::note_commit(
layouter.namespace(|| "Hash NoteCommit pieces"), layouter.namespace(|| "Hash NoteCommit pieces"),
@ -2273,53 +2246,40 @@ mod tests {
let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION); let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
// Hash g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi // Hash g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi
let lsb = |y_lsb: pallas::Base| y_lsb == pallas::Base::one(); let lsb = |y_lsb: pallas::Base| y_lsb == pallas::Base::one();
let point = domain let point = self
.commit( .gd_x
iter::empty() .zip(self.gd_y_lsb)
.chain( .zip(self.pkd_x.zip(self.pkd_y_lsb))
self.gd_x .zip(self.rho.zip(self.psi))
.unwrap() .map(|(((gd_x, gd_y_lsb), (pkd_x, pkd_y_lsb)), (rho, psi))| {
.to_le_bits() domain
.iter() .commit(
.by_vals() iter::empty()
.take(L_ORCHARD_BASE), .chain(
gd_x.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
)
.chain(Some(lsb(gd_y_lsb)))
.chain(
pkd_x
.to_le_bits()
.iter()
.by_vals()
.take(L_ORCHARD_BASE),
)
.chain(Some(lsb(pkd_y_lsb)))
.chain(value.to_le_bits().iter().by_vals().take(L_VALUE))
.chain(
rho.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
)
.chain(
psi.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
),
&rcm,
) )
.chain(Some(lsb(self.gd_y_lsb.unwrap()))) .unwrap()
.chain( .to_affine()
self.pkd_x });
.unwrap() NonIdentityPoint::new(ecc_chip, layouter.namespace(|| "witness cm"), point)?
.to_le_bits()
.iter()
.by_vals()
.take(L_ORCHARD_BASE),
)
.chain(Some(lsb(self.pkd_y_lsb.unwrap())))
.chain(value.to_le_bits().iter().by_vals().take(L_VALUE))
.chain(
self.rho
.unwrap()
.to_le_bits()
.iter()
.by_vals()
.take(L_ORCHARD_BASE),
)
.chain(
self.psi
.unwrap()
.to_le_bits()
.iter()
.by_vals()
.take(L_ORCHARD_BASE),
),
&rcm,
)
.unwrap()
.to_affine();
NonIdentityPoint::new(
ecc_chip,
layouter.namespace(|| "witness cm"),
Some(point),
)?
}; };
cm.constrain_equal(layouter.namespace(|| "cm == expected cm"), &expected_cm) cm.constrain_equal(layouter.namespace(|| "cm == expected cm"), &expected_cm)
} }
@ -2331,66 +2291,66 @@ mod tests {
// `gd_x` = -1, `pkd_x` = -1 (these have to be x-coordinates of curve points) // `gd_x` = -1, `pkd_x` = -1 (these have to be x-coordinates of curve points)
// `rho` = 0, `psi` = 0 // `rho` = 0, `psi` = 0
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::one()), gd_y_lsb: Value::known(pallas::Base::one()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::one()), pkd_y_lsb: Value::known(pallas::Base::one()),
rho: Some(pallas::Base::zero()), rho: Value::known(pallas::Base::zero()),
psi: Some(pallas::Base::zero()), psi: Value::known(pallas::Base::zero()),
}, },
// `rho` = T_Q - 1, `psi` = T_Q - 1 // `rho` = T_Q - 1, `psi` = T_Q - 1
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::zero()), gd_y_lsb: Value::known(pallas::Base::zero()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::zero()), pkd_y_lsb: Value::known(pallas::Base::zero()),
rho: Some(pallas::Base::from_u128(T_Q - 1)), rho: Value::known(pallas::Base::from_u128(T_Q - 1)),
psi: Some(pallas::Base::from_u128(T_Q - 1)), psi: Value::known(pallas::Base::from_u128(T_Q - 1)),
}, },
// `rho` = T_Q, `psi` = T_Q // `rho` = T_Q, `psi` = T_Q
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::one()), gd_y_lsb: Value::known(pallas::Base::one()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::zero()), pkd_y_lsb: Value::known(pallas::Base::zero()),
rho: Some(pallas::Base::from_u128(T_Q)), rho: Value::known(pallas::Base::from_u128(T_Q)),
psi: Some(pallas::Base::from_u128(T_Q)), psi: Value::known(pallas::Base::from_u128(T_Q)),
}, },
// `rho` = 2^127 - 1, `psi` = 2^127 - 1 // `rho` = 2^127 - 1, `psi` = 2^127 - 1
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::zero()), gd_y_lsb: Value::known(pallas::Base::zero()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::one()), pkd_y_lsb: Value::known(pallas::Base::one()),
rho: Some(pallas::Base::from_u128((1 << 127) - 1)), rho: Value::known(pallas::Base::from_u128((1 << 127) - 1)),
psi: Some(pallas::Base::from_u128((1 << 127) - 1)), psi: Value::known(pallas::Base::from_u128((1 << 127) - 1)),
}, },
// `rho` = 2^127, `psi` = 2^127 // `rho` = 2^127, `psi` = 2^127
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::zero()), gd_y_lsb: Value::known(pallas::Base::zero()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::zero()), pkd_y_lsb: Value::known(pallas::Base::zero()),
rho: Some(pallas::Base::from_u128(1 << 127)), rho: Value::known(pallas::Base::from_u128(1 << 127)),
psi: Some(pallas::Base::from_u128(1 << 127)), psi: Value::known(pallas::Base::from_u128(1 << 127)),
}, },
// `rho` = 2^254 - 1, `psi` = 2^254 - 1 // `rho` = 2^254 - 1, `psi` = 2^254 - 1
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::one()), gd_y_lsb: Value::known(pallas::Base::one()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::one()), pkd_y_lsb: Value::known(pallas::Base::one()),
rho: Some(two_pow_254 - pallas::Base::one()), rho: Value::known(two_pow_254 - pallas::Base::one()),
psi: Some(two_pow_254 - pallas::Base::one()), psi: Value::known(two_pow_254 - pallas::Base::one()),
}, },
// `rho` = 2^254, `psi` = 2^254 // `rho` = 2^254, `psi` = 2^254
MyCircuit { MyCircuit {
gd_x: Some(-pallas::Base::one()), gd_x: Value::known(-pallas::Base::one()),
gd_y_lsb: Some(pallas::Base::one()), gd_y_lsb: Value::known(pallas::Base::one()),
pkd_x: Some(-pallas::Base::one()), pkd_x: Value::known(-pallas::Base::one()),
pkd_y_lsb: Some(pallas::Base::zero()), pkd_y_lsb: Value::known(pallas::Base::zero()),
rho: Some(two_pow_254), rho: Value::known(two_pow_254),
psi: Some(two_pow_254), psi: Value::known(two_pow_254),
}, },
]; ];

View File

@ -118,6 +118,11 @@ impl OrchardDomain {
rho: *act.nullifier(), rho: *act.nullifier(),
} }
} }
/// Constructs a domain from a nullifier.
pub fn for_nullifier(nullifier: Nullifier) -> Self {
OrchardDomain { rho: nullifier }
}
} }
impl Domain for OrchardDomain { impl Domain for OrchardDomain {
@ -355,6 +360,28 @@ impl ShieldedOutput<OrchardDomain, COMPACT_NOTE_SIZE> for CompactAction {
} }
} }
impl CompactAction {
/// Create a CompactAction from its constituent parts
pub fn from_parts(
nullifier: Nullifier,
cmx: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 52],
) -> Self {
Self {
nullifier,
cmx,
ephemeral_key,
enc_ciphertext,
}
}
///Returns the nullifier of the note being spent.
pub fn nullifier(&self) -> Nullifier {
self.nullifier
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use proptest::prelude::*; use proptest::prelude::*;
@ -364,6 +391,7 @@ mod tests {
EphemeralKeyBytes, EphemeralKeyBytes,
}; };
use super::{prf_ock_orchard, CompactAction, OrchardDomain, OrchardNoteEncryption};
use crate::note::note_type::testing::arb_note_type; use crate::note::note_type::testing::arb_note_type;
use crate::note::NoteType; use crate::note::NoteType;
use crate::{ use crate::{
@ -381,10 +409,7 @@ mod tests {
Address, Note, Address, Note,
}; };
use super::{ use super::orchard_parse_note_plaintext_without_memo;
orchard_parse_note_plaintext_without_memo, prf_ock_orchard, CompactAction, OrchardDomain,
OrchardNoteEncryption,
};
proptest! { proptest! {
#[test] #[test]
@ -472,6 +497,7 @@ mod tests {
}; };
let note = Note::from_parts(recipient, value, note_type, rho, rseed); let note = Note::from_parts(recipient, value, note_type, rho, rseed);
assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx);
let action = Action::from_parts( let action = Action::from_parts(

View File

@ -421,6 +421,7 @@ pub mod testing {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::note::note_type::testing::{arb_note_type, native_note_type}; use crate::note::note_type::testing::{arb_note_type, native_note_type};
use crate::note::NoteType; use crate::note::NoteType;
use proptest::prelude::*; use proptest::prelude::*;
@ -499,8 +500,8 @@ mod tests {
), prop::collection::vec(arb_trapdoor(), n_values)) ), prop::collection::vec(arb_trapdoor(), n_values))
), ),
) { ) {
// Test with native and arbitrary note types // Test with native note type (zec)
_bsk_consistent_with_bvk(&native_values, &arb_values, &neg_trapdoors); _bsk_consistent_with_bvk(&native_values, &arb_values, &neg_trapdoors);
} }
} }
} }