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]
## [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
### Changed
- Migrated to `bitvec 1`, `ff 0.12`, `group 0.12`, `incrementalmerkletree 0.3`,

View File

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

View File

@ -161,8 +161,8 @@ of such entity.
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.
16. **Modification of This License.** This License is Copyright © 2007 Zooko
Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate this
16. **Modification of This License.** This License is Copyright © 2021-2022 Electric Coin Company.
Permission is granted to copy, distribute, or communicate this
License without modification. Nothing in this License permits You to modify
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
@ -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
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
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
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
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
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 ff::Field;
use halo2_proofs::circuit::Value;
use nonempty::NonEmpty;
use pasta_curves::pallas;
use rand::{prelude::SliceRandom, CryptoRng, RngCore};
@ -181,6 +182,7 @@ impl ActionInfo {
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha);
let note_type = self.spend.note.note_type();
let note = Note::new(
self.output.recipient,
@ -225,26 +227,25 @@ impl ActionInfo {
},
),
Circuit {
path: Some(self.spend.merkle_path.auth_path()),
pos: Some(self.spend.merkle_path.position()),
g_d_old: Some(sender_address.g_d()),
pk_d_old: Some(*sender_address.pk_d()),
v_old: Some(self.spend.note.value()),
//split: Some(self.spend.split_flag),
rho_old: Some(rho_old),
psi_old: Some(psi_old),
rcm_old: Some(rcm_old),
cm_old: Some(self.spend.note.commitment()),
alpha: Some(alpha),
ak: Some(ak),
nk: Some(*self.spend.fvk.nk()),
rivk: Some(self.spend.fvk.rivk(self.spend.scope)),
g_d_new: Some(note.recipient().g_d()),
pk_d_new: Some(*note.recipient().pk_d()),
v_new: Some(note.value()),
psi_new: Some(note.rseed().psi(&note.rho())),
rcm_new: Some(note.rseed().rcm(&note.rho())),
rcv: Some(self.rcv),
path: Value::known(self.spend.merkle_path.auth_path()),
pos: Value::known(self.spend.merkle_path.position()),
g_d_old: Value::known(sender_address.g_d()),
pk_d_old: Value::known(*sender_address.pk_d()),
v_old: Value::known(self.spend.note.value()),
rho_old: Value::known(rho_old),
psi_old: Value::known(psi_old),
rcm_old: Value::known(rcm_old),
cm_old: Value::known(self.spend.note.commitment()),
alpha: Value::known(alpha),
ak: Value::known(ak),
nk: Value::known(*self.spend.fvk.nk()),
rivk: Value::known(self.spend.fvk.rivk(self.spend.scope)),
g_d_new: Value::known(note.recipient().g_d()),
pk_d_new: Value::known(*note.recipient().pk_d()),
v_new: Value::known(note.value()),
psi_new: Value::known(note.rseed().psi(&note.rho())),
rcm_new: Value::known(note.rseed().rcm(&note.rho())),
rcv: Value::known(self.rcv),
},
)
}
@ -273,8 +274,16 @@ impl Builder {
/// 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
/// the given note.
///
/// [`OrchardDomain`]: crate::note_encryption::OrchardDomain
/// [`MerkleHashOrchard`]: crate::tree::MerkleHashOrchard
pub fn add_spend(
&mut self,
fvk: FullViewingKey,

View File

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

View File

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

View File

@ -21,7 +21,7 @@ use halo2_gadgets::{
};
use halo2_proofs::{
arithmetic::FieldExt,
circuit::{AssignedCell, Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter, Value},
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>(
mut layouter: impl Layouter<F>,
column: Column<Advice>,
value: Option<V>,
value: Value<V>,
) -> Result<AssignedCell<V, F>, plonk::Error>
where
for<'v> Assigned<F>: From<&'v V>,
{
layouter.assign_region(
|| "load private",
|mut region| {
region.assign_advice(
|| "load private",
column,
0,
|| value.ok_or(plonk::Error::Synthesis),
)
},
|mut region| region.assign_advice(|| "load private", column, 0, || value),
)
}

View File

@ -74,12 +74,7 @@ impl AddInstruction<pallas::Base> for AddChip {
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),
)
region.assign_advice(|| "c", self.config.c, 0, || scalar_val)
},
)
}

View File

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

View File

@ -118,6 +118,11 @@ impl OrchardDomain {
rho: *act.nullifier(),
}
}
/// Constructs a domain from a nullifier.
pub fn for_nullifier(nullifier: Nullifier) -> Self {
OrchardDomain { rho: nullifier }
}
}
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)]
mod tests {
use proptest::prelude::*;
@ -364,6 +391,7 @@ mod tests {
EphemeralKeyBytes,
};
use super::{prf_ock_orchard, CompactAction, OrchardDomain, OrchardNoteEncryption};
use crate::note::note_type::testing::arb_note_type;
use crate::note::NoteType;
use crate::{
@ -381,10 +409,7 @@ mod tests {
Address, Note,
};
use super::{
orchard_parse_note_plaintext_without_memo, prf_ock_orchard, CompactAction, OrchardDomain,
OrchardNoteEncryption,
};
use super::orchard_parse_note_plaintext_without_memo;
proptest! {
#[test]
@ -472,6 +497,7 @@ mod tests {
};
let note = Note::from_parts(recipient, value, note_type, rho, rseed);
assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx);
let action = Action::from_parts(

View File

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