mirror of https://github.com/zcash/halo2.git
commit
92cfa372e0
|
@ -1,4 +1,9 @@
|
||||||
use crate::keys::{DiversifiedTransmissionKey, Diversifier};
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
keys::{DiversifiedTransmissionKey, Diversifier},
|
||||||
|
spec::diversify_hash,
|
||||||
|
};
|
||||||
|
|
||||||
/// A shielded payment address.
|
/// A shielded payment address.
|
||||||
///
|
///
|
||||||
|
@ -18,6 +23,18 @@ pub struct Address {
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
pub(crate) fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self {
|
pub(crate) fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self {
|
||||||
|
// We assume here that pk_d is correctly-derived from d. We ensure this for
|
||||||
|
// internal APIs. For parsing from raw byte encodings, we assume that users aren't
|
||||||
|
// modifying internals of encoded address formats. If they do, that can result in
|
||||||
|
// lost funds, but we can't defend against that from here.
|
||||||
Address { d, pk_d }
|
Address { d, pk_d }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn g_d(&self) -> pallas::Point {
|
||||||
|
diversify_hash(self.d.as_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pk_d(&self) -> &DiversifiedTransmissionKey {
|
||||||
|
&self.pk_d
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/keys.rs
28
src/keys.rs
|
@ -14,8 +14,8 @@ 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_expand, prf_expand_vec, to_base,
|
commit_ivk, diversify_hash, extract_p, ka_orchard, prf_expand, prf_expand_vec, prf_nf,
|
||||||
to_scalar,
|
to_base, to_scalar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,6 +102,12 @@ impl From<&SpendingKey> for NullifierDerivingKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NullifierDerivingKey {
|
||||||
|
pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base {
|
||||||
|
prf_nf(self.0, rho)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The randomness for $\mathsf{Commit}^\mathsf{ivk}$.
|
/// The randomness for $\mathsf{Commit}^\mathsf{ivk}$.
|
||||||
///
|
///
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
|
@ -142,6 +148,10 @@ impl From<&SpendingKey> for FullViewingKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FullViewingKey {
|
impl FullViewingKey {
|
||||||
|
pub(crate) fn nk(&self) -> &NullifierDerivingKey {
|
||||||
|
&self.nk
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -229,6 +239,13 @@ impl DiversifierKey {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Diversifier([u8; 11]);
|
pub struct Diversifier([u8; 11]);
|
||||||
|
|
||||||
|
impl Diversifier {
|
||||||
|
/// Returns the byte array corresponding to this diversifier.
|
||||||
|
pub fn as_array(&self) -> &[u8; 11] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A key that provides the capability to detect and decrypt incoming notes from the block
|
/// 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.
|
/// chain, without being able to spend the notes or detect when they are spent.
|
||||||
///
|
///
|
||||||
|
@ -290,7 +307,12 @@ impl DiversifiedTransmissionKey {
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
|
fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
|
||||||
let g_d = diversify_hash(&d.0);
|
let g_d = diversify_hash(&d.as_array());
|
||||||
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
|
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// $repr_P(self)$
|
||||||
|
pub(crate) fn to_bytes(&self) -> [u8; 32] {
|
||||||
|
self.0.to_bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
61
src/note.rs
61
src/note.rs
|
@ -1,20 +1,36 @@
|
||||||
use crate::{keys::FullViewingKey, value::NoteValue, Address};
|
use group::GroupEncoding;
|
||||||
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
keys::FullViewingKey,
|
||||||
|
spec::{prf_expand, to_base, to_scalar},
|
||||||
|
value::NoteValue,
|
||||||
|
Address,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod commitment;
|
||||||
|
pub use self::commitment::NoteCommitment;
|
||||||
|
|
||||||
|
mod nullifier;
|
||||||
|
pub use self::nullifier::Nullifier;
|
||||||
|
|
||||||
/// The ZIP 212 seed randomness for a note.
|
/// The ZIP 212 seed randomness for a note.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RandomSeed([u8; 32]);
|
struct RandomSeed([u8; 32]);
|
||||||
|
|
||||||
impl RandomSeed {
|
impl RandomSeed {
|
||||||
fn psi(&self) -> () {
|
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||||
todo!()
|
///
|
||||||
|
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||||
|
fn psi(&self) -> pallas::Base {
|
||||||
|
to_base(prf_expand(&self.0, &[0x09]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rcm(&self) -> () {
|
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||||
todo!()
|
///
|
||||||
}
|
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||||
|
fn esk(&self) -> pallas::Scalar {
|
||||||
fn esk(&self) -> () {
|
to_scalar(prf_expand(&self.0, &[0x04]))
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,24 +54,29 @@ pub struct Note {
|
||||||
|
|
||||||
impl Note {
|
impl Note {
|
||||||
/// Derives the commitment to this note.
|
/// Derives the commitment to this note.
|
||||||
|
///
|
||||||
|
/// Defined in [Zcash Protocol Spec § 3.2: Notes][notes].
|
||||||
|
///
|
||||||
|
/// [notes]: https://zips.z.cash/protocol/nu5.pdf#notes
|
||||||
pub fn commitment(&self) -> NoteCommitment {
|
pub fn commitment(&self) -> NoteCommitment {
|
||||||
todo!()
|
let g_d = self.recipient.g_d();
|
||||||
|
|
||||||
|
NoteCommitment::derive(
|
||||||
|
g_d.to_bytes(),
|
||||||
|
self.recipient.pk_d().to_bytes(),
|
||||||
|
self.value,
|
||||||
|
self.rho.0,
|
||||||
|
self.rseed.psi(),
|
||||||
|
(&self.rseed).into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derives the nullifier for this note.
|
/// Derives the nullifier for this note.
|
||||||
pub fn nullifier(&self, _: &FullViewingKey) -> Nullifier {
|
pub fn nullifier(&self, fvk: &FullViewingKey) -> Nullifier {
|
||||||
todo!()
|
Nullifier::derive(fvk.nk(), self.rho.0, self.rseed.psi(), self.commitment())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An encrypted note.
|
/// An encrypted note.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EncryptedNote;
|
pub struct EncryptedNote;
|
||||||
|
|
||||||
/// A commitment to a note.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NoteCommitment;
|
|
||||||
|
|
||||||
/// A unique nullifier for a note.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Nullifier;
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use bitvec::{array::BitArray, order::Lsb0};
|
||||||
|
use ff::PrimeField;
|
||||||
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
constants::L_ORCHARD_BASE,
|
||||||
|
primitives::sinsemilla,
|
||||||
|
spec::{prf_expand, to_scalar},
|
||||||
|
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])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A commitment to a note.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NoteCommitment(pub(super) pallas::Point);
|
||||||
|
|
||||||
|
impl NoteCommitment {
|
||||||
|
/// $NoteCommit^Orchard$.
|
||||||
|
///
|
||||||
|
/// Defined in [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit].
|
||||||
|
///
|
||||||
|
/// [concretesinsemillacommit]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
|
||||||
|
pub(super) fn derive(
|
||||||
|
g_d: [u8; 32],
|
||||||
|
pk_d: [u8; 32],
|
||||||
|
v: NoteValue,
|
||||||
|
rho: pallas::Base,
|
||||||
|
psi: pallas::Base,
|
||||||
|
rcm: NoteCommitTrapdoor,
|
||||||
|
) -> Self {
|
||||||
|
let domain = sinsemilla::CommitDomain::new("z.cash:Orchard-NoteCommit");
|
||||||
|
NoteCommitment(
|
||||||
|
domain.commit(
|
||||||
|
iter::empty()
|
||||||
|
.chain(BitArray::<Lsb0, _>::new(g_d).iter().by_val())
|
||||||
|
.chain(BitArray::<Lsb0, _>::new(pk_d).iter().by_val())
|
||||||
|
.chain(v.to_le_bits().iter().by_val())
|
||||||
|
.chain(rho.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
||||||
|
.chain(psi.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
||||||
|
&rcm.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
use halo2::arithmetic::CurveExt;
|
||||||
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
|
use super::NoteCommitment;
|
||||||
|
use crate::{
|
||||||
|
keys::NullifierDerivingKey,
|
||||||
|
spec::{extract_p, mod_r_p},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A unique nullifier for a note.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Nullifier(pub(super) pallas::Base);
|
||||||
|
|
||||||
|
impl Nullifier {
|
||||||
|
/// $DeriveNullifier$.
|
||||||
|
///
|
||||||
|
/// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers].
|
||||||
|
///
|
||||||
|
/// [commitmentsandnullifiers]: https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
|
||||||
|
pub(super) fn derive(
|
||||||
|
nk: &NullifierDerivingKey,
|
||||||
|
rho: pallas::Base,
|
||||||
|
psi: pallas::Base,
|
||||||
|
cm: NoteCommitment,
|
||||||
|
) -> Self {
|
||||||
|
let k = pallas::Point::hash_to_curve("z.cash:Orchard")(b"K");
|
||||||
|
|
||||||
|
Nullifier(extract_p(&(k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0)))
|
||||||
|
}
|
||||||
|
}
|
42
src/spec.rs
42
src/spec.rs
|
@ -8,7 +8,10 @@ use group::{Curve, Group};
|
||||||
use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt};
|
use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt};
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
use crate::{constants::L_ORCHARD_BASE, primitives::sinsemilla};
|
use crate::{
|
||||||
|
constants::L_ORCHARD_BASE,
|
||||||
|
primitives::{poseidon, sinsemilla},
|
||||||
|
};
|
||||||
|
|
||||||
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
|
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
|
||||||
|
|
||||||
|
@ -30,6 +33,14 @@ pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar {
|
||||||
pallas::Scalar::from_bytes_wide(&x)
|
pallas::Scalar::from_bytes_wide(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
|
||||||
|
///
|
||||||
|
/// This requires no modular reduction because Pallas' base field is smaller than its
|
||||||
|
/// scalar field.
|
||||||
|
pub(crate) fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
|
||||||
|
pallas::Scalar::from_repr(x.to_repr()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -41,16 +52,15 @@ pub(crate) fn commit_ivk(
|
||||||
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
||||||
// bits, which is equal to L_ORCHARD_BASE.
|
// bits, which is equal to L_ORCHARD_BASE.
|
||||||
let domain = sinsemilla::CommitDomain::new(&"z.cash:Orchard-CommitIvk");
|
let domain = sinsemilla::CommitDomain::new(&"z.cash:Orchard-CommitIvk");
|
||||||
let ivk = domain.short_commit(
|
// TODO: handle the (negligible probability of) failure of SinsemillaShortCommit.
|
||||||
iter::empty()
|
mod_r_p(
|
||||||
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
domain.short_commit(
|
||||||
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
iter::empty()
|
||||||
rivk,
|
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
||||||
);
|
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
||||||
|
rivk,
|
||||||
// Convert from pallas::Base to pallas::Scalar. This requires no modular reduction
|
),
|
||||||
// because Pallas' base field is smaller than its scalar field.
|
)
|
||||||
pallas::Scalar::from_repr(ivk.to_repr()).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
||||||
|
@ -88,6 +98,16 @@ pub(crate) fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> [u8; 64] {
|
||||||
*h.finalize().as_array()
|
*h.finalize().as_array()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// $PRF^\mathsf{nfOrchard}(nk, \rho) := Poseidon(nk, \rho)$
|
||||||
|
///
|
||||||
|
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
|
||||||
|
///
|
||||||
|
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
|
||||||
|
pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
|
||||||
|
poseidon::Hash::init(poseidon::OrchardNullifier, poseidon::ConstantLength(2))
|
||||||
|
.hash(iter::empty().chain(Some(nk)).chain(Some(rho)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
||||||
///
|
///
|
||||||
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
||||||
|
|
10
src/value.rs
10
src/value.rs
|
@ -13,10 +13,18 @@
|
||||||
//! [`Action`]: crate::bundle::Action
|
//! [`Action`]: crate::bundle::Action
|
||||||
//! [`Bundle`]: crate::bundle::Bundle
|
//! [`Bundle`]: crate::bundle::Bundle
|
||||||
|
|
||||||
|
use bitvec::{array::BitArray, order::Lsb0};
|
||||||
|
|
||||||
/// The value of an individual Orchard note.
|
/// The value of an individual Orchard note.
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct NoteValue(u64);
|
pub struct NoteValue(u64);
|
||||||
|
|
||||||
|
impl NoteValue {
|
||||||
|
pub(crate) fn to_le_bits(&self) -> BitArray<Lsb0, [u8; 8]> {
|
||||||
|
BitArray::<Lsb0, _>::new(self.0.to_le_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A sum of Orchard note values.
|
/// A sum of Orchard note values.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ValueSum(i64);
|
pub struct ValueSum(i64);
|
||||||
|
|
Loading…
Reference in New Issue