zcash_script/depend/zcash/src/rust/src/sapling/spec.rs

230 lines
6.9 KiB
Rust

use std::convert::TryInto;
use blake2s_simd::Params as Blake2sParams;
use group::{cofactor::CofactorGroup, GroupEncoding};
use incrementalmerkletree::Hashable;
use rand_core::{OsRng, RngCore};
use zcash_primitives::{
constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
merkle_tree::HashSer,
sapling::{
merkle_hash,
note::{ExtractedNoteCommitment, NoteCommitment},
value::NoteValue,
Diversifier, Node, Note, NullifierDerivingKey, PaymentAddress, Rseed,
},
};
use crate::de_ct;
#[cxx::bridge]
mod ffi {
#[namespace = "sapling::spec"]
extern "Rust" {
fn tree_uncommitted() -> [u8; 32];
fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32];
fn to_scalar(input: &[u8; 64]) -> [u8; 32];
fn ask_to_ak(ask: &[u8; 32]) -> [u8; 32];
fn nsk_to_nk(nsk: &[u8; 32]) -> [u8; 32];
fn crh_ivk(ak: &[u8; 32], nk: &[u8; 32]) -> [u8; 32];
fn check_diversifier(diversifier: [u8; 11]) -> bool;
fn ivk_to_pkd(ivk: &[u8; 32], diversifier: [u8; 11]) -> Result<[u8; 32]>;
fn generate_r() -> [u8; 32];
fn compute_nf(
diversifier: &[u8; 11],
pk_d: &[u8; 32],
value: u64,
rcm: &[u8; 32],
nk: &[u8; 32],
position: u64,
) -> Result<[u8; 32]>;
fn compute_cmu(
diversifier: [u8; 11],
pk_d: &[u8; 32],
value: u64,
rcm: &[u8; 32],
) -> Result<[u8; 32]>;
}
}
/// Reads an FsRepr from a [u8; 32]
/// and multiplies it by the given base.
fn fixed_scalar_mult(from: &[u8; 32], p_g: &jubjub::SubgroupPoint) -> jubjub::SubgroupPoint {
// We only call this with `from` being a valid jubjub::Scalar.
let f = jubjub::Scalar::from_bytes(from).unwrap();
p_g * f
}
/// Writes the "uncommitted" note value for empty leaves of the Merkle tree.
///
/// `result` must be a valid pointer to 32 bytes which will be written.
fn tree_uncommitted() -> [u8; 32] {
// Should be okay, caller is responsible for ensuring the pointer
// is a valid pointer to 32 bytes that can be mutated.
let mut result = [0; 32];
Node::empty_leaf()
.write(&mut result[..])
.expect("Sapling leaves are 32 bytes");
result
}
fn to_scalar(input: &[u8; 64]) -> [u8; 32] {
jubjub::Scalar::from_bytes_wide(input).to_bytes()
}
pub(crate) fn ask_to_ak(ask: &[u8; 32]) -> [u8; 32] {
let ak = fixed_scalar_mult(ask, &SPENDING_KEY_GENERATOR);
ak.to_bytes()
}
pub(crate) fn nsk_to_nk(nsk: &[u8; 32]) -> [u8; 32] {
let nk = fixed_scalar_mult(nsk, &PROOF_GENERATION_KEY_GENERATOR);
nk.to_bytes()
}
pub(crate) fn crh_ivk(ak: &[u8; 32], nk: &[u8; 32]) -> [u8; 32] {
let mut h = Blake2sParams::new()
.hash_length(32)
.personal(CRH_IVK_PERSONALIZATION)
.to_state();
h.update(ak);
h.update(nk);
let mut h: [u8; 32] = h.finalize().as_ref().try_into().unwrap();
// Drop the last five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111;
h
}
pub(crate) fn check_diversifier(diversifier: [u8; 11]) -> bool {
let diversifier = Diversifier(diversifier);
diversifier.g_d().is_some()
}
pub(crate) fn ivk_to_pkd(ivk: &[u8; 32], diversifier: [u8; 11]) -> Result<[u8; 32], String> {
let ivk = de_ct(jubjub::Scalar::from_bytes(ivk));
let diversifier = Diversifier(diversifier);
if let (Some(ivk), Some(g_d)) = (ivk, diversifier.g_d()) {
let pk_d = g_d * ivk;
Ok(pk_d.to_bytes())
} else {
Err("Diversifier is invalid".to_owned())
}
}
/// Test generation of commitment randomness
#[test]
fn test_gen_r() {
// Verify different r values are generated
let r1 = generate_r();
let r2 = generate_r();
assert_ne!(r1, r2);
// Verify r values are valid in the field
let _ = jubjub::Scalar::from_bytes(&r1).unwrap();
let _ = jubjub::Scalar::from_bytes(&r2).unwrap();
}
/// Generates a uniformly random scalar in Jubjub.
fn generate_r() -> [u8; 32] {
// create random 64 byte buffer
let mut rng = OsRng;
let mut buffer = [0u8; 64];
rng.fill_bytes(&mut buffer);
// reduce to uniform value
let r = jubjub::Scalar::from_bytes_wide(&buffer);
r.to_bytes()
}
// Private utility function to get Note from C parameters
fn priv_get_note(
diversifier: &[u8; 11],
pk_d: &[u8; 32],
value: u64,
rcm: &[u8; 32],
) -> Result<Note, String> {
let recipient_bytes = {
let mut tmp = [0; 43];
tmp[..11].copy_from_slice(&diversifier[..]);
tmp[11..].copy_from_slice(&pk_d[..]);
tmp
};
let recipient = PaymentAddress::from_bytes(&recipient_bytes)
.ok_or_else(|| "Invalid recipient encoding".to_string())?;
// Deserialize randomness
// If this is after ZIP 212, the caller has calculated rcm, and we don't need to call
// Note::derive_esk, so we just pretend the note was using this rcm all along.
let rseed = Rseed::BeforeZip212(
de_ct(jubjub::Scalar::from_bytes(rcm)).ok_or_else(|| "Invalid rcm encoding".to_string())?,
);
Ok(Note::from_parts(
recipient,
NoteValue::from_raw(value),
rseed,
))
}
/// Computes a Sapling nullifier.
pub(crate) fn compute_nf(
diversifier: &[u8; 11],
pk_d: &[u8; 32],
value: u64,
rcm: &[u8; 32],
nk: &[u8; 32],
position: u64,
) -> Result<[u8; 32], String> {
// ZIP 216: Nullifier derivation is not consensus-critical
// (nullifiers are revealed, not calculated by consensus).
// In any case, ZIP 216 is now enabled retroactively.
let note = priv_get_note(diversifier, pk_d, value, rcm)?;
let nk = match de_ct(jubjub::ExtendedPoint::from_bytes(nk)) {
Some(p) => p,
None => return Err("Invalid nk encoding".to_owned()),
};
let nk = match de_ct(nk.into_subgroup()) {
Some(nk) => NullifierDerivingKey(nk),
None => return Err("nk is not in the prime-order subgroup".to_owned()),
};
let nf = note.nf(&nk, position);
Ok(nf.0)
}
/// Computes a Sapling commitment.
pub(crate) fn compute_cmu(
diversifier: [u8; 11],
pk_d: &[u8; 32],
value: u64,
rcm: &[u8; 32],
) -> Result<[u8; 32], String> {
let diversifier = Diversifier(diversifier);
let g_d = diversifier
.g_d()
.ok_or_else(|| "Invalid diversifier".to_string())?;
let pk_d =
de_ct(jubjub::ExtendedPoint::from_bytes(pk_d)).ok_or_else(|| "Invalid pk_d".to_string())?;
let pk_d = de_ct(pk_d.into_subgroup())
.ok_or_else(|| "pk_d is not in the prime-order subgroup".to_string())?;
let rcm = de_ct(jubjub::Scalar::from_bytes(rcm)).ok_or_else(|| "Invalid rcm".to_string())?;
let cmu = ExtractedNoteCommitment::from(NoteCommitment::temporary_zcashd_derive(
g_d.to_bytes(),
pk_d.to_bytes(),
NoteValue::from_raw(value),
rcm,
));
Ok(cmu.to_bytes())
}