1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Orchard notes

use group::{ff::PrimeField, GroupEncoding};
use halo2::pasta::pallas;
use rand_core::{CryptoRng, RngCore};

use crate::{
    amount::{Amount, NonNegative},
    error::{NoteError, RandError},
};

use super::{address::Address, sinsemilla::extract_p};

mod ciphertexts;
mod nullifiers;

pub use ciphertexts::{EncryptedNote, WrappedNoteKey};
pub use nullifiers::Nullifier;

#[cfg(any(test, feature = "proptest-impl"))]
mod arbitrary;

/// A random seed (rseed) used in the Orchard note creation.
#[derive(Clone, Copy, Debug)]
// At the moment this field is never read.
//
// TODO: consider replacing this code with the equivalent `orchard` crate code,
//       which is better tested.
#[allow(dead_code)]
pub struct SeedRandomness(pub(crate) [u8; 32]);

impl SeedRandomness {
    pub fn new<T>(csprng: &mut T) -> Result<Self, RandError>
    where
        T: RngCore + CryptoRng,
    {
        let mut bytes = [0u8; 32];
        csprng
            .try_fill_bytes(&mut bytes)
            .map_err(|_| RandError::FillBytes)?;
        Ok(Self(bytes))
    }
}

/// Used as input to PRF^nf as part of deriving the _nullifier_ of the _note_.
///
/// When creating a new note from spending an old note, the new note's _rho_ is
/// the _nullifier_ of the previous note. If creating a note from scratch (like
/// a miner reward), a dummy note is constructed, and its nullifier as the _rho_
/// for the actual output note. When creating a dummy note, its _rho_ is chosen
/// as a random Pallas point's x-coordinate.
///
/// <https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes>
#[derive(Clone, Debug)]
pub struct Rho(pub(crate) pallas::Base);

impl From<Rho> for [u8; 32] {
    fn from(rho: Rho) -> Self {
        rho.0.to_repr()
    }
}

impl From<Nullifier> for Rho {
    fn from(nf: Nullifier) -> Self {
        Self(nf.0)
    }
}

impl Rho {
    pub fn new<T>(csprng: &mut T) -> Result<Self, NoteError>
    where
        T: RngCore + CryptoRng,
    {
        let mut bytes = [0u8; 32];
        csprng
            .try_fill_bytes(&mut bytes)
            .map_err(|_| NoteError::from(RandError::FillBytes))?;

        let possible_point = pallas::Point::from_bytes(&bytes);

        if possible_point.is_some().into() {
            Ok(Self(extract_p(possible_point.unwrap())))
        } else {
            Err(NoteError::InvalidRho)
        }
    }
}

/// Additional randomness used in deriving the _nullifier_.
///
/// <https://zips.z.cash/protocol/nu5.pdf#orchardsend>
#[derive(Clone, Debug)]
pub struct Psi(pub(crate) pallas::Base);

impl From<Psi> for [u8; 32] {
    fn from(psi: Psi) -> Self {
        psi.0.to_repr()
    }
}

/// A Note represents that a value is spendable by the recipient who holds the
/// spending key corresponding to a given shielded payment address.
///
/// <https://zips.z.cash/protocol/protocol.pdf#notes>
#[derive(Clone, Debug)]
pub struct Note {
    /// The recipient's shielded payment address.
    pub address: Address,
    /// An integer representing the value of the _note_ in zatoshi.
    pub value: Amount<NonNegative>,
    /// Used as input to PRF^nfOrchard_nk as part of deriving the _nullifier_ of
    /// the _note_.
    pub rho: Rho,
    /// 32 random bytes from which _rcm_, _psi_, and the _ephemeral private key_
    /// are derived.
    pub rseed: SeedRandomness,
}

impl Note {
    /// Create an Orchard _note_, by choosing 32 uniformly random bytes for
    /// rseed.
    ///
    /// <https://zips.z.cash/protocol/protocol.pdf#notes>
    pub fn new<T>(
        csprng: &mut T,
        address: Address,
        value: Amount<NonNegative>,
        nf_old: Nullifier,
    ) -> Result<Self, RandError>
    where
        T: RngCore + CryptoRng,
    {
        Ok(Self {
            address,
            value,
            rho: nf_old.into(),
            rseed: SeedRandomness::new(csprng)?,
        })
    }
}