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)?,
})
}
}