Merge pull request #258 from therealyingtong/zip212-esk-plaintext-impl

ZIP212 implementation
This commit is contained in:
Daira Hopwood 2020-08-12 10:19:08 +01:00 committed by GitHub
commit 81c3b54b24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 903 additions and 427 deletions

View File

@ -1,5 +1,6 @@
use pairing::bls12_381::Bls12;
use zcash_primitives::{
consensus,
note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo},
primitives::{Note, PaymentAddress},
transaction::Transaction,
@ -30,7 +31,8 @@ pub struct DecryptedOutput {
/// Scans a [`Transaction`] for any information that can be decrypted by the set of
/// [`ExtendedFullViewingKey`]s.
pub fn decrypt_transaction(
pub fn decrypt_transaction<P: consensus::Parameters>(
height: u32,
tx: &Transaction,
extfvks: &[ExtendedFullViewingKey],
) -> Vec<DecryptedOutput> {
@ -49,21 +51,27 @@ pub fn decrypt_transaction(
};
for (account, (ivk, ovk)) in vks.iter().enumerate() {
let ((note, to, memo), outgoing) =
match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) {
Some(ret) => (ret, false),
None => match try_sapling_output_recovery(
ovk,
&output.cv,
&output.cmu,
&epk,
&output.enc_ciphertext,
&output.out_ciphertext,
) {
Some(ret) => (ret, true),
None => continue,
},
};
let ((note, to, memo), outgoing) = match try_sapling_note_decryption::<P>(
height,
ivk,
&epk,
&output.cmu,
&output.enc_ciphertext,
) {
Some(ret) => (ret, false),
None => match try_sapling_output_recovery::<P>(
height,
ovk,
&output.cv,
&output.cmu,
&epk,
&output.enc_ciphertext,
&output.out_ciphertext,
) {
Some(ret) => (ret, true),
None => continue,
},
};
decrypted.push(DecryptedOutput {
index,
note,

View File

@ -4,6 +4,7 @@ use ff::PrimeField;
use std::collections::HashSet;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_primitives::{
consensus,
jubjub::fs::Fs,
merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption,
@ -22,7 +23,8 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx};
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented
/// with this output's commitment.
fn scan_output(
fn scan_output<P: consensus::Parameters>(
height: u32,
(index, output): (usize, CompactOutput),
ivks: &[Fs],
spent_from_accounts: &HashSet<usize>,
@ -49,10 +51,11 @@ fn scan_output(
tree.append(node).unwrap();
for (account, ivk) in ivks.iter().enumerate() {
let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) {
Some(ret) => ret,
None => continue,
};
let (note, to) =
match try_sapling_compact_note_decryption::<P>(height, ivk, &epk, &cmu, &ct) {
Some(ret) => ret,
None => continue,
};
// A note is marked as "change" if the account that received it
// also spent notes in the same transaction. This will catch,
@ -83,7 +86,7 @@ fn scan_output(
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
/// incremented appropriately.
pub fn scan_block(
pub fn scan_block<P: consensus::Parameters>(
block: CompactBlock,
extfvks: &[ExtendedFullViewingKey],
nullifiers: &[(&[u8], usize)],
@ -150,7 +153,8 @@ pub fn scan_block(
.map(|output| &mut output.witness)
.collect();
if let Some(output) = scan_output(
if let Some(output) = scan_output::<P>(
block.height as u32,
to_scan,
&ivks,
&spent_from_accounts,
@ -187,11 +191,13 @@ mod tests {
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{OsRng, RngCore};
use zcash_primitives::{
consensus::TestNetwork,
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
merkle_tree::CommitmentTree,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::Note,
transaction::components::Amount,
util::generate_random_rseed,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
@ -249,11 +255,12 @@ mod tests {
// Create a fake Note for the account
let mut rng = OsRng;
let rseed = generate_random_rseed::<TestNetwork, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
@ -318,7 +325,7 @@ mod tests {
assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
let txs = scan_block::<TestNetwork>(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
@ -350,7 +357,7 @@ mod tests {
assert_eq!(cb.vtx.len(), 3);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
let txs = scan_block::<TestNetwork>(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
@ -378,7 +385,7 @@ mod tests {
assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []);
let txs = scan_block::<TestNetwork>(cb, &[], &[(&nf, account)], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];

View File

@ -33,12 +33,17 @@ use zcash_primitives::zip32::ExtendedFullViewingKey;
use zcash_client_backend::constants::mainnet::{
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS,
};
#[cfg(not(feature = "mainnet"))]
use zcash_client_backend::constants::testnet::{
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS,
};
#[cfg(feature = "mainnet")]
pub use zcash_primitives::consensus::MainNetwork as Network;
#[cfg(not(feature = "mainnet"))]
pub use zcash_primitives::consensus::TestNetwork as Network;
pub mod address;
pub mod chain;
pub mod error;
@ -89,7 +94,8 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error:
#[cfg(test)]
mod tests {
use ff::{Field, PrimeField};
use crate::Network;
use ff::PrimeField;
use pairing::bls12_381::Bls12;
use protobuf::Message;
use rand_core::{OsRng, RngCore};
@ -100,10 +106,10 @@ mod tests {
};
use zcash_primitives::{
block::BlockHash,
jubjub::fs::Fs,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::{Note, PaymentAddress},
transaction::components::Amount,
util::generate_random_rseed,
zip32::ExtendedFullViewingKey,
JUBJUB,
};
@ -120,11 +126,12 @@ mod tests {
// Create a fake Note for the account
let mut rng = OsRng;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
@ -168,6 +175,7 @@ mod tests {
value: Amount,
) -> CompactBlock {
let mut rng = OsRng;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
// Create a fake CompactBlock containing the note
let mut cspend = CompactSpend::new();
@ -184,7 +192,7 @@ mod tests {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
@ -208,11 +216,12 @@ mod tests {
// Create a fake Note for the change
ctx.outputs.push({
let change_addr = extfvk.default_address().unwrap().1;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: change_addr.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: change_addr.pk_d().clone(),
value: (in_value - value).into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,

View File

@ -2,7 +2,7 @@
use ff::PrimeField;
use protobuf::parse_from_bytes;
use rusqlite::{types::ToSql, Connection, NO_PARAMS};
use rusqlite::{types::ToSql, Connection, OptionalExtension, NO_PARAMS};
use std::path::Path;
use zcash_client_backend::{
decrypt_transaction, encoding::decode_extended_full_viewing_key,
@ -18,7 +18,7 @@ use zcash_primitives::{
use crate::{
address::RecipientAddress,
error::{Error, ErrorKind},
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT,
Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT,
};
struct CompactBlockRow {
@ -187,7 +187,7 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
let txs = {
let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect();
let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect();
scan_block(
scan_block::<Network>(
block,
&extfvks[..],
&nf_refs,
@ -269,7 +269,7 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
.collect();
for output in tx.shielded_outputs {
let rcm = output.note.r.to_repr();
let rcm = output.note.rcm().to_repr();
let nf = output.note.nf(
&extfvks[output.account].fvk.vk,
output.witness.position() as u64,
@ -372,7 +372,23 @@ pub fn decrypt_and_store_transaction<P: AsRef<Path>>(
.collect::<Result<Result<Option<_>, _>, _>>()??
.ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?;
let outputs = decrypt_transaction(tx, &extfvks);
// Height is block height for mined transactions, and the "mempool height" (chain height + 1) for mempool transactions.
let mut stmt_select_block = data.prepare("SELECT block FROM transactions WHERE txid = ?")?;
let height = match stmt_select_block
.query_row(&[tx.txid().0.to_vec()], |row| row.get(0))
.optional()?
{
Some(height) => height,
None => data
.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
row.get(0)
})
.optional()?
.map(|last_height: u32| last_height + 1)
.unwrap_or(SAPLING_ACTIVATION_HEIGHT as u32),
};
let outputs = decrypt_transaction::<Network>(height as u32, tx, &extfvks);
if outputs.is_empty() {
// Nothing to see here
@ -457,7 +473,7 @@ pub fn decrypt_and_store_transaction<P: AsRef<Path>>(
])?;
}
} else {
let rcm = output.note.r.to_repr();
let rcm = output.note.rcm().to_repr();
// Try updating an existing received note.
if stmt_update_received_note.execute(&[

View File

@ -9,11 +9,12 @@ use std::path::Path;
use zcash_client_backend::encoding::encode_extended_full_viewing_key;
use zcash_primitives::{
consensus,
consensus::{NetworkUpgrade, Parameters},
jubjub::fs::{Fs, FsRepr},
keys::OutgoingViewingKey,
merkle_tree::{IncrementalWitness, MerklePath},
note_encryption::Memo,
primitives::{Diversifier, Note},
primitives::{Diversifier, Note, Rseed},
prover::TxProver,
sapling::Node,
transaction::{
@ -27,7 +28,7 @@ use zcash_primitives::{
use crate::{
address::RecipientAddress,
error::{Error, ErrorKind},
get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
get_target_and_anchor_heights, Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
};
/// Describes a policy for which outgoing viewing key should be able to decrypt
@ -234,14 +235,22 @@ pub fn create_to_address<P: AsRef<Path>>(
let note_value: i64 = row.get(1)?;
let rcm = {
let rseed = {
let d: Vec<_> = row.get(2)?;
let tmp = FsRepr(
d[..]
.try_into()
.map_err(|_| Error(ErrorKind::InvalidNote))?,
);
Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?
if Network::is_nu_active(NetworkUpgrade::Canopy, height) {
let mut r = [0u8; 32];
r.copy_from_slice(&d[..]);
Rseed::AfterZip212(r)
} else {
let tmp = FsRepr(
d[..]
.try_into()
.map_err(|_| Error(ErrorKind::InvalidNote))?,
);
let r = Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?;
Rseed::BeforeZip212(r)
}
};
let from = extfvk
@ -249,7 +258,7 @@ pub fn create_to_address<P: AsRef<Path>>(
.vk
.to_payment_address(diversifier, &JUBJUB)
.unwrap();
let note = from.create_note(note_value as u64, rcm, &JUBJUB).unwrap();
let note = from.create_note(note_value as u64, rseed, &JUBJUB).unwrap();
let merkle_path = {
let d: Vec<_> = row.get(3)?;
@ -279,7 +288,7 @@ pub fn create_to_address<P: AsRef<Path>>(
}
// Create the transaction
let mut builder = Builder::new(height);
let mut builder = Builder::<Network, OsRng>::new(height);
for selected in notes {
builder.add_sapling_spend(
extsk.clone(),
@ -391,7 +400,7 @@ mod tests {
query::{get_balance, get_verified_balance},
scan::scan_cached_blocks,
tests::{fake_compact_block, insert_into_cache},
SAPLING_ACTIVATION_HEIGHT,
Network, SAPLING_ACTIVATION_HEIGHT,
};
fn test_prover() -> impl TxProver {
@ -813,7 +822,8 @@ mod tests {
.unwrap();
let output = &tx.shielded_outputs[output_index as usize];
try_sapling_output_recovery(
try_sapling_output_recovery::<Network>(
SAPLING_ACTIVATION_HEIGHT as u32,
&extfvk.fvk.ovk,
&output.cv,
&output.cmu,

View File

@ -111,6 +111,8 @@ const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
NetworkUpgrade::Canopy,
];
pub const ZIP212_GRACE_PERIOD: u32 = 32256;
/// A globally-unique identifier for a set of consensus rules within the Zcash chain.
///
/// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash

View File

@ -24,7 +24,7 @@ pub mod redjubjub;
pub mod sapling;
pub mod serialize;
pub mod transaction;
mod util;
pub mod util;
pub mod zip32;
#[cfg(test)]

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,14 @@ use crate::pedersen_hash::{pedersen_hash, Personalization};
use byteorder::{LittleEndian, WriteBytesExt};
use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder};
use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform};
use crate::keys::prf_expand;
use blake2s_simd::Params as Blake2sParams;
use rand_core::{CryptoRng, RngCore};
#[derive(Clone)]
pub struct ValueCommitment<E: JubjubEngine> {
pub value: u64,
@ -207,18 +211,29 @@ impl<E: JubjubEngine> PaymentAddress<E> {
pub fn create_note(
&self,
value: u64,
randomness: E::Fs,
randomness: Rseed<E::Fs>,
params: &E::Params,
) -> Option<Note<E>> {
self.g_d(params).map(|g_d| Note {
value,
r: randomness,
rseed: randomness,
g_d,
pk_d: self.pk_d.clone(),
})
}
}
/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212).
///
/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value.
/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive
/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`.
#[derive(Copy, Clone, Debug)]
pub enum Rseed<Fs> {
BeforeZip212(Fs),
AfterZip212([u8; 32]),
}
#[derive(Clone, Debug)]
pub struct Note<E: JubjubEngine> {
/// The value of the note
@ -227,8 +242,8 @@ pub struct Note<E: JubjubEngine> {
pub g_d: edwards::Point<E, PrimeOrder>,
/// The public key of the address, g_d^ivk
pub pk_d: edwards::Point<E, PrimeOrder>,
/// The commitment randomness
pub r: E::Fs,
/// rseed
pub rseed: Rseed<E::Fs>,
}
impl<E: JubjubEngine> PartialEq for Note<E> {
@ -236,7 +251,7 @@ impl<E: JubjubEngine> PartialEq for Note<E> {
self.value == other.value
&& self.g_d == other.g_d
&& self.pk_d == other.pk_d
&& self.r == other.r
&& self.rcm() == other.rcm()
}
}
@ -280,7 +295,7 @@ impl<E: JubjubEngine> Note<E> {
// Compute final commitment
params
.generator(FixedGenerators::NoteCommitmentRandomness)
.mul(self.r, params)
.mul(self.rcm(), params)
.add(&hash_of_contents, params)
}
@ -313,4 +328,35 @@ impl<E: JubjubEngine> Note<E> {
// commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).to_xy().0
}
pub fn rcm(&self) -> E::Fs {
match self.rseed {
Rseed::BeforeZip212(rcm) => rcm,
Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x04]).as_bytes()),
}
}
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> E::Fs {
match self.derive_esk() {
None => {
// create random 64 byte buffer
let mut buffer = [0u8; 64];
&rng.fill_bytes(&mut buffer);
// reduce to uniform value
E::Fs::to_uniform(&buffer[..])
}
Some(esk) => esk,
}
}
/// Returns the derived `esk` if this note was created after ZIP 212 activated.
pub fn derive_esk(&self) -> Option<E::Fs> {
match self.rseed {
Rseed::BeforeZip212(_) => None,
Rseed::AfterZip212(rseed) => {
Some(E::Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes()))
}
}
}
}

View File

@ -2,7 +2,7 @@
use crate::{
jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed},
};
use pairing::bls12_381::{Bls12, Fr};
@ -31,7 +31,7 @@ pub trait TxProver {
ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>,
diversifier: Diversifier,
rcm: Fs,
rseed: Rseed<Fs>,
ar: Fs,
value: u64,
anchor: Fr,
@ -78,7 +78,7 @@ pub(crate) mod mock {
use crate::{
jubjub::{edwards, fs::Fs, FixedGenerators, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, ValueCommitment},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
};
use crate::{
@ -104,7 +104,7 @@ pub(crate) mod mock {
_ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>,
_diversifier: Diversifier,
_rcm: Fs,
_rcm: Rseed<Fs>,
ar: Fs,
value: u64,
_anchor: Fr,

View File

@ -10,13 +10,14 @@ use pairing::bls12_381::{Bls12, Fr};
use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
use std::error;
use std::fmt;
use std::marker::PhantomData;
use crate::{
consensus,
keys::OutgoingViewingKey,
legacy::TransparentAddress,
merkle_tree::MerklePath,
note_encryption::{generate_esk, Memo, SaplingNoteEncryption},
note_encryption::{Memo, SaplingNoteEncryption},
prover::TxProver,
redjubjub::PrivateKey,
sapling::{spend_sig, Node},
@ -24,6 +25,7 @@ use crate::{
components::{amount::DEFAULT_FEE, Amount, OutputDescription, SpendDescription, TxOut},
signature_hash_data, Transaction, TransactionData, SIGHASH_ALL,
},
util::generate_random_rseed,
JUBJUB,
};
@ -86,7 +88,8 @@ pub struct SaplingOutput {
}
impl SaplingOutput {
pub fn new<R: RngCore + CryptoRng>(
pub fn new<R: RngCore + CryptoRng, P: consensus::Parameters>(
height: u32,
rng: &mut R,
ovk: OutgoingViewingKey,
to: PaymentAddress<Bls12>,
@ -101,13 +104,13 @@ impl SaplingOutput {
return Err(Error::InvalidAmount);
}
let rcm = Fs::random(rng);
let rseed = generate_random_rseed::<P, R>(height, rng);
let note = Note {
g_d,
pk_d: to.pk_d().clone(),
value: value.into(),
r: rcm,
rseed,
};
Ok(SaplingOutput {
@ -136,7 +139,7 @@ impl SaplingOutput {
ctx,
encryptor.esk().clone(),
self.to,
self.note.r,
self.note.rcm(),
self.note.value,
);
@ -302,8 +305,9 @@ impl TransactionMetadata {
}
/// Generates a [`Transaction`] from its inputs and outputs.
pub struct Builder<R: RngCore + CryptoRng> {
pub struct Builder<P: consensus::Parameters, R: RngCore + CryptoRng> {
rng: R,
height: u32,
mtx: TransactionData,
fee: Amount,
anchor: Option<Fr>,
@ -311,9 +315,10 @@ pub struct Builder<R: RngCore + CryptoRng> {
outputs: Vec<SaplingOutput>,
transparent_inputs: TransparentInputs,
change_address: Option<(OutgoingViewingKey, PaymentAddress<Bls12>)>,
phantom: PhantomData<P>,
}
impl Builder<OsRng> {
impl<P: consensus::Parameters> Builder<P, OsRng> {
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
/// using default values for general transaction fields and the default OS random.
///
@ -328,7 +333,7 @@ impl Builder<OsRng> {
}
}
impl<R: RngCore + CryptoRng> Builder<R> {
impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
/// Creates a new `Builder` targeted for inclusion in the block with the given height
/// and randomness source, using default values for general transaction fields.
///
@ -338,12 +343,13 @@ impl<R: RngCore + CryptoRng> Builder<R> {
/// expiry delta (20 blocks).
///
/// The fee will be set to the default fee (0.0001 ZEC).
pub fn new_with_rng(height: u32, rng: R) -> Builder<R> {
pub fn new_with_rng(height: u32, rng: R) -> Builder<P, R> {
let mut mtx = TransactionData::new();
mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA;
Builder {
rng,
height,
mtx,
fee: DEFAULT_FEE,
anchor: None,
@ -351,6 +357,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
outputs: vec![],
transparent_inputs: TransparentInputs::default(),
change_address: None,
phantom: PhantomData,
}
}
@ -399,7 +406,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
value: Amount,
memo: Option<Memo>,
) -> Result<(), Error> {
let output = SaplingOutput::new(&mut self.rng, ovk, to, value, memo)?;
let output = SaplingOutput::new::<R, P>(self.height, &mut self.rng, ovk, to, value, memo)?;
self.mtx.value_balance -= value;
@ -555,7 +562,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
&mut ctx,
proof_generation_key,
spend.diversifier,
spend.note.r,
spend.note.rseed,
spend.alpha,
spend.note.value,
anchor,
@ -610,22 +617,29 @@ impl<R: RngCore + CryptoRng> Builder<R> {
}
};
let rseed = generate_random_rseed::<P, R>(self.height, &mut self.rng);
(
payment_address,
Note {
g_d,
pk_d,
r: Fs::random(&mut self.rng),
rseed,
value: 0,
},
)
};
let esk = generate_esk(&mut self.rng);
let esk = dummy_note.generate_or_derive_esk(&mut self.rng);
let epk = dummy_note.g_d.mul(esk, &JUBJUB);
let (zkproof, cv) =
prover.output_proof(&mut ctx, esk, dummy_to, dummy_note.r, dummy_note.value);
let (zkproof, cv) = prover.output_proof(
&mut ctx,
esk,
dummy_to,
dummy_note.rcm(),
dummy_note.value,
);
let cmu = dummy_note.cm(&JUBJUB);
@ -696,14 +710,17 @@ impl<R: RngCore + CryptoRng> Builder<R> {
mod tests {
use ff::{Field, PrimeField};
use rand_core::OsRng;
use std::marker::PhantomData;
use crate::jubjub::fs::Fs;
use super::{Builder, Error};
use crate::{
consensus,
consensus::TestNetwork,
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::Rseed,
prover::mock::MockTxProver,
sapling::Node,
transaction::components::Amount,
@ -718,7 +735,7 @@ mod tests {
let ovk = extfvk.fvk.ovk;
let to = extfvk.default_address().unwrap().1;
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
assert_eq!(
builder.add_sapling_output(ovk, to, Amount::from_i64(-1).unwrap(), None),
Err(Error::InvalidAmount)
@ -727,14 +744,19 @@ mod tests {
#[test]
fn binding_sig_absent_if_no_shielded_spend_or_output() {
use crate::consensus::{NetworkUpgrade, Parameters};
use crate::transaction::{
builder::{self, TransparentInputs},
TransactionData,
};
let sapling_activation_height =
TestNetwork::activation_height(NetworkUpgrade::Sapling).unwrap();
// Create a builder with 0 fee, so we can construct t outputs
let mut builder = builder::Builder {
let mut builder = builder::Builder::<TestNetwork, OsRng> {
rng: OsRng,
height: sapling_activation_height,
mtx: TransactionData::new(),
fee: Amount::zero(),
anchor: None,
@ -742,6 +764,7 @@ mod tests {
outputs: vec![],
transparent_inputs: TransparentInputs::default(),
change_address: None,
phantom: PhantomData,
};
// Create a tx with only t output. No binding_sig should be present
@ -765,14 +788,14 @@ mod tests {
let mut rng = OsRng;
let note1 = to
.create_note(50000, Fs::random(&mut rng), &JUBJUB)
.create_note(50000, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB)
.unwrap();
let cm1 = Node::new(note1.cm(&JUBJUB).to_repr());
let mut tree = CommitmentTree::new();
tree.append(cm1).unwrap();
let witness1 = IncrementalWitness::from_tree(&tree);
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
// Create a tx with a sapling spend. binding_sig should be present
builder
@ -798,7 +821,7 @@ mod tests {
#[test]
fn fails_on_negative_transparent_output() {
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
assert_eq!(
builder.add_transparent_output(
&TransparentAddress::PublicKey([0; 20]),
@ -818,7 +841,7 @@ mod tests {
// Fails with no inputs or outputs
// 0.0001 t-ZEC fee
{
let builder = Builder::new(0);
let builder = Builder::<TestNetwork, OsRng>::new(0);
assert_eq!(
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap()))
@ -832,7 +855,7 @@ mod tests {
// Fail if there is only a Sapling output
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
{
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
builder
.add_sapling_output(
ovk.clone(),
@ -850,7 +873,7 @@ mod tests {
// Fail if there is only a transparent output
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee
{
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
builder
.add_transparent_output(
&TransparentAddress::PublicKey([0; 20]),
@ -864,7 +887,7 @@ mod tests {
}
let note1 = to
.create_note(59999, Fs::random(&mut rng), &JUBJUB)
.create_note(59999, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB)
.unwrap();
let cm1 = Node::new(note1.cm(&JUBJUB).to_repr());
let mut tree = CommitmentTree::new();
@ -874,7 +897,7 @@ mod tests {
// Fail if there is insufficient input
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in
{
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
builder
.add_sapling_spend(
extsk.clone(),
@ -903,7 +926,9 @@ mod tests {
);
}
let note2 = to.create_note(1, Fs::random(&mut rng), &JUBJUB).unwrap();
let note2 = to
.create_note(1, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB)
.unwrap();
let cm2 = Node::new(note2.cm(&JUBJUB).to_repr());
tree.append(cm2).unwrap();
witness1.append(cm2).unwrap();
@ -915,7 +940,7 @@ mod tests {
// (Still fails because we are using a MockTxProver which doesn't correctly
// compute bindingSig.)
{
let mut builder = Builder::new(0);
let mut builder = Builder::<TestNetwork, OsRng>::new(0);
builder
.add_sapling_spend(
extsk.clone(),

View File

@ -1,6 +1,13 @@
use blake2b_simd::Params;
use crate::jubjub::{JubjubEngine, ToUniform};
use crate::{
consensus,
consensus::NetworkUpgrade,
jubjub::{fs::Fs, JubjubEngine, ToUniform},
primitives::Rseed,
};
use ff::Field;
use rand_core::{CryptoRng, RngCore};
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
@ -9,3 +16,16 @@ pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E:
let ret = hasher.finalize();
E::Fs::to_uniform(ret.as_ref())
}
pub fn generate_random_rseed<P: consensus::Parameters, R: RngCore + CryptoRng>(
height: u32,
rng: &mut R,
) -> Rseed<Fs> {
if P::is_nu_active(NetworkUpgrade::Canopy, height) {
let mut buffer = [0u8; 32];
&rng.fill_bytes(&mut buffer);
Rseed::AfterZip212(buffer)
} else {
Rseed::BeforeZip212(Fs::random(rng))
}
}

View File

@ -544,7 +544,7 @@ fn test_input_circuit_with_bls12_381() {
use zcash_primitives::{
jubjub::{edwards, fs, JubjubBls12},
pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey},
primitives::{Diversifier, Note, ProofGenerationKey, Rseed},
};
let params = &JubjubBls12::new();
@ -598,7 +598,7 @@ fn test_input_circuit_with_bls12_381() {
value: value_commitment.value,
g_d: g_d.clone(),
pk_d: payment_address.pk_d().clone(),
r: commitment_randomness.clone(),
rseed: Rseed::BeforeZip212(commitment_randomness.clone()),
};
let mut position = 0u64;
@ -694,7 +694,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
use zcash_primitives::{
jubjub::{edwards, fs, JubjubBls12},
pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey},
primitives::{Diversifier, Note, ProofGenerationKey, Rseed},
};
let params = &JubjubBls12::new();
@ -782,7 +782,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
value: value_commitment.value,
g_d: g_d.clone(),
pk_d: payment_address.pk_d().clone(),
r: commitment_randomness.clone(),
rseed: Rseed::BeforeZip212(commitment_randomness.clone()),
};
let mut position = 0u64;
@ -877,7 +877,7 @@ fn test_output_circuit_with_bls12_381() {
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
jubjub::{edwards, fs, JubjubBls12},
primitives::{Diversifier, ProofGenerationKey},
primitives::{Diversifier, ProofGenerationKey, Rseed},
};
let params = &JubjubBls12::new();
@ -941,7 +941,11 @@ fn test_output_circuit_with_bls12_381() {
);
let expected_cm = payment_address
.create_note(value_commitment.value, commitment_randomness, params)
.create_note(
value_commitment.value,
Rseed::BeforeZip212(commitment_randomness),
params,
)
.expect("should be valid")
.cm(params);

View File

@ -4,7 +4,7 @@ use bellman::groth16::{Parameters, PreparedVerifyingKey};
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::{
jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed},
};
use zcash_primitives::{
merkle_tree::MerklePath,
@ -132,7 +132,7 @@ impl TxProver for LocalTxProver {
ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>,
diversifier: Diversifier,
rcm: Fs,
rseed: Rseed<Fs>,
ar: Fs,
value: u64,
anchor: Fr,
@ -148,7 +148,7 @@ impl TxProver for LocalTxProver {
let (proof, cv, rk) = ctx.spend_proof(
proof_generation_key,
diversifier,
rcm,
rseed,
ar,
value,
anchor,

View File

@ -8,7 +8,7 @@ use rand_core::OsRng;
use std::ops::{AddAssign, Neg};
use zcash_primitives::{
jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown},
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment},
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
};
use zcash_primitives::{
merkle_tree::MerklePath,
@ -43,7 +43,7 @@ impl SaplingProvingContext {
&mut self,
proof_generation_key: ProofGenerationKey<Bls12>,
diversifier: Diversifier,
rcm: Fs,
rseed: Rseed<Fs>,
ar: Fs,
value: u64,
anchor: Fr,
@ -102,7 +102,7 @@ impl SaplingProvingContext {
.g_d::<Bls12>(params)
.expect("was a valid diversifier before"),
pk_d: payment_address.pk_d().clone(),
r: rcm,
rseed,
};
let nullifier = note.nf(&viewing_key, merkle_path.position, params);
@ -113,7 +113,7 @@ impl SaplingProvingContext {
value_commitment: Some(value_commitment.clone()),
proof_generation_key: Some(proof_generation_key),
payment_address: Some(payment_address),
commitment_randomness: Some(rcm),
commitment_randomness: Some(note.rcm()),
ar: Some(ar),
auth_path: merkle_path
.auth_path