Merge pull request #60 from zcash/dummy-notes

Dummy note generation
This commit is contained in:
str4d 2021-04-22 14:04:50 +01:00 committed by GitHub
commit 0ccb0101df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 1 deletions

View File

@ -8,6 +8,7 @@ use fpe::ff1::{BinaryNumeralString, FF1};
use group::GroupEncoding;
use halo2::arithmetic::FieldExt;
use pasta_curves::pallas;
use rand::RngCore;
use subtle::CtOption;
use crate::{
@ -28,6 +29,23 @@ use crate::{
pub struct SpendingKey([u8; 32]);
impl SpendingKey {
/// Generates a random spending key.
///
/// This is only used when generating dummy notes. Real spending keys should be
/// derived according to [ZIP 32].
///
/// [ZIP 32]: https://zips.z.cash/zip-0032
pub(crate) fn random(rng: &mut impl RngCore) -> Self {
loop {
let mut bytes = [0; 32];
rng.fill_bytes(&mut bytes);
let sk = SpendingKey::from_bytes(bytes);
if sk.is_some().into() {
break sk.unwrap();
}
}
}
/// Constructs an Orchard spending key from uniformly-random bytes.
///
/// Returns `None` if the bytes do not correspond to a valid Orchard spending key.

View File

@ -1,8 +1,9 @@
use group::GroupEncoding;
use pasta_curves::pallas;
use rand::RngCore;
use crate::{
keys::FullViewingKey,
keys::{FullViewingKey, SpendingKey},
spec::{prf_expand, to_base, to_scalar},
value::NoteValue,
Address,
@ -19,6 +20,12 @@ pub use self::nullifier::Nullifier;
struct RandomSeed([u8; 32]);
impl RandomSeed {
pub(crate) fn random(rng: &mut impl RngCore) -> Self {
let mut bytes = [0; 32];
rng.fill_bytes(&mut bytes);
RandomSeed(bytes)
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
@ -53,6 +60,25 @@ pub struct Note {
}
impl Note {
/// Generates a dummy spent note.
///
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
///
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
pub(crate) fn dummy(rng: &mut impl RngCore, rho: Option<Nullifier>) -> (FullViewingKey, Self) {
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
let recipient = fvk.default_address();
let note = Note {
recipient,
value: NoteValue::zero(),
rho: rho.unwrap_or_else(|| Nullifier::dummy(rng)),
rseed: RandomSeed::random(rng),
};
(fvk, note)
}
/// Derives the commitment to this note.
///
/// Defined in [Zcash Protocol Spec § 3.2: Notes][notes].

View File

@ -1,5 +1,7 @@
use group::Group;
use halo2::arithmetic::CurveExt;
use pasta_curves::pallas;
use rand::RngCore;
use super::NoteCommitment;
use crate::{
@ -12,6 +14,22 @@ use crate::{
pub struct Nullifier(pub(super) pallas::Base);
impl Nullifier {
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
///
/// Nullifiers are required by consensus to be unique. For dummy output notes, we get
/// this restriction as intended: the note's $\rho$ value is set to the nullifier of
/// the accompanying spent note within the action, which is constrained by consensus
/// to be unique. In the case of dummy spent notes, we get this restriction by
/// following the chain backwards: the nullifier of the dummy spent note will be
/// constrained by consensus to be unique, and the nullifier's uniqueness is derived
/// from the uniqueness of $\rho$.
///
/// Instead of explicitly sampling for a unique nullifier, we rely here on the size of
/// the base field to make the chance of sapling a colliding nullifier negligible.
pub(crate) fn dummy(rng: &mut impl RngCore) -> Self {
Nullifier(extract_p(&pallas::Point::random(rng)))
}
/// $DeriveNullifier$.
///
/// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers].

View File

@ -47,6 +47,11 @@ impl std::error::Error for OverflowError {}
pub struct NoteValue(u64);
impl NoteValue {
pub(crate) fn zero() -> Self {
// Default for u64 is zero.
Default::default()
}
pub(crate) fn to_le_bits(self) -> BitArray<Lsb0, [u8; 8]> {
BitArray::<Lsb0, _>::new(self.0.to_le_bytes())
}