mirror of https://github.com/zcash/orchard.git
Merge branch 'zsa1' into zsa-builder
This commit is contained in:
commit
60a17a2179
11
CHANGELOG.md
11
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`,
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
157
src/circuit.rs
157
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<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)
|
||||
|
|
|
@ -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,6 +825,10 @@ mod tests {
|
|||
rivk_gadget,
|
||||
)?;
|
||||
|
||||
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
|
||||
|
@ -829,27 +836,18 @@ mod tests {
|
|||
.short_commit(
|
||||
iter::empty()
|
||||
.chain(
|
||||
self.ak
|
||||
.unwrap()
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(L_ORCHARD_BASE),
|
||||
ak.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
|
||||
)
|
||||
.chain(
|
||||
self.nk
|
||||
.unwrap()
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(L_ORCHARD_BASE),
|
||||
nk.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
|
||||
),
|
||||
&rivk,
|
||||
)
|
||||
.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),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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(
|
||||
self.gd_x
|
||||
.unwrap()
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(L_ORCHARD_BASE),
|
||||
gd_x.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
|
||||
)
|
||||
.chain(Some(lsb(self.gd_y_lsb.unwrap())))
|
||||
.chain(Some(lsb(gd_y_lsb)))
|
||||
.chain(
|
||||
self.pkd_x
|
||||
.unwrap()
|
||||
pkd_x
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(L_ORCHARD_BASE),
|
||||
)
|
||||
.chain(Some(lsb(self.pkd_y_lsb.unwrap())))
|
||||
.chain(Some(lsb(pkd_y_lsb)))
|
||||
.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),
|
||||
rho.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
|
||||
)
|
||||
.chain(
|
||||
self.psi
|
||||
.unwrap()
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(L_ORCHARD_BASE),
|
||||
psi.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE),
|
||||
),
|
||||
&rcm,
|
||||
)
|
||||
.unwrap()
|
||||
.to_affine();
|
||||
NonIdentityPoint::new(
|
||||
ecc_chip,
|
||||
layouter.namespace(|| "witness cm"),
|
||||
Some(point),
|
||||
)?
|
||||
.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),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,7 +500,7 @@ mod tests {
|
|||
), 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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue