mirror of https://github.com/zcash/orchard.git
Merge pull request #362 from zcash/batch-scanner-improvements
Batch scanner improvements
This commit is contained in:
commit
d05b6cee9d
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -11,12 +11,21 @@ and this project adheres to Rust's notion of
|
||||||
### Added
|
### Added
|
||||||
- `orchard::Proof::add_to_batch`
|
- `orchard::Proof::add_to_batch`
|
||||||
- `orchard::address::Address::diversifier`
|
- `orchard::address::Address::diversifier`
|
||||||
- `orchard::keys::Diversifier::from_bytes`
|
- `orchard::keys:`:
|
||||||
|
- `Diversifier::from_bytes`
|
||||||
|
- `PreparedEphemeralPublicKey`
|
||||||
|
- `PreparedIncomingViewingKey`
|
||||||
- `orchard::note`:
|
- `orchard::note`:
|
||||||
- `RandomSeed`
|
- `RandomSeed`
|
||||||
- `Note::{from_parts, rseed}`
|
- `Note::{from_parts, rseed}`
|
||||||
|
- `impl memuse::DynamicUsage for Nullifier`
|
||||||
|
- `orchard::note_encryption`:
|
||||||
|
- `impl memuse::DynamicUsage for OrchardDomain`
|
||||||
- `orchard::builder::SpendInfo::new`
|
- `orchard::builder::SpendInfo::new`
|
||||||
- `orchard::circuit::Circuit::from_action_context`
|
- `orchard::circuit::Circuit::from_action_context`
|
||||||
|
- impls of `Eq` for:
|
||||||
|
- `orchard::zip32::ChildIndex`
|
||||||
|
- `orchard::value::ValueSum`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Migrated to `zcash_note_encryption 0.2`.
|
- Migrated to `zcash_note_encryption 0.2`.
|
||||||
|
|
|
@ -28,12 +28,12 @@ bitvec = "1"
|
||||||
blake2b_simd = "1"
|
blake2b_simd = "1"
|
||||||
ff = "0.12"
|
ff = "0.12"
|
||||||
fpe = "0.5"
|
fpe = "0.5"
|
||||||
group = "0.12"
|
group = { version = "0.12.1", features = ["wnaf-memuse"] }
|
||||||
halo2_gadgets = "0.2"
|
halo2_gadgets = "0.2"
|
||||||
halo2_proofs = "0.2"
|
halo2_proofs = "0.2"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
memuse = { version = "0.2", features = ["nonempty"] }
|
memuse = { version = "0.2.1", features = ["nonempty"] }
|
||||||
pasta_curves = "0.4"
|
pasta_curves = "0.4"
|
||||||
proptest = { version = "1.0.0", optional = true }
|
proptest = { version = "1.0.0", optional = true }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
|
@ -3,7 +3,7 @@ use orchard::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
bundle::Flags,
|
bundle::Flags,
|
||||||
circuit::ProvingKey,
|
circuit::ProvingKey,
|
||||||
keys::{FullViewingKey, Scope, SpendingKey},
|
keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey},
|
||||||
note_encryption::{CompactAction, OrchardDomain},
|
note_encryption::{CompactAction, OrchardDomain},
|
||||||
value::NoteValue,
|
value::NoteValue,
|
||||||
Anchor, Bundle,
|
Anchor, Bundle,
|
||||||
|
@ -21,6 +21,7 @@ fn bench_note_decryption(c: &mut Criterion) {
|
||||||
let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap());
|
let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap());
|
||||||
let valid_ivk = fvk.to_ivk(Scope::External);
|
let valid_ivk = fvk.to_ivk(Scope::External);
|
||||||
let recipient = valid_ivk.address_at(0u32);
|
let recipient = valid_ivk.address_at(0u32);
|
||||||
|
let valid_ivk = PreparedIncomingViewingKey::new(&valid_ivk);
|
||||||
|
|
||||||
// Compact actions don't have the full AEAD ciphertext, so ZIP 307 trial-decryption
|
// Compact actions don't have the full AEAD ciphertext, so ZIP 307 trial-decryption
|
||||||
// relies on an invalid ivk resulting in random noise for which the note commitment
|
// relies on an invalid ivk resulting in random noise for which the note commitment
|
||||||
|
@ -31,15 +32,15 @@ fn bench_note_decryption(c: &mut Criterion) {
|
||||||
//
|
//
|
||||||
// Our fixed (action, invalid ivk) tuple will always fall into a specific rejection
|
// Our fixed (action, invalid ivk) tuple will always fall into a specific rejection
|
||||||
// case. In order to reflect the real behaviour in the benchmarks, we trial-decrypt
|
// case. In order to reflect the real behaviour in the benchmarks, we trial-decrypt
|
||||||
// with 1000 invalid ivks (each of which will result in a different uniformly-random
|
// with 10240 invalid ivks (each of which will result in a different uniformly-random
|
||||||
// plaintext); this is equivalent to trial-decrypting 1000 different actions with the
|
// plaintext); this is equivalent to trial-decrypting 10240 different actions with the
|
||||||
// same ivk, but is faster to set up.
|
// same ivk, but is faster to set up.
|
||||||
let invalid_ivks: Vec<_> = (0u32..1000)
|
let invalid_ivks: Vec<_> = (0u32..10240)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let mut sk = [0; 32];
|
let mut sk = [0; 32];
|
||||||
sk[..4].copy_from_slice(&i.to_le_bytes());
|
sk[..4].copy_from_slice(&i.to_le_bytes());
|
||||||
let fvk = FullViewingKey::from(&SpendingKey::from_bytes(sk).unwrap());
|
let fvk = FullViewingKey::from(&SpendingKey::from_bytes(sk).unwrap());
|
||||||
fvk.to_ivk(Scope::External)
|
PreparedIncomingViewingKey::new(&fvk.to_ivk(Scope::External))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
address::Address,
|
address::Address,
|
||||||
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
|
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
|
||||||
circuit::{Instance, Proof, VerifyingKey},
|
circuit::{Instance, Proof, VerifyingKey},
|
||||||
keys::{IncomingViewingKey, OutgoingViewingKey},
|
keys::{IncomingViewingKey, OutgoingViewingKey, PreparedIncomingViewingKey},
|
||||||
note::Note,
|
note::Note,
|
||||||
note_encryption::OrchardDomain,
|
note_encryption::OrchardDomain,
|
||||||
primitives::redpallas::{self, Binding, SpendAuth},
|
primitives::redpallas::{self, Binding, SpendAuth},
|
||||||
|
@ -284,14 +284,18 @@ impl<T: Authorization, V> Bundle<T, V> {
|
||||||
&self,
|
&self,
|
||||||
keys: &[IncomingViewingKey],
|
keys: &[IncomingViewingKey],
|
||||||
) -> Vec<(usize, IncomingViewingKey, Note, Address, [u8; 512])> {
|
) -> Vec<(usize, IncomingViewingKey, Note, Address, [u8; 512])> {
|
||||||
|
let prepared_keys: Vec<_> = keys
|
||||||
|
.iter()
|
||||||
|
.map(|ivk| (ivk, PreparedIncomingViewingKey::new(ivk)))
|
||||||
|
.collect();
|
||||||
self.actions
|
self.actions
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(idx, action)| {
|
.filter_map(|(idx, action)| {
|
||||||
let domain = OrchardDomain::for_action(action);
|
let domain = OrchardDomain::for_action(action);
|
||||||
keys.iter().find_map(move |ivk| {
|
prepared_keys.iter().find_map(|(ivk, prepared_ivk)| {
|
||||||
try_note_decryption(&domain, ivk, action)
|
try_note_decryption(&domain, prepared_ivk, action)
|
||||||
.map(|(n, a, m)| (idx, ivk.clone(), n, a, m))
|
.map(|(n, a, m)| (idx, (*ivk).clone(), n, a, m))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -305,9 +309,10 @@ impl<T: Authorization, V> Bundle<T, V> {
|
||||||
action_idx: usize,
|
action_idx: usize,
|
||||||
key: &IncomingViewingKey,
|
key: &IncomingViewingKey,
|
||||||
) -> Option<(Note, Address, [u8; 512])> {
|
) -> Option<(Note, Address, [u8; 512])> {
|
||||||
|
let prepared_ivk = PreparedIncomingViewingKey::new(key);
|
||||||
self.actions.get(action_idx).and_then(move |action| {
|
self.actions.get(action_idx).and_then(move |action| {
|
||||||
let domain = OrchardDomain::for_action(action);
|
let domain = OrchardDomain::for_action(action);
|
||||||
try_note_decryption(&domain, key, action)
|
try_note_decryption(&domain, &prepared_ivk, action)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
src/keys.rs
58
src/keys.rs
|
@ -20,8 +20,9 @@ use crate::{
|
||||||
address::Address,
|
address::Address,
|
||||||
primitives::redpallas::{self, SpendAuth},
|
primitives::redpallas::{self, SpendAuth},
|
||||||
spec::{
|
spec::{
|
||||||
commit_ivk, diversify_hash, extract_p, ka_orchard, prf_nf, to_base, to_scalar,
|
commit_ivk, diversify_hash, extract_p, ka_orchard, ka_orchard_prepared, prf_nf, to_base,
|
||||||
NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar, PrfExpand,
|
to_scalar, NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar,
|
||||||
|
PreparedNonIdentityBase, PreparedNonZeroScalar, PrfExpand,
|
||||||
},
|
},
|
||||||
zip32::{self, ChildIndex, ExtendedSpendingKey},
|
zip32::{self, ChildIndex, ExtendedSpendingKey},
|
||||||
};
|
};
|
||||||
|
@ -627,7 +628,8 @@ impl KeyAgreementPrivateKey {
|
||||||
|
|
||||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||||
fn address(&self, d: Diversifier) -> Address {
|
fn address(&self, d: Diversifier) -> Address {
|
||||||
let pk_d = DiversifiedTransmissionKey::derive_inner(self, &d);
|
let prepared_ivk = PreparedIncomingViewingKey::new_inner(self);
|
||||||
|
let pk_d = DiversifiedTransmissionKey::derive(&prepared_ivk, &d);
|
||||||
Address::from_parts(d, pk_d)
|
Address::from_parts(d, pk_d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -704,6 +706,32 @@ impl IncomingViewingKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Orchard incoming viewing key that has been precomputed for trial decryption.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PreparedIncomingViewingKey(PreparedNonZeroScalar);
|
||||||
|
|
||||||
|
impl memuse::DynamicUsage for PreparedIncomingViewingKey {
|
||||||
|
fn dynamic_usage(&self) -> usize {
|
||||||
|
self.0.dynamic_usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
||||||
|
self.0.dynamic_usage_bounds()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreparedIncomingViewingKey {
|
||||||
|
/// Performs the necessary precomputations to use an `IncomingViewingKey` for note
|
||||||
|
/// decryption.
|
||||||
|
pub fn new(ivk: &IncomingViewingKey) -> Self {
|
||||||
|
Self::new_inner(&ivk.ivk)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(ivk: &KeyAgreementPrivateKey) -> Self {
|
||||||
|
Self(PreparedNonZeroScalar::new(&ivk.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A key that provides the capability to recover outgoing transaction information from
|
/// A key that provides the capability to recover outgoing transaction information from
|
||||||
/// the block chain.
|
/// the block chain.
|
||||||
///
|
///
|
||||||
|
@ -753,13 +781,9 @@ impl DiversifiedTransmissionKey {
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
pub(crate) fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
|
pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self {
|
||||||
Self::derive_inner(&ivk.ivk, d)
|
let g_d = PreparedNonIdentityBase::new(diversify_hash(d.as_array()));
|
||||||
}
|
DiversifiedTransmissionKey(ka_orchard_prepared(&ivk.0, &g_d))
|
||||||
|
|
||||||
fn derive_inner(ivk: &KeyAgreementPrivateKey, d: &Diversifier) -> Self {
|
|
||||||
let g_d = diversify_hash(d.as_array());
|
|
||||||
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// $abst_P(bytes)$
|
/// $abst_P(bytes)$
|
||||||
|
@ -841,6 +865,20 @@ impl EphemeralPublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Orchard ephemeral public key that has been precomputed for trial decryption.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PreparedEphemeralPublicKey(PreparedNonIdentityBase);
|
||||||
|
|
||||||
|
impl PreparedEphemeralPublicKey {
|
||||||
|
pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
|
||||||
|
PreparedEphemeralPublicKey(PreparedNonIdentityBase::new(epk.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
|
||||||
|
SharedSecret(ka_orchard_prepared(&ivk.0, &self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{SharedSecret} := \mathbb{P}^{\ast}$
|
/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{SharedSecret} := \mathbb{P}^{\ast}$
|
||||||
///
|
///
|
||||||
/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use group::{ff::PrimeField, Group};
|
use group::{ff::PrimeField, Group};
|
||||||
use halo2_proofs::arithmetic::CurveExt;
|
use halo2_proofs::arithmetic::CurveExt;
|
||||||
|
use memuse::DynamicUsage;
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use subtle::CtOption;
|
use subtle::CtOption;
|
||||||
|
@ -14,6 +15,9 @@ use crate::{
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Nullifier(pub(crate) pallas::Base);
|
pub struct Nullifier(pub(crate) pallas::Base);
|
||||||
|
|
||||||
|
// We know that `pallas::Base` doesn't allocate internally.
|
||||||
|
memuse::impl_no_dynamic_usage!(Nullifier);
|
||||||
|
|
||||||
impl Nullifier {
|
impl Nullifier {
|
||||||
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
|
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
|
||||||
///
|
///
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
action::Action,
|
action::Action,
|
||||||
keys::{
|
keys::{
|
||||||
DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey,
|
DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey,
|
||||||
IncomingViewingKey, OutgoingViewingKey, SharedSecret,
|
OutgoingViewingKey, PreparedEphemeralPublicKey, PreparedIncomingViewingKey, SharedSecret,
|
||||||
},
|
},
|
||||||
note::{ExtractedNoteCommitment, Nullifier, RandomSeed},
|
note::{ExtractedNoteCommitment, Nullifier, RandomSeed},
|
||||||
spec::diversify_hash,
|
spec::diversify_hash,
|
||||||
|
@ -85,6 +85,16 @@ pub struct OrchardDomain {
|
||||||
rho: Nullifier,
|
rho: Nullifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl memuse::DynamicUsage for OrchardDomain {
|
||||||
|
fn dynamic_usage(&self) -> usize {
|
||||||
|
self.rho.dynamic_usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
||||||
|
self.rho.dynamic_usage_bounds()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OrchardDomain {
|
impl OrchardDomain {
|
||||||
/// Constructs a domain that can be used to trial-decrypt this action's output note.
|
/// Constructs a domain that can be used to trial-decrypt this action's output note.
|
||||||
pub fn for_action<T>(act: &Action<T>) -> Self {
|
pub fn for_action<T>(act: &Action<T>) -> Self {
|
||||||
|
@ -102,13 +112,13 @@ impl OrchardDomain {
|
||||||
impl Domain for OrchardDomain {
|
impl Domain for OrchardDomain {
|
||||||
type EphemeralSecretKey = EphemeralSecretKey;
|
type EphemeralSecretKey = EphemeralSecretKey;
|
||||||
type EphemeralPublicKey = EphemeralPublicKey;
|
type EphemeralPublicKey = EphemeralPublicKey;
|
||||||
type PreparedEphemeralPublicKey = EphemeralPublicKey;
|
type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey;
|
||||||
type SharedSecret = SharedSecret;
|
type SharedSecret = SharedSecret;
|
||||||
type SymmetricKey = Hash;
|
type SymmetricKey = Hash;
|
||||||
type Note = Note;
|
type Note = Note;
|
||||||
type Recipient = Address;
|
type Recipient = Address;
|
||||||
type DiversifiedTransmissionKey = DiversifiedTransmissionKey;
|
type DiversifiedTransmissionKey = DiversifiedTransmissionKey;
|
||||||
type IncomingViewingKey = IncomingViewingKey;
|
type IncomingViewingKey = PreparedIncomingViewingKey;
|
||||||
type OutgoingViewingKey = OutgoingViewingKey;
|
type OutgoingViewingKey = OutgoingViewingKey;
|
||||||
type ValueCommitment = ValueCommitment;
|
type ValueCommitment = ValueCommitment;
|
||||||
type ExtractedCommitment = ExtractedNoteCommitment;
|
type ExtractedCommitment = ExtractedNoteCommitment;
|
||||||
|
@ -124,7 +134,7 @@ impl Domain for OrchardDomain {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey {
|
fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey {
|
||||||
epk
|
PreparedEphemeralPublicKey::new(epk)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ka_derive_public(
|
fn ka_derive_public(
|
||||||
|
@ -352,7 +362,7 @@ mod tests {
|
||||||
action::Action,
|
action::Action,
|
||||||
keys::{
|
keys::{
|
||||||
DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey,
|
DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey,
|
||||||
OutgoingViewingKey,
|
OutgoingViewingKey, PreparedIncomingViewingKey,
|
||||||
},
|
},
|
||||||
note::{ExtractedNoteCommitment, Nullifier, RandomSeed, TransmittedNoteCiphertext},
|
note::{ExtractedNoteCommitment, Nullifier, RandomSeed, TransmittedNoteCiphertext},
|
||||||
primitives::redpallas,
|
primitives::redpallas,
|
||||||
|
@ -370,7 +380,9 @@ mod tests {
|
||||||
//
|
//
|
||||||
|
|
||||||
// Recipient key material
|
// Recipient key material
|
||||||
let ivk = IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap();
|
let ivk = PreparedIncomingViewingKey::new(
|
||||||
|
&IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap(),
|
||||||
|
);
|
||||||
let ovk = OutgoingViewingKey::from(tv.ovk);
|
let ovk = OutgoingViewingKey::from(tv.ovk);
|
||||||
let d = Diversifier::from_bytes(tv.default_d);
|
let d = Diversifier::from_bytes(tv.default_d);
|
||||||
let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap();
|
let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap();
|
||||||
|
|
50
src/spec.rs
50
src/spec.rs
|
@ -4,10 +4,10 @@ use core::iter;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldBits};
|
use ff::{Field, PrimeField, PrimeFieldBits};
|
||||||
use group::GroupEncoding;
|
use group::{Curve, Group, GroupEncoding, WnafBase, WnafScalar};
|
||||||
use group::{Curve, Group};
|
|
||||||
use halo2_gadgets::{poseidon::primitives as poseidon, sinsemilla::primitives as sinsemilla};
|
use halo2_gadgets::{poseidon::primitives as poseidon, sinsemilla::primitives as sinsemilla};
|
||||||
use halo2_proofs::arithmetic::{CurveAffine, CurveExt, FieldExt};
|
use halo2_proofs::arithmetic::{CurveAffine, CurveExt, FieldExt};
|
||||||
|
use memuse::DynamicUsage;
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
use subtle::{ConditionallySelectable, CtOption};
|
use subtle::{ConditionallySelectable, CtOption};
|
||||||
|
|
||||||
|
@ -140,6 +140,36 @@ impl Deref for NonZeroPallasScalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PREPARED_WINDOW_SIZE: usize = 4;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct PreparedNonIdentityBase(WnafBase<pallas::Point, PREPARED_WINDOW_SIZE>);
|
||||||
|
|
||||||
|
impl PreparedNonIdentityBase {
|
||||||
|
pub(crate) fn new(base: NonIdentityPallasPoint) -> Self {
|
||||||
|
PreparedNonIdentityBase(WnafBase::new(base.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct PreparedNonZeroScalar(WnafScalar<pallas::Scalar, PREPARED_WINDOW_SIZE>);
|
||||||
|
|
||||||
|
impl DynamicUsage for PreparedNonZeroScalar {
|
||||||
|
fn dynamic_usage(&self) -> usize {
|
||||||
|
self.0.dynamic_usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
||||||
|
self.0.dynamic_usage_bounds()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreparedNonZeroScalar {
|
||||||
|
pub(crate) fn new(scalar: &NonZeroPallasScalar) -> Self {
|
||||||
|
PreparedNonZeroScalar(WnafScalar::new(scalar))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// $\mathsf{ToBase}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod q_P)$
|
/// $\mathsf{ToBase}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod q_P)$
|
||||||
///
|
///
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
|
@ -213,8 +243,20 @@ pub(crate) fn ka_orchard(
|
||||||
sk: &NonZeroPallasScalar,
|
sk: &NonZeroPallasScalar,
|
||||||
b: &NonIdentityPallasPoint,
|
b: &NonIdentityPallasPoint,
|
||||||
) -> NonIdentityPallasPoint {
|
) -> NonIdentityPallasPoint {
|
||||||
let mut wnaf = group::Wnaf::new();
|
ka_orchard_prepared(
|
||||||
NonIdentityPallasPoint(wnaf.scalar(sk.deref()).base(*b.deref()))
|
&PreparedNonZeroScalar::new(sk),
|
||||||
|
&PreparedNonIdentityBase::new(*b),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
||||||
|
///
|
||||||
|
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
||||||
|
pub(crate) fn ka_orchard_prepared(
|
||||||
|
sk: &PreparedNonZeroScalar,
|
||||||
|
b: &PreparedNonIdentityBase,
|
||||||
|
) -> NonIdentityPallasPoint {
|
||||||
|
NonIdentityPallasPoint(&b.0 * &sk.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coordinate extractor for Pallas.
|
/// Coordinate extractor for Pallas.
|
||||||
|
|
|
@ -144,7 +144,7 @@ pub(crate) enum Sign {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sum of Orchard note values.
|
/// A sum of Orchard note values.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct ValueSum(i128);
|
pub struct ValueSum(i128);
|
||||||
|
|
||||||
impl ValueSum {
|
impl ValueSum {
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl FvkTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A hardened child index for a derived key.
|
/// A hardened child index for a derived key.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct ChildIndex(u32);
|
pub struct ChildIndex(u32);
|
||||||
|
|
||||||
impl TryFrom<u32> for ChildIndex {
|
impl TryFrom<u32> for ChildIndex {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use orchard::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
bundle::{Authorized, Flags},
|
bundle::{Authorized, Flags},
|
||||||
circuit::{ProvingKey, VerifyingKey},
|
circuit::{ProvingKey, VerifyingKey},
|
||||||
keys::{FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey},
|
keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey},
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
note_encryption::OrchardDomain,
|
note_encryption::OrchardDomain,
|
||||||
tree::{MerkleHashOrchard, MerklePath},
|
tree::{MerkleHashOrchard, MerklePath},
|
||||||
|
@ -57,7 +57,7 @@ fn bundle_chain() {
|
||||||
|
|
||||||
// Create a shielded bundle spending the previous output.
|
// Create a shielded bundle spending the previous output.
|
||||||
let shielded_bundle: Bundle<_, i64> = {
|
let shielded_bundle: Bundle<_, i64> = {
|
||||||
let ivk = fvk.to_ivk(Scope::External);
|
let ivk = PreparedIncomingViewingKey::new(&fvk.to_ivk(Scope::External));
|
||||||
let (note, _, _) = shielding_bundle
|
let (note, _, _) = shielding_bundle
|
||||||
.actions()
|
.actions()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Reference in New Issue