masp_primitives/
sapling.rs

1//! Structs and constants specific to the Sapling shielded pool.
2
3pub mod group_hash;
4pub mod keys;
5pub mod note_encryption;
6pub mod pedersen_hash;
7pub mod prover;
8pub mod redjubjub;
9pub mod util;
10
11use bitvec::{order::Lsb0, view::AsBits};
12use blake2s_simd::Params as Blake2sParams;
13use borsh::{BorshDeserialize, BorshSerialize};
14use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
15use ff::{Field, PrimeField};
16use group::{cofactor::CofactorGroup, Curve, Group, GroupEncoding};
17use incrementalmerkletree::{self, Altitude};
18use lazy_static::lazy_static;
19use rand_core::{CryptoRng, RngCore};
20use std::{
21    array::TryFromSliceError,
22    cmp::Ordering,
23    convert::TryFrom,
24    fmt::{Display, Formatter},
25    hash::{Hash, Hasher},
26    io::{self, Read, Write},
27    str::FromStr,
28};
29use subtle::{Choice, ConstantTimeEq, CtOption};
30
31use crate::{
32    asset_type::AssetType,
33    constants::{self, SPENDING_KEY_GENERATOR},
34    keys::prf_expand,
35    merkle_tree::{HashSer, Hashable},
36    transaction::components::amount::MAX_MONEY,
37};
38
39use self::{
40    group_hash::group_hash,
41    pedersen_hash::{pedersen_hash, Personalization},
42    redjubjub::{PrivateKey, PublicKey, Signature},
43};
44use borsh::schema::add_definition;
45use borsh::schema::Declaration;
46use borsh::schema::Definition;
47use borsh::schema::Fields;
48use borsh::BorshSchema;
49use std::collections::BTreeMap;
50
51pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
52
53/// Compute a parent node in the Sapling commitment tree given its two children.
54pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] {
55    let lhs = {
56        let mut tmp = [false; 256];
57        for (a, b) in tmp.iter_mut().zip(lhs.as_bits::<Lsb0>()) {
58            *a = *b;
59        }
60        tmp
61    };
62
63    let rhs = {
64        let mut tmp = [false; 256];
65        for (a, b) in tmp.iter_mut().zip(rhs.as_bits::<Lsb0>()) {
66            *a = *b;
67        }
68        tmp
69    };
70
71    jubjub::ExtendedPoint::from(pedersen_hash(
72        Personalization::MerkleTree(depth),
73        lhs.iter()
74            .copied()
75            .take(bls12_381::Scalar::NUM_BITS as usize)
76            .chain(
77                rhs.iter()
78                    .copied()
79                    .take(bls12_381::Scalar::NUM_BITS as usize),
80            ),
81    ))
82    .to_affine()
83    .get_u()
84    .to_repr()
85}
86
87/// A node within the Sapling commitment tree.
88#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
89#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Default)]
90pub struct Node {
91    repr: [u8; 32],
92}
93
94impl Node {
95    pub fn new(repr: [u8; 32]) -> Self {
96        Node { repr }
97    }
98
99    /// Constructs a new note commitment tree node from a [`bls12_381::Scalar`]
100    pub fn from_scalar(cmu: bls12_381::Scalar) -> Self {
101        Self {
102            repr: cmu.to_repr(),
103        }
104    }
105}
106
107impl incrementalmerkletree::Hashable for Node {
108    fn empty_leaf() -> Self {
109        Node {
110            repr: Note::uncommitted().to_repr(),
111        }
112    }
113
114    fn combine(altitude: Altitude, lhs: &Self, rhs: &Self) -> Self {
115        Node {
116            repr: merkle_hash(altitude.into(), &lhs.repr, &rhs.repr),
117        }
118    }
119
120    fn empty_root(altitude: Altitude) -> Self {
121        EMPTY_ROOTS[<usize>::from(altitude)]
122    }
123}
124
125impl HashSer for Node {
126    fn read<R: Read>(mut reader: R) -> io::Result<Self> {
127        let mut repr = [0u8; 32];
128        reader.read_exact(&mut repr)?;
129        Ok(Node { repr })
130    }
131
132    fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
133        writer.write_all(self.repr.as_ref())
134    }
135}
136
137impl From<Node> for bls12_381::Scalar {
138    fn from(node: Node) -> Self {
139        // Tree nodes should be in the prime field.
140        bls12_381::Scalar::from_repr(node.repr).unwrap()
141    }
142}
143
144lazy_static! {
145    static ref EMPTY_ROOTS: Vec<Node> = {
146        let mut v = vec![Node::blank()];
147        for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
148            let next = Node::combine(d, &v[d], &v[d]);
149            v.push(next);
150        }
151        v
152    };
153}
154
155/// Create the spendAuthSig for a Sapling SpendDescription.
156pub fn spend_sig<R: RngCore + CryptoRng>(
157    ask: PrivateKey,
158    ar: jubjub::Fr,
159    sighash: &[u8; 32],
160    rng: &mut R,
161) -> Signature {
162    spend_sig_internal(ask, ar, sighash, rng)
163}
164
165pub(crate) fn spend_sig_internal<R: RngCore>(
166    ask: PrivateKey,
167    ar: jubjub::Fr,
168    sighash: &[u8; 32],
169    rng: &mut R,
170) -> Signature {
171    // We compute `rsk`...
172    let rsk = ask.randomize(ar);
173
174    // We compute `rk` from there (needed for key prefixing)
175    let rk = PublicKey::from_private(&rsk, SPENDING_KEY_GENERATOR);
176
177    // Compute the signature's message for rk/spend_auth_sig
178    let mut data_to_be_signed = [0u8; 64];
179    data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
180    data_to_be_signed[32..64].copy_from_slice(&sighash[..]);
181
182    // Do the signing
183    rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR)
184}
185
186#[derive(Clone)]
187pub struct ValueCommitment {
188    pub asset_generator: jubjub::ExtendedPoint,
189    pub value: u64,
190    pub randomness: jubjub::Fr,
191}
192
193impl ValueCommitment {
194    pub fn commitment(&self) -> jubjub::SubgroupPoint {
195        (CofactorGroup::clear_cofactor(&self.asset_generator) * jubjub::Fr::from(self.value))
196            + (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness)
197    }
198}
199
200#[derive(Clone, Debug)]
201pub struct ProofGenerationKey {
202    pub ak: jubjub::SubgroupPoint,
203    pub nsk: jubjub::Fr,
204}
205
206impl ProofGenerationKey {
207    pub fn to_viewing_key(&self) -> ViewingKey {
208        ViewingKey {
209            ak: self.ak,
210            nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
211        }
212    }
213}
214
215impl BorshSerialize for ProofGenerationKey {
216    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
217        writer.write_all(&self.ak.to_bytes())?;
218        writer.write_all(&self.nsk.to_repr())?;
219        Ok(())
220    }
221}
222
223impl BorshDeserialize for ProofGenerationKey {
224    fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
225        let ak = {
226            let mut buf = [0u8; 32];
227            reader.read_exact(&mut buf)?;
228            jubjub::SubgroupPoint::from_bytes(&buf)
229        };
230        if ak.is_none().into() {
231            return Err(io::Error::new(
232                io::ErrorKind::InvalidInput,
233                "ak not in prime-order subgroup",
234            ));
235        }
236        let nsk_bytes = <[u8; 32]>::deserialize_reader(reader)?;
237        let nsk = Option::from(jubjub::Fr::from_bytes(&nsk_bytes))
238            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?;
239        Ok(Self {
240            ak: ak.unwrap(),
241            nsk,
242        })
243    }
244}
245
246impl BorshSchema for ProofGenerationKey {
247    fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
248        let definition = Definition::Struct {
249            fields: Fields::NamedFields(vec![
250                ("ak".into(), <[u8; 32]>::declaration()),
251                ("nsk".into(), <[u8; 32]>::declaration()),
252            ]),
253        };
254        add_definition(Self::declaration(), definition, definitions);
255        <[u8; 32]>::add_definitions_recursively(definitions);
256    }
257
258    fn declaration() -> Declaration {
259        "ProofGenerationKey".into()
260    }
261}
262
263/// A key used to derive the nullifier for a Sapling note.
264#[derive(Debug, Copy, Clone, PartialEq, Eq)]
265#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
266pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
267
268impl BorshSerialize for NullifierDerivingKey {
269    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
270        writer.write_all(&self.0.to_bytes())
271    }
272}
273
274impl BorshDeserialize for NullifierDerivingKey {
275    fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
276        let nk = {
277            let mut buf = [0u8; 32];
278            reader.read_exact(&mut buf)?;
279            jubjub::SubgroupPoint::from_bytes(&buf)
280        };
281        if nk.is_none().into() {
282            return Err(io::Error::new(
283                io::ErrorKind::InvalidInput,
284                "nk not in prime-order subgroup",
285            ));
286        }
287        Ok(Self(nk.unwrap()))
288    }
289}
290
291impl BorshSchema for NullifierDerivingKey {
292    fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
293        let definition = Definition::Struct {
294            fields: Fields::UnnamedFields(vec![<[u8; 32]>::declaration()]),
295        };
296        add_definition(Self::declaration(), definition, definitions);
297        <[u8; 32]>::add_definitions_recursively(definitions);
298    }
299
300    fn declaration() -> Declaration {
301        "NullifierDerivingKey".into()
302    }
303}
304
305#[derive(Debug, PartialEq, Eq, Clone, Copy)]
306#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
307pub struct ViewingKey {
308    pub ak: jubjub::SubgroupPoint,
309    pub nk: NullifierDerivingKey,
310}
311
312impl Hash for ViewingKey {
313    fn hash<H>(&self, state: &mut H)
314    where
315        H: Hasher,
316    {
317        self.ak.to_bytes().hash(state);
318        self.nk.0.to_bytes().hash(state);
319    }
320}
321
322impl ViewingKey {
323    pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint {
324        self.ak + constants::SPENDING_KEY_GENERATOR * ar
325    }
326
327    pub fn ivk(&self) -> SaplingIvk {
328        let mut h = [0; 32];
329        h.copy_from_slice(
330            Blake2sParams::new()
331                .hash_length(32)
332                .personal(constants::CRH_IVK_PERSONALIZATION)
333                .to_state()
334                .update(&self.ak.to_bytes())
335                .update(&self.nk.0.to_bytes())
336                .finalize()
337                .as_bytes(),
338        );
339
340        // Drop the most significant five bits, so it can be interpreted as a scalar.
341        h[31] &= 0b0000_0111;
342
343        SaplingIvk(jubjub::Fr::from_repr(h).unwrap())
344    }
345
346    pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
347        self.ivk().to_payment_address(diversifier)
348    }
349
350    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
351        let ak = {
352            let mut buf = [0u8; 32];
353            reader.read_exact(&mut buf)?;
354            jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity()))
355        };
356        let nk = {
357            let mut buf = [0u8; 32];
358            reader.read_exact(&mut buf)?;
359            jubjub::SubgroupPoint::from_bytes(&buf)
360        };
361        if ak.is_none().into() {
362            return Err(io::Error::new(
363                io::ErrorKind::InvalidInput,
364                "ak not of prime order",
365            ));
366        }
367        if nk.is_none().into() {
368            return Err(io::Error::new(
369                io::ErrorKind::InvalidInput,
370                "nk not in prime-order subgroup",
371            ));
372        }
373        let ak = ak.unwrap();
374        let nk = nk.unwrap();
375
376        Ok(ViewingKey {
377            ak,
378            nk: NullifierDerivingKey(nk),
379        })
380    }
381
382    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
383        writer.write_all(&self.ak.to_bytes())?;
384        writer.write_all(&self.nk.0.to_bytes())?;
385
386        Ok(())
387    }
388
389    pub fn to_bytes(&self) -> [u8; 64] {
390        let mut result = [0u8; 64];
391        self.write(&mut result[..])
392            .expect("should be able to serialize a ViewingKey");
393        result
394    }
395}
396
397impl BorshSerialize for ViewingKey {
398    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
399        self.write(writer)
400    }
401}
402
403impl BorshDeserialize for ViewingKey {
404    fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
405        Self::read(reader)
406    }
407}
408
409impl BorshSchema for ViewingKey {
410    fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
411        let definition = Definition::Struct {
412            fields: Fields::NamedFields(vec![
413                ("ak".into(), <[u8; 32]>::declaration()),
414                ("nk".into(), NullifierDerivingKey::declaration()),
415            ]),
416        };
417        add_definition(Self::declaration(), definition, definitions);
418        <[u8; 32]>::add_definitions_recursively(definitions);
419        NullifierDerivingKey::add_definitions_recursively(definitions);
420    }
421
422    fn declaration() -> Declaration {
423        "ViewingKey".into()
424    }
425}
426
427impl PartialOrd for ViewingKey {
428    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
429        Some(self.cmp(other))
430    }
431}
432
433impl Ord for ViewingKey {
434    fn cmp(&self, other: &Self) -> Ordering {
435        self.to_bytes().cmp(&other.to_bytes())
436    }
437}
438
439#[derive(Debug, Clone)]
440pub struct SaplingIvk(pub jubjub::Fr);
441
442impl SaplingIvk {
443    pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
444        diversifier.g_d().and_then(|g_d| {
445            let pk_d = g_d * self.0;
446
447            PaymentAddress::from_parts(diversifier, pk_d)
448        })
449    }
450
451    pub fn to_repr(&self) -> [u8; 32] {
452        self.0.to_repr()
453    }
454}
455
456#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
457#[derive(
458    Copy, Clone, Debug, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, BorshSchema,
459)]
460pub struct Diversifier(pub [u8; 11]);
461
462impl Diversifier {
463    pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
464        group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION)
465    }
466}
467
468/// A Sapling payment address.
469///
470/// # Invariants
471///
472/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
473/// and not the identity).
474#[derive(Clone, Copy, Debug)]
475pub struct PaymentAddress {
476    pk_d: jubjub::SubgroupPoint,
477    diversifier: Diversifier,
478}
479
480impl PartialEq for PaymentAddress {
481    fn eq(&self, other: &Self) -> bool {
482        self.pk_d == other.pk_d && self.diversifier == other.diversifier
483    }
484}
485
486impl Eq for PaymentAddress {}
487
488impl PaymentAddress {
489    /// Constructs a PaymentAddress from a diversifier and a Jubjub point.
490    ///
491    /// Returns None if `pk_d` is the identity.
492    pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option<Self> {
493        if pk_d.is_identity().into() {
494            None
495        } else {
496            Some(PaymentAddress { pk_d, diversifier })
497        }
498    }
499
500    /// Constructs a PaymentAddress from a diversifier and a Jubjub point.
501    ///
502    /// Only for test code, as this explicitly bypasses the invariant.
503    #[cfg(test)]
504    #[allow(dead_code)]
505    pub(crate) fn from_parts_unchecked(
506        diversifier: Diversifier,
507        pk_d: jubjub::SubgroupPoint,
508    ) -> Self {
509        PaymentAddress { pk_d, diversifier }
510    }
511
512    /// Parses a PaymentAddress from bytes.
513    pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
514        let diversifier = {
515            let mut tmp = [0; 11];
516            tmp.copy_from_slice(&bytes[0..11]);
517            Diversifier(tmp)
518        };
519        // Check that the diversifier is valid
520        diversifier.g_d()?;
521
522        let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap());
523        if pk_d.is_some().into() {
524            PaymentAddress::from_parts(diversifier, pk_d.unwrap())
525        } else {
526            None
527        }
528    }
529
530    /// Returns the byte encoding of this `PaymentAddress`.
531    pub fn to_bytes(&self) -> [u8; 43] {
532        let mut bytes = [0; 43];
533        bytes[0..11].copy_from_slice(&self.diversifier.0);
534        bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
535        bytes
536    }
537
538    /// Returns the [`Diversifier`] for this `PaymentAddress`.
539    pub fn diversifier(&self) -> &Diversifier {
540        &self.diversifier
541    }
542
543    /// Returns `pk_d` for this `PaymentAddress`.
544    pub fn pk_d(&self) -> &jubjub::SubgroupPoint {
545        &self.pk_d
546    }
547
548    pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
549        self.diversifier.g_d()
550    }
551
552    pub fn create_note(&self, asset_type: AssetType, value: u64, rseed: Rseed) -> Option<Note> {
553        self.g_d().map(|g_d| Note {
554            asset_type,
555            value,
556            rseed,
557            g_d,
558            pk_d: self.pk_d,
559        })
560    }
561}
562
563impl Display for PaymentAddress {
564    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
565        write!(f, "{}", hex::encode(self.to_bytes()))
566    }
567}
568
569impl FromStr for PaymentAddress {
570    type Err = io::Error;
571
572    fn from_str(s: &str) -> Result<Self, Self::Err> {
573        let vec = hex::decode(s).map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))?;
574        BorshDeserialize::try_from_slice(&vec)
575    }
576}
577
578impl PartialOrd for PaymentAddress {
579    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
580        Some(self.cmp(other))
581    }
582}
583impl Ord for PaymentAddress {
584    fn cmp(&self, other: &Self) -> Ordering {
585        self.to_bytes().cmp(&other.to_bytes())
586    }
587}
588impl Hash for PaymentAddress {
589    fn hash<H: Hasher>(&self, state: &mut H) {
590        self.to_bytes().hash(state)
591    }
592}
593
594impl BorshSerialize for PaymentAddress {
595    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
596        writer.write(self.to_bytes().as_ref()).and(Ok(()))
597    }
598}
599impl BorshDeserialize for PaymentAddress {
600    fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
601        let mut data = [0u8; 43];
602        reader.read_exact(&mut data)?;
603        let res = Self::from_bytes(&data);
604        let pa = res.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?;
605        Ok(pa)
606    }
607}
608impl BorshSchema for PaymentAddress {
609    fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
610        let definition = Definition::Struct {
611            fields: Fields::NamedFields(vec![
612                ("diversifier".into(), Diversifier::declaration()),
613                ("pk_d".into(), <[u8; 32]>::declaration()),
614            ]),
615        };
616        add_definition(Self::declaration(), definition, definitions);
617        Diversifier::add_definitions_recursively(definitions);
618        <[u8; 32]>::add_definitions_recursively(definitions);
619    }
620
621    fn declaration() -> Declaration {
622        "PaymentAddress".into()
623    }
624}
625
626/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212).
627///
628/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value.
629/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive
630/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`.
631#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
632#[derive(Copy, Clone, Debug)]
633pub enum Rseed {
634    BeforeZip212(jubjub::Fr),
635    AfterZip212([u8; 32]),
636}
637
638impl BorshSchema for Rseed {
639    fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
640        let definition = Definition::Enum {
641            tag_width: 1,
642            variants: vec![
643                (1, "BeforeZip212".into(), <[u8; 32]>::declaration()),
644                (2, "AfterZip212".into(), <[u8; 32]>::declaration()),
645            ],
646        };
647        add_definition(Self::declaration(), definition, definitions);
648        <[u8; 32]>::add_definitions_recursively(definitions);
649    }
650
651    fn declaration() -> Declaration {
652        "Rseed".into()
653    }
654}
655
656impl BorshSerialize for Rseed {
657    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> io::Result<()> {
658        match self {
659            Rseed::BeforeZip212(rcm) => {
660                // Write note plaintext lead byte
661                writer.write_u8(1)?;
662                // Write rseed
663                writer.write_all(&rcm.to_repr())
664            }
665            Rseed::AfterZip212(rseed) => {
666                // Write note plaintext lead byte
667                writer.write_u8(2)?;
668                // Write rseed
669                writer.write_all(rseed)
670            }
671        }?;
672        Ok(())
673    }
674}
675
676impl BorshDeserialize for Rseed {
677    fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
678        // Read note plaintext lead byte
679        let rseed_type = reader.read_u8()?;
680        // Read rseed
681        let rseed_bytes = <[u8; 32]>::deserialize_reader(reader)?;
682        let rseed = if rseed_type == 0x01 {
683            let data = Option::from(jubjub::Fr::from_bytes(&rseed_bytes))
684                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "rseed not in field"))?;
685            Rseed::BeforeZip212(data)
686        } else {
687            Rseed::AfterZip212(rseed_bytes)
688        };
689        Ok(rseed)
690    }
691}
692
693/// Typesafe wrapper for nullifier values.
694#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
695#[derive(
696    Copy,
697    Clone,
698    Debug,
699    PartialEq,
700    Eq,
701    PartialOrd,
702    Ord,
703    Hash,
704    BorshSerialize,
705    BorshDeserialize,
706    BorshSchema,
707)]
708pub struct Nullifier(pub [u8; 32]);
709
710impl Nullifier {
711    pub fn from_slice(bytes: &[u8]) -> Result<Nullifier, TryFromSliceError> {
712        bytes.try_into().map(Nullifier)
713    }
714
715    pub fn to_vec(&self) -> Vec<u8> {
716        self.0.to_vec()
717    }
718}
719impl AsRef<[u8]> for Nullifier {
720    fn as_ref(&self) -> &[u8] {
721        &self.0
722    }
723}
724
725impl ConstantTimeEq for Nullifier {
726    fn ct_eq(&self, other: &Self) -> Choice {
727        self.0.ct_eq(&other.0)
728    }
729}
730
731#[derive(Clone, Copy, Debug, PartialEq, Eq)]
732pub struct NoteValue(u64);
733
734impl TryFrom<u64> for NoteValue {
735    type Error = ();
736
737    fn try_from(value: u64) -> Result<Self, Self::Error> {
738        if value <= MAX_MONEY {
739            Ok(NoteValue(value))
740        } else {
741            Err(())
742        }
743    }
744}
745
746impl From<NoteValue> for u64 {
747    fn from(value: NoteValue) -> u64 {
748        value.0
749    }
750}
751
752#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
753#[derive(Clone, Debug, Copy)]
754pub struct Note<R = Rseed> {
755    /// The asset type that the note represents
756    pub asset_type: AssetType,
757    /// The value of the note
758    pub value: u64,
759    /// The diversified base of the address, GH(d)
760    pub g_d: jubjub::SubgroupPoint,
761    /// The public key of the address, g_d^ivk
762    pub pk_d: jubjub::SubgroupPoint,
763    /// rseed
764    pub rseed: R,
765}
766
767impl PartialEq for Note {
768    fn eq(&self, other: &Self) -> bool {
769        self.value == other.value
770            && self.asset_type == other.asset_type
771            && self.g_d == other.g_d
772            && self.pk_d == other.pk_d
773            && self.rcm() == other.rcm()
774    }
775}
776
777impl Note {
778    pub fn uncommitted() -> bls12_381::Scalar {
779        // The smallest u-coordinate that is not on the curve
780        // is one.
781        bls12_381::Scalar::one()
782    }
783
784    /// Computes the note commitment, returning the full point.
785    fn cm_full_point(&self) -> jubjub::SubgroupPoint {
786        // Calculate the note contents, as bytes
787        let mut note_contents = vec![];
788
789        // Write the asset generator, cofactor not cleared
790        note_contents.extend_from_slice(&self.asset_type.asset_generator().to_bytes());
791
792        // Writing the value in little endian
793        note_contents.write_u64::<LittleEndian>(self.value).unwrap();
794
795        // Write g_d
796        note_contents.extend_from_slice(&self.g_d.to_bytes());
797
798        // Write pk_d
799        note_contents.extend_from_slice(&self.pk_d.to_bytes());
800
801        assert_eq!(note_contents.len(), 32 + 32 + 32 + 8);
802
803        // Compute the Pedersen hash of the note contents
804        let hash_of_contents = pedersen_hash(
805            Personalization::NoteCommitment,
806            note_contents
807                .into_iter()
808                .flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
809        );
810
811        // Compute final commitment
812        (constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents
813    }
814
815    /// Computes the nullifier given the nullifier deriving key and
816    /// note position
817    pub fn nf(&self, nk: &NullifierDerivingKey, position: u64) -> Nullifier {
818        // Compute rho = cm + position.G
819        let rho = self.cm_full_point()
820            + (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position));
821
822        // Compute nf = BLAKE2s(nk | rho)
823        Nullifier::from_slice(
824            Blake2sParams::new()
825                .hash_length(32)
826                .personal(constants::PRF_NF_PERSONALIZATION)
827                .to_state()
828                .update(&nk.0.to_bytes())
829                .update(&rho.to_bytes())
830                .finalize()
831                .as_bytes(),
832        )
833        .unwrap()
834    }
835
836    /// Computes the note commitment
837    pub fn cmu(&self) -> bls12_381::Scalar {
838        // The commitment is in the prime order subgroup, so mapping the
839        // commitment to the u-coordinate is an injective encoding.
840        jubjub::ExtendedPoint::from(self.cm_full_point())
841            .to_affine()
842            .get_u()
843    }
844
845    pub fn rcm(&self) -> jubjub::Fr {
846        match self.rseed {
847            Rseed::BeforeZip212(rcm) => rcm,
848            Rseed::AfterZip212(rseed) => {
849                jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array())
850            }
851        }
852    }
853
854    pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> jubjub::Fr {
855        self.generate_or_derive_esk_internal(rng)
856    }
857
858    pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(&self, rng: &mut R) -> jubjub::Fr {
859        match self.derive_esk() {
860            None => jubjub::Fr::random(rng),
861            Some(esk) => esk,
862        }
863    }
864
865    /// Returns the derived `esk` if this note was created after ZIP 212 activated.
866    pub fn derive_esk(&self) -> Option<jubjub::Fr> {
867        match self.rseed {
868            Rseed::BeforeZip212(_) => None,
869            Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide(
870                prf_expand(&rseed, &[0x05]).as_array(),
871            )),
872        }
873    }
874
875    /// Returns [`self.cmu`] in the correct representation for inclusion in the Sapling
876    /// note commitment tree.
877    pub fn commitment(&self) -> Node {
878        Node {
879            repr: self.cmu().to_repr(),
880        }
881    }
882}
883
884impl<T: BorshSchema> BorshSchema for Note<T> {
885    fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
886        let definition = Definition::Struct {
887            fields: Fields::NamedFields(vec![
888                ("asset_type".into(), AssetType::declaration()),
889                ("value".into(), u64::declaration()),
890                ("g_d".into(), <[u8; 32]>::declaration()),
891                ("pk_d".into(), <[u8; 32]>::declaration()),
892                ("rseed".into(), T::declaration()),
893            ]),
894        };
895        add_definition(Self::declaration(), definition, definitions);
896        AssetType::add_definitions_recursively(definitions);
897        u64::add_definitions_recursively(definitions);
898        <[u8; 32]>::add_definitions_recursively(definitions);
899        T::add_definitions_recursively(definitions);
900    }
901
902    fn declaration() -> Declaration {
903        format!("Note<{}>", T::declaration())
904    }
905}
906
907impl<T: BorshSerialize> BorshSerialize for Note<T> {
908    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> io::Result<()> {
909        // Write asset type
910        self.asset_type.serialize(writer)?;
911        // Write note value
912        writer.write_u64::<LittleEndian>(self.value)?;
913        // Write diversified base
914        writer.write_all(&self.g_d.to_bytes())?;
915        // Write diversified transmission key
916        writer.write_all(&self.pk_d.to_bytes())?;
917        // Write the rseed
918        self.rseed.serialize(writer)?;
919        Ok(())
920    }
921}
922
923impl<T: BorshDeserialize> BorshDeserialize for Note<T> {
924    fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
925        // Read asset type
926        let asset_type = AssetType::deserialize_reader(reader)?;
927        // Read note value
928        let value = reader.read_u64::<LittleEndian>()?;
929        // Read diversified base
930        let g_d_bytes = <[u8; 32]>::deserialize_reader(reader)?;
931        let g_d = Option::from(jubjub::SubgroupPoint::from_bytes(&g_d_bytes))
932            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "g_d not in field"))?;
933        // Read diversified transmission key
934        let pk_d_bytes = <[u8; 32]>::deserialize_reader(reader)?;
935        let pk_d = Option::from(jubjub::SubgroupPoint::from_bytes(&pk_d_bytes))
936            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pk_d not in field"))?;
937        // Read note plaintext lead byte
938        let rseed = T::deserialize_reader(reader)?;
939        // Finally construct note object
940        Ok(Note {
941            asset_type,
942            value,
943            g_d,
944            pk_d,
945            rseed,
946        })
947    }
948}
949#[cfg(any(test, feature = "test-dependencies"))]
950pub mod testing {
951    use proptest::prelude::*;
952    use std::cmp::min;
953
954    use crate::transaction::components::amount::MAX_MONEY;
955
956    use super::{
957        keys::testing::arb_full_viewing_key, Diversifier, Node, Note, NoteValue, PaymentAddress,
958        Rseed, SaplingIvk,
959    };
960
961    prop_compose! {
962        pub fn arb_note_value()(value in 0u64..=MAX_MONEY) -> NoteValue {
963            NoteValue::try_from(value).unwrap()
964        }
965    }
966
967    prop_compose! {
968        /// The
969        pub fn arb_positive_note_value(bound: u64)(
970            value in 1u64..=(min(bound, MAX_MONEY))
971        ) -> NoteValue {
972            NoteValue::try_from(value).unwrap()
973        }
974    }
975
976    prop_compose! {
977        pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
978            fvk.vk.ivk()
979        }
980    }
981
982    pub fn arb_payment_address() -> impl Strategy<Value = PaymentAddress> {
983        arb_incoming_viewing_key().prop_flat_map(|ivk: SaplingIvk| {
984            any::<[u8; 11]>().prop_filter_map(
985                "Sampled diversifier must generate a valid Sapling payment address.",
986                move |d| ivk.to_payment_address(Diversifier(d)),
987            )
988        })
989    }
990
991    prop_compose! {
992        pub fn arb_node()(value in prop::array::uniform32(prop::num::u8::ANY)) -> Node {
993            Node {
994                repr: value
995            }
996        }
997    }
998
999    prop_compose! {
1000        pub fn arb_note(value: NoteValue)(
1001            asset_type in crate::asset_type::testing::arb_asset_type(),
1002            addr in arb_payment_address(),
1003            rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212)
1004                ) -> Note {
1005            Note {
1006                value: value.into(),
1007                g_d: addr.g_d().unwrap(), // this unwrap is safe because arb_payment_address always generates an address with a valid g_d
1008                pk_d: *addr.pk_d(),
1009                rseed,
1010                asset_type
1011            }
1012        }
1013    }
1014}
1015
1016#[cfg(test)]
1017mod tests {
1018    use crate::{
1019        sapling::testing::{arb_note, arb_positive_note_value},
1020        sapling::Note,
1021        transaction::components::amount::MAX_MONEY,
1022    };
1023    use borsh::BorshDeserialize;
1024    use proptest::prelude::*;
1025
1026    proptest! {
1027        #![proptest_config(ProptestConfig::with_cases(10))]
1028        #[test]
1029        fn note_serialization(note in arb_positive_note_value(MAX_MONEY).prop_flat_map(arb_note)) {
1030            // BorshSerialize
1031            let borsh = borsh::to_vec(&note).unwrap();
1032            // BorshDeserialize
1033            let de_note: Note = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap();
1034            prop_assert_eq!(note, de_note);
1035        }
1036    }
1037}