diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f3e8d5..5077477b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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`, diff --git a/Cargo.toml b/Cargo.toml index b50a0299..692cbd58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orchard" -version = "0.1.0" +version = "0.2.0" authors = [ "Sean Bowe ", "Jack Grigg ", @@ -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 diff --git a/LICENSE-BOSL b/LICENSE-BOSL index d73baa19..7abc6ba2 100644 --- a/LICENSE-BOSL +++ b/LICENSE-BOSL @@ -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 " or with a notice of your own that is not confusingly similar to the notice in this -License. \ No newline at end of file +License. diff --git a/book/src/design/nullifiers.md b/book/src/design/nullifiers.md index f84a5145..f9a11bad 100644 --- a/book/src/design/nullifiers.md +++ b/book/src/design/nullifiers.md @@ -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 diff --git a/src/builder.rs b/src/builder.rs index 9b028882..c3c1269b 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -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(¬e.rho())), - rcm_new: Some(note.rseed().rcm(¬e.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(¬e.rho())), + rcm_new: Value::known(note.rseed().rcm(¬e.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, diff --git a/src/bundle.rs b/src/bundle.rs index e0c928f0..01b61d79 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -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; diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs new file mode 100644 index 00000000..c60d0cd5 --- /dev/null +++ b/src/bundle/batch.rs @@ -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, +} + +/// Batch validation context for Orchard. +/// +/// This batch-validates proofs and RedPallas signatures. +#[derive(Debug, Default)] +pub struct BatchValidator { + proofs: plonk::BatchVerifier, + signatures: Vec, +} + +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>( + &mut self, + bundle: &Bundle, + 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(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 + } + } + } +} diff --git a/src/circuit.rs b/src/circuit.rs index e8e6efe9..346ae9b3 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -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, - pub(crate) g_d_old: Option, - pub(crate) pk_d_old: Option, - pub(crate) v_old: Option, - // pub(crate) split: Option, - pub(crate) rho_old: Option, - pub(crate) psi_old: Option, - pub(crate) rcm_old: Option, - pub(crate) cm_old: Option, - pub(crate) alpha: Option, - pub(crate) ak: Option, - pub(crate) nk: Option, - pub(crate) rivk: Option, - pub(crate) g_d_new: Option, - pub(crate) pk_d_new: Option, - pub(crate) v_new: Option, - pub(crate) psi_new: Option, - pub(crate) rcm_new: Option, - pub(crate) rcv: Option, + pub(crate) path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, + pub(crate) pos: Value, + pub(crate) g_d_old: Value, + pub(crate) pk_d_old: Value, + pub(crate) v_old: Value, + pub(crate) rho_old: Value, + pub(crate) psi_old: Value, + pub(crate) rcm_old: Value, + pub(crate) cm_old: Value, + pub(crate) alpha: Value, + pub(crate) ak: Value, + pub(crate) nk: Value, + pub(crate) rivk: Value, + pub(crate) g_d_new: Value, + pub(crate) pk_d_new: Value, + pub(crate) v_new: Value, + pub(crate) psi_new: Value, + pub(crate) rcm_new: Value, + pub(crate) rcv: Value, } impl plonk::Circuit for Circuit { @@ -359,7 +358,7 @@ impl plonk::Circuit for Circuit { )?; // Witness ak_P. - let ak_P: Option = self.ak.as_ref().map(|ak| ak.into()); + let ak_P: Value = 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 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 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 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 for Circuit { /// The verifying key for the Orchard Action circuit. #[derive(Debug)] pub struct VerifyingKey { - params: halo2_proofs::poly::commitment::Params, - vk: plonk::VerifyingKey, + pub(crate) params: halo2_proofs::poly::commitment::Params, + pub(crate) vk: plonk::VerifyingKey, } 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, + instances: Vec, + ) { + 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) -> 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) diff --git a/src/circuit/commit_ivk.rs b/src/circuit/commit_ivk.rs index 1460c9d8..8001a0ac 100644 --- a/src/circuit/commit_ivk.rs +++ b/src/circuit/commit_ivk.rs @@ -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, nk: AssignedCell, b_0: RangeConstrained>, - b_1: RangeConstrained>, + b_1: RangeConstrained>, b_2: RangeConstrained>, d_0: RangeConstrained>, - d_1: RangeConstrained>, + d_1: RangeConstrained>, z13_a: AssignedCell, a_prime: AssignedCell, z13_a_prime: AssignedCell, @@ -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, - nk: Option, + ak: Value, + nk: Value, } impl UtilitiesInstructions 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), }, ]; diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 7fa52ecd..21d24333 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -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: Chip { pub(in crate::circuit) fn assign_free_advice( mut layouter: impl Layouter, column: Column, - value: Option, + value: Value, ) -> Result, plonk::Error> where for<'v> Assigned: 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), ) } diff --git a/src/circuit/gadget/add_chip.rs b/src/circuit/gadget/add_chip.rs index bf013528..0973a3f3 100644 --- a/src/circuit/gadget/add_chip.rs +++ b/src/circuit/gadget/add_chip.rs @@ -74,12 +74,7 @@ impl AddInstruction 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) }, ) } diff --git a/src/circuit/note_commit.rs b/src/circuit/note_commit.rs index b7163285..1f4a3cec 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/note_commit.rs @@ -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>, - RangeConstrained>, - RangeConstrained>, + RangeConstrained>, + RangeConstrained>, RangeConstrained>, ), Error, @@ -170,7 +170,7 @@ impl DecomposeB { layouter: &mut impl Layouter, b: NoteCommitPiece, b_0: RangeConstrained>, - b_1: RangeConstrained>, + b_1: RangeConstrained>, b_2: RangeConstrained>, b_3: RangeConstrained>, ) -> Result, 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>, - RangeConstrained>, + RangeConstrained>, + RangeConstrained>, RangeConstrained>, ), Error, @@ -313,7 +308,7 @@ impl DecomposeD { &self, layouter: &mut impl Layouter, d: NoteCommitPiece, - d_0: RangeConstrained>, + d_0: RangeConstrained>, d_1: RangeConstrained>, d_2: RangeConstrained>, z1_d: AssignedCell, @@ -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>, + RangeConstrained>, RangeConstrained>, ), Error, @@ -561,7 +551,7 @@ impl DecomposeG { &self, layouter: &mut impl Layouter, g: NoteCommitPiece, - g_0: RangeConstrained>, + g_0: RangeConstrained>, g_1: RangeConstrained>, z1_g: AssignedCell, ) -> Result, 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>, - RangeConstrained>, + RangeConstrained>, ), 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, h: NoteCommitPiece, h_0: RangeConstrained>, - h_1: RangeConstrained>, + h_1: RangeConstrained>, ) -> Result, 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, y: AssignedCell, - lsb: RangeConstrained>, + lsb: RangeConstrained>, k_0: RangeConstrained>, k_2: RangeConstrained>, - k_3: RangeConstrained>, + k_3: RangeConstrained>, j: AssignedCell, z1_j: AssignedCell, z13_j: AssignedCell, @@ -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, y: AssignedCell, - lsb: RangeConstrained>, + lsb: RangeConstrained>, ) -> Result>, 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, - gd_y_lsb: Option, - pkd_x: Option, - pkd_y_lsb: Option, - rho: Option, - psi: Option, + gd_x: Value, + gd_y_lsb: Value, + pkd_x: Value, + pkd_y_lsb: Value, + rho: Value, + psi: Value, } impl Circuit 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), }, ]; diff --git a/src/note_encryption.rs b/src/note_encryption.rs index 9ee57bec..87dba4f8 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -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 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( diff --git a/src/value.rs b/src/value.rs index bfbc7868..a3a2e7a8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -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); } } }