mirror of https://github.com/zcash/orchard.git
Merge pull request #80 from zcash/spec-updates
Update implementation to match protocol spec version 2021.2.0
This commit is contained in:
commit
97710e04d6
|
@ -23,6 +23,15 @@ $$\mathsf{cm} = \mathit{Commit}^{\mathsf{cm}}_{\mathsf{rcm}}(\text{rest of note}
|
|||
This is the same split (and rationale) as in Sapling, but using the more PLONK-efficient
|
||||
Sinsemilla instead of Bowe--Hopwood Pedersen hashes.
|
||||
|
||||
Note that we also deviate from Sapling by using $\mathit{ShortCommit}$ to deriving $\mathsf{ivk}$
|
||||
instead of a full PRF. This removes an unnecessary (large) PRF primitive from the circuit,
|
||||
at the cost of requiring $\mathsf{rivk}$ to be part of the full viewing key.
|
||||
Note that for $\mathsf{ivk}$, we also deviate from Sapling in two ways:
|
||||
|
||||
- We use $\mathit{ShortCommit}$ to derive $\mathsf{ivk}$ instead of a full PRF. This removes an
|
||||
unnecessary (large) PRF primitive from the circuit, at the cost of requiring $\mathsf{rivk}$ to be
|
||||
part of the full viewing key.
|
||||
- We define $\mathsf{ivk}$ as an integer in $[1, q_P)$; that is, we exclude $\mathsf{ivk} = 0$. For
|
||||
Sapling, we relied on BLAKE2s to make $\mathsf{ivk} = 0$ infeasible to produce, but it was still
|
||||
technically possible. For Orchard, we get this by construction:
|
||||
- $0$ is not a valid x-coordinate for any Pallas point.
|
||||
- $\mathsf{SinsemillaShortCommit}$ internally maps points to field elements by replacing the identity (which
|
||||
has no affine coordinates) with $0$. But $\mathsf{SinsemillaCommit}$ is defined using incomplete addition, and
|
||||
thus will never produce the identity.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use pasta_curves::pallas;
|
||||
|
||||
use crate::{
|
||||
keys::{DiversifiedTransmissionKey, Diversifier},
|
||||
spec::diversify_hash,
|
||||
spec::{diversify_hash, NonIdentityPallasPoint},
|
||||
};
|
||||
|
||||
/// A shielded payment address.
|
||||
|
@ -15,7 +13,7 @@ use crate::{
|
|||
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
||||
/// let address = FullViewingKey::from(&sk).default_address();
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Address {
|
||||
d: Diversifier,
|
||||
pk_d: DiversifiedTransmissionKey,
|
||||
|
@ -30,7 +28,7 @@ impl Address {
|
|||
Address { d, pk_d }
|
||||
}
|
||||
|
||||
pub(crate) fn g_d(&self) -> pallas::Point {
|
||||
pub(crate) fn g_d(&self) -> NonIdentityPallasPoint {
|
||||
diversify_hash(self.d.as_array())
|
||||
}
|
||||
|
||||
|
|
93
src/keys.rs
93
src/keys.rs
|
@ -16,7 +16,7 @@ use crate::{
|
|||
primitives::redpallas::{self, SpendAuth},
|
||||
spec::{
|
||||
commit_ivk, diversify_hash, extract_p, ka_orchard, prf_expand, prf_expand_vec, prf_nf,
|
||||
to_base, to_scalar,
|
||||
to_base, to_scalar, NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -57,7 +57,7 @@ impl SpendingKey {
|
|||
// needed. Also, `from` would panic on ask = 0.
|
||||
let ask = SpendAuthorizingKey::derive_inner(&sk);
|
||||
// If ivk = ⊥, discard this key.
|
||||
let ivk = IncomingViewingKey::derive_inner(&(&sk).into());
|
||||
let ivk = KeyAgreementPrivateKey::derive_inner(&(&sk).into());
|
||||
CtOption::new(sk, !(ask.ct_is_zero() | ivk.is_none()))
|
||||
}
|
||||
}
|
||||
|
@ -218,17 +218,18 @@ impl FullViewingKey {
|
|||
|
||||
/// Returns the default payment address for this key.
|
||||
pub fn default_address(&self) -> Address {
|
||||
self.address(DiversifierKey::from(self).default_diversifier())
|
||||
IncomingViewingKey::from(self).default_address()
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key at the given index.
|
||||
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
|
||||
self.address(DiversifierKey::from(self).get(j))
|
||||
IncomingViewingKey::from(self).address_at(j)
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||
pub fn address(&self, d: Diversifier) -> Address {
|
||||
IncomingViewingKey::from(self).address(d)
|
||||
// Shortcut: we don't need to derive DiversifierKey.
|
||||
KeyAgreementPrivateKey::from(self).address(d)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,7 +288,7 @@ impl DiversifierKey {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Diversifier([u8; 11]);
|
||||
|
||||
impl Diversifier {
|
||||
|
@ -297,6 +298,49 @@ impl Diversifier {
|
|||
}
|
||||
}
|
||||
|
||||
/// The private key $\mathsf{ivk}$ used in $KA^{Orchard}$, for decrypting incoming notes.
|
||||
///
|
||||
/// In Sapling this is what was encoded as an incoming viewing key. For Orchard, we store
|
||||
/// both this and [`DiversifierKey`] inside [`IncomingViewingKey`] for usability (to
|
||||
/// enable deriving the default address for an incoming viewing key), while this separate
|
||||
/// type represents $\mathsf{ivk}$.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
///
|
||||
/// # Implementation notes
|
||||
///
|
||||
/// We store $\mathsf{ivk}$ in memory as a scalar instead of a base, so that we aren't
|
||||
/// incurring an expensive serialize-and-parse step every time we use it (e.g. for trial
|
||||
/// decryption of notes). When we actually want to serialize ivk, we're guaranteed to get
|
||||
/// a valid base field element encoding, because we always construct ivk from an integer
|
||||
/// in the correct range.
|
||||
#[derive(Debug)]
|
||||
struct KeyAgreementPrivateKey(NonZeroPallasScalar);
|
||||
|
||||
impl From<&FullViewingKey> for KeyAgreementPrivateKey {
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
// KeyAgreementPrivateKey cannot be constructed such that this unwrap would fail.
|
||||
let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
|
||||
KeyAgreementPrivateKey(ivk.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyAgreementPrivateKey {
|
||||
/// Derives ivk from fvk. Internal use only, does not enforce all constraints.
|
||||
fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
|
||||
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
|
||||
commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||
fn address(&self, d: Diversifier) -> Address {
|
||||
let pk_d = DiversifiedTransmissionKey::derive(self, &d);
|
||||
Address::from_parts(d, pk_d)
|
||||
}
|
||||
}
|
||||
|
||||
/// A key that provides the capability to detect and decrypt incoming notes from the block
|
||||
/// chain, without being able to spend the notes or detect when they are spent.
|
||||
///
|
||||
|
@ -306,31 +350,38 @@ impl Diversifier {
|
|||
/// This key is not suitable for use on its own in a wallet, as it cannot maintain
|
||||
/// accurate balance. You should use a [`FullViewingKey`] instead.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
/// Defined in [Zcash Protocol Spec § 5.6.4.3: Orchard Raw Incoming Viewing Keys][orchardinviewingkeyencoding].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
/// [orchardinviewingkeyencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardinviewingkeyencoding
|
||||
#[derive(Debug)]
|
||||
pub struct IncomingViewingKey(pallas::Scalar);
|
||||
pub struct IncomingViewingKey {
|
||||
dk: DiversifierKey,
|
||||
ivk: KeyAgreementPrivateKey,
|
||||
}
|
||||
|
||||
impl From<&FullViewingKey> for IncomingViewingKey {
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
let ivk = IncomingViewingKey::derive_inner(fvk);
|
||||
// IncomingViewingKey cannot be constructed such that this unwrap would fail.
|
||||
IncomingViewingKey(ivk.unwrap())
|
||||
IncomingViewingKey {
|
||||
dk: fvk.into(),
|
||||
ivk: fvk.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IncomingViewingKey {
|
||||
/// Derives ask from sk. Internal use only, does not enforce all constraints.
|
||||
fn derive_inner(fvk: &FullViewingKey) -> CtOption<pallas::Scalar> {
|
||||
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
|
||||
commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
|
||||
/// Returns the default payment address for this key.
|
||||
pub fn default_address(&self) -> Address {
|
||||
self.address(self.dk.default_diversifier())
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key at the given index.
|
||||
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
|
||||
self.address(self.dk.get(j))
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||
pub fn address(&self, d: Diversifier) -> Address {
|
||||
let pk_d = DiversifiedTransmissionKey::derive(self, &d);
|
||||
Address::from_parts(d, pk_d)
|
||||
self.ivk.address(d)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,14 +408,14 @@ impl From<&FullViewingKey> for OutgoingViewingKey {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
|
||||
|
||||
impl DiversifiedTransmissionKey {
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
|
||||
fn derive(ivk: &KeyAgreementPrivateKey, d: &Diversifier) -> Self {
|
||||
let g_d = diversify_hash(&d.as_array());
|
||||
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
|
||||
}
|
||||
|
|
65
src/note.rs
65
src/note.rs
|
@ -2,10 +2,11 @@
|
|||
use group::GroupEncoding;
|
||||
use pasta_curves::pallas;
|
||||
use rand::RngCore;
|
||||
use subtle::CtOption;
|
||||
|
||||
use crate::{
|
||||
keys::{FullViewingKey, SpendingKey},
|
||||
spec::{prf_expand, to_base, to_scalar},
|
||||
spec::{prf_expand_vec, to_base, to_scalar},
|
||||
value::NoteValue,
|
||||
Address,
|
||||
};
|
||||
|
@ -30,15 +31,25 @@ impl RandomSeed {
|
|||
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||
///
|
||||
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||
fn psi(&self) -> pallas::Base {
|
||||
to_base(prf_expand(&self.0, &[0x09]))
|
||||
fn psi(&self, rho: &Nullifier) -> pallas::Base {
|
||||
to_base(prf_expand_vec(&self.0, &[&[0x09], &rho.to_bytes()[..]]))
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||
///
|
||||
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||
fn esk(&self) -> pallas::Scalar {
|
||||
to_scalar(prf_expand(&self.0, &[0x04]))
|
||||
fn esk(&self, rho: &Nullifier) -> pallas::Scalar {
|
||||
to_scalar(prf_expand_vec(&self.0, &[&[0x04], &rho.to_bytes()[..]]))
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||
///
|
||||
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||
fn rcm(&self, rho: &Nullifier) -> commitment::NoteCommitTrapdoor {
|
||||
commitment::NoteCommitTrapdoor(to_scalar(prf_expand_vec(
|
||||
&self.0,
|
||||
&[&[0x05], &rho.to_bytes()[..]],
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,11 +83,16 @@ impl Note {
|
|||
rho: Nullifier,
|
||||
mut rng: impl RngCore,
|
||||
) -> Self {
|
||||
Note {
|
||||
loop {
|
||||
let note = Note {
|
||||
recipient,
|
||||
value,
|
||||
rho,
|
||||
rseed: RandomSeed::random(&mut rng),
|
||||
};
|
||||
if note.commitment_inner().is_some().into() {
|
||||
break note;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,12 +109,12 @@ impl Note {
|
|||
let fvk: FullViewingKey = (&sk).into();
|
||||
let recipient = fvk.default_address();
|
||||
|
||||
let note = Note {
|
||||
let note = Note::new(
|
||||
recipient,
|
||||
value: NoteValue::zero(),
|
||||
rho: rho.unwrap_or_else(|| Nullifier::dummy(rng)),
|
||||
rseed: RandomSeed::random(rng),
|
||||
};
|
||||
NoteValue::zero(),
|
||||
rho.unwrap_or_else(|| Nullifier::dummy(rng)),
|
||||
rng,
|
||||
);
|
||||
|
||||
(sk, fvk, note)
|
||||
}
|
||||
|
@ -114,23 +130,40 @@ impl Note {
|
|||
///
|
||||
/// [notes]: https://zips.z.cash/protocol/nu5.pdf#notes
|
||||
pub fn commitment(&self) -> NoteCommitment {
|
||||
// `Note` will always have a note commitment by construction.
|
||||
self.commitment_inner().unwrap()
|
||||
}
|
||||
|
||||
/// Derives the commitment to this note.
|
||||
///
|
||||
/// This is the internal fallible API, used to check at construction time that the
|
||||
/// note has a commitment. Once you have a [`Note`] object, use `note.commitment()`
|
||||
/// instead.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 3.2: Notes][notes].
|
||||
///
|
||||
/// [notes]: https://zips.z.cash/protocol/nu5.pdf#notes
|
||||
fn commitment_inner(&self) -> CtOption<NoteCommitment> {
|
||||
let g_d = self.recipient.g_d();
|
||||
|
||||
// `Note` will always have a note commitment by construction.
|
||||
NoteCommitment::derive(
|
||||
g_d.to_bytes(),
|
||||
self.recipient.pk_d().to_bytes(),
|
||||
self.value,
|
||||
self.rho.0,
|
||||
self.rseed.psi(),
|
||||
(&self.rseed).into(),
|
||||
self.rseed.psi(&self.rho),
|
||||
self.rseed.rcm(&self.rho),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Derives the nullifier for this note.
|
||||
pub fn nullifier(&self, fvk: &FullViewingKey) -> Nullifier {
|
||||
Nullifier::derive(fvk.nk(), self.rho.0, self.rseed.psi(), self.commitment())
|
||||
Nullifier::derive(
|
||||
fvk.nk(),
|
||||
self.rho.0,
|
||||
self.rseed.psi(&self.rho),
|
||||
self.commitment(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,22 +5,9 @@ use ff::PrimeField;
|
|||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
use subtle::CtOption;
|
||||
|
||||
use crate::{
|
||||
constants::L_ORCHARD_BASE,
|
||||
primitives::sinsemilla,
|
||||
spec::{extract_p, prf_expand, to_scalar},
|
||||
value::NoteValue,
|
||||
};
|
||||
use crate::{constants::L_ORCHARD_BASE, primitives::sinsemilla, spec::extract_p, value::NoteValue};
|
||||
|
||||
use super::RandomSeed;
|
||||
|
||||
pub(super) struct NoteCommitTrapdoor(pallas::Scalar);
|
||||
|
||||
impl From<&RandomSeed> for NoteCommitTrapdoor {
|
||||
fn from(rseed: &RandomSeed) -> Self {
|
||||
NoteCommitTrapdoor(to_scalar(prf_expand(&rseed.0, &[0x05])))
|
||||
}
|
||||
}
|
||||
pub(super) struct NoteCommitTrapdoor(pub(super) pallas::Scalar);
|
||||
|
||||
/// A commitment to a note.
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
};
|
||||
|
||||
/// A unique nullifier for a note.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Nullifier(pub(crate) pallas::Base);
|
||||
|
||||
impl Nullifier {
|
||||
|
|
86
src/spec.rs
86
src/spec.rs
|
@ -1,9 +1,10 @@
|
|||
//! Helper functions defined in the Zcash Protocol Specification.
|
||||
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
|
||||
use blake2b_simd::Params;
|
||||
use ff::PrimeField;
|
||||
use ff::{Field, PrimeField};
|
||||
use group::{Curve, Group};
|
||||
use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt};
|
||||
use pasta_curves::pallas;
|
||||
|
@ -16,6 +17,63 @@ use crate::{
|
|||
|
||||
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
|
||||
|
||||
/// A Pallas point that is guaranteed to not be the identity.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct NonIdentityPallasPoint(pallas::Point);
|
||||
|
||||
impl Deref for NonIdentityPallasPoint {
|
||||
type Target = pallas::Point;
|
||||
|
||||
fn deref(&self) -> &pallas::Point {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An integer in [1..q_P].
|
||||
pub(crate) struct NonZeroPallasBase(pallas::Base);
|
||||
|
||||
impl NonZeroPallasBase {
|
||||
/// Constructs a wrapper for a base field element that is guaranteed to be non-zero.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `s.is_zero()`.
|
||||
fn guaranteed(s: pallas::Base) -> Self {
|
||||
assert!(!s.is_zero());
|
||||
NonZeroPallasBase(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// An integer in [1..r_P].
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NonZeroPallasScalar(pallas::Scalar);
|
||||
|
||||
impl From<NonZeroPallasBase> for NonZeroPallasScalar {
|
||||
fn from(s: NonZeroPallasBase) -> Self {
|
||||
NonZeroPallasScalar::guaranteed(mod_r_p(s.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl NonZeroPallasScalar {
|
||||
/// Constructs a wrapper for a scalar field element that is guaranteed to be non-zero.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `s.is_zero()`.
|
||||
fn guaranteed(s: pallas::Scalar) -> Self {
|
||||
assert!(!s.is_zero());
|
||||
NonZeroPallasScalar(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for NonZeroPallasScalar {
|
||||
type Target = pallas::Scalar;
|
||||
|
||||
fn deref(&self) -> &pallas::Scalar {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// $\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].
|
||||
|
@ -49,7 +107,7 @@ pub(crate) fn commit_ivk(
|
|||
ak: &pallas::Base,
|
||||
nk: &pallas::Base,
|
||||
rivk: &pallas::Scalar,
|
||||
) -> CtOption<pallas::Scalar> {
|
||||
) -> CtOption<NonZeroPallasBase> {
|
||||
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
||||
// bits, which is equal to L_ORCHARD_BASE.
|
||||
let domain = sinsemilla::CommitDomain::new(&"z.cash:Orchard-CommitIvk");
|
||||
|
@ -60,21 +118,24 @@ pub(crate) fn commit_ivk(
|
|||
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
||||
rivk,
|
||||
)
|
||||
.map(mod_r_p)
|
||||
// Commit^ivk.Output is specified as [1..q_P] ∪ {⊥}. We get this from
|
||||
// sinsemilla::CommitDomain::short_commit by construction:
|
||||
// - 0 is not a valid x-coordinate for any Pallas point.
|
||||
// - sinsemilla::CommitDomain::short_commit calls extract_p_bottom, which replaces
|
||||
// the identity (which has no affine coordinates) with 0. but Sinsemilla is
|
||||
// defined using incomplete addition, and thus will never produce the identity.
|
||||
.map(NonZeroPallasBase::guaranteed)
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
||||
///
|
||||
/// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash
|
||||
pub(crate) fn diversify_hash(d: &[u8; 11]) -> pallas::Point {
|
||||
pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint {
|
||||
let hasher = pallas::Point::hash_to_curve("z.cash:Orchard-gd");
|
||||
let pk_d = hasher(d);
|
||||
if pk_d.is_identity().into() {
|
||||
// If the identity occurs, we replace it with a different fixed point.
|
||||
hasher(&[])
|
||||
} else {
|
||||
pk_d
|
||||
}
|
||||
// TODO: Replace the unwrap_or_else with a cached fixed point.
|
||||
NonIdentityPallasPoint(CtOption::new(pk_d, !pk_d.is_identity()).unwrap_or_else(|| hasher(&[])))
|
||||
}
|
||||
|
||||
/// $PRF^\mathsf{expand}(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)$
|
||||
|
@ -110,8 +171,11 @@ pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
|
|||
/// 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(sk: &pallas::Scalar, b: &pallas::Point) -> pallas::Point {
|
||||
b * sk
|
||||
pub(crate) fn ka_orchard(
|
||||
sk: &NonZeroPallasScalar,
|
||||
b: &NonIdentityPallasPoint,
|
||||
) -> NonIdentityPallasPoint {
|
||||
NonIdentityPallasPoint(b.deref() * sk.deref())
|
||||
}
|
||||
|
||||
/// Coordinate extractor for Pallas.
|
||||
|
|
Loading…
Reference in New Issue