Pass height to methods which encrypt or decrypt Sapling outputs

This commit is contained in:
therealyingtong 2020-07-30 13:13:59 +08:00
parent 71d31abad6
commit b537f0f712
No known key found for this signature in database
GPG Key ID: 179F32A1503D607E
6 changed files with 106 additions and 41 deletions

View File

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

View File

@ -4,6 +4,7 @@ use ff::PrimeField;
use std::collections::HashSet; use std::collections::HashSet;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_primitives::{ use zcash_primitives::{
consensus,
jubjub::fs::Fs, jubjub::fs::Fs,
merkle_tree::{CommitmentTree, IncrementalWitness}, merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption, note_encryption::try_sapling_compact_note_decryption,
@ -22,7 +23,9 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx};
/// ///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented
/// with this output's commitment. /// with this output's commitment.
fn scan_output( fn scan_output<P: consensus::Parameters>(
parameters: &P,
height: u32,
(index, output): (usize, CompactOutput), (index, output): (usize, CompactOutput),
ivks: &[Fs], ivks: &[Fs],
spent_from_accounts: &HashSet<usize>, spent_from_accounts: &HashSet<usize>,
@ -49,10 +52,11 @@ fn scan_output(
tree.append(node).unwrap(); tree.append(node).unwrap();
for (account, ivk) in ivks.iter().enumerate() { for (account, ivk) in ivks.iter().enumerate() {
let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) { let (note, to) =
Some(ret) => ret, match try_sapling_compact_note_decryption(parameters, height, ivk, &epk, &cmu, &ct) {
None => continue, Some(ret) => ret,
}; None => continue,
};
// A note is marked as "change" if the account that received it // A note is marked as "change" if the account that received it
// also spent notes in the same transaction. This will catch, // also spent notes in the same transaction. This will catch,
@ -83,7 +87,8 @@ fn scan_output(
/// ///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
/// incremented appropriately. /// incremented appropriately.
pub fn scan_block( pub fn scan_block<P: consensus::Parameters>(
parameters: &P,
block: CompactBlock, block: CompactBlock,
extfvks: &[ExtendedFullViewingKey], extfvks: &[ExtendedFullViewingKey],
nullifiers: &[(&[u8], usize)], nullifiers: &[(&[u8], usize)],
@ -151,6 +156,8 @@ pub fn scan_block(
.collect(); .collect();
if let Some(output) = scan_output( if let Some(output) = scan_output(
parameters,
block.height as u32,
to_scan, to_scan,
&ivks, &ivks,
&spent_from_accounts, &spent_from_accounts,
@ -187,6 +194,7 @@ mod tests {
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
use zcash_primitives::{ use zcash_primitives::{
consensus,
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
merkle_tree::CommitmentTree, merkle_tree::CommitmentTree,
note_encryption::{Memo, SaplingNoteEncryption}, note_encryption::{Memo, SaplingNoteEncryption},
@ -318,7 +326,14 @@ mod tests {
assert_eq!(cb.vtx.len(), 2); assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new(); let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []); let txs = scan_block(
&consensus::MainNetwork,
cb,
&[extfvk],
&[],
&mut tree,
&mut [],
);
assert_eq!(txs.len(), 1); assert_eq!(txs.len(), 1);
let tx = &txs[0]; let tx = &txs[0];
@ -350,7 +365,14 @@ mod tests {
assert_eq!(cb.vtx.len(), 3); assert_eq!(cb.vtx.len(), 3);
let mut tree = CommitmentTree::new(); let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []); let txs = scan_block(
&consensus::MainNetwork,
cb,
&[extfvk],
&[],
&mut tree,
&mut [],
);
assert_eq!(txs.len(), 1); assert_eq!(txs.len(), 1);
let tx = &txs[0]; let tx = &txs[0];
@ -378,7 +400,14 @@ mod tests {
assert_eq!(cb.vtx.len(), 2); assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new(); let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []); let txs = scan_block(
&consensus::MainNetwork,
cb,
&[],
&[(&nf, account)],
&mut tree,
&mut [],
);
assert_eq!(txs.len(), 1); assert_eq!(txs.len(), 1);
let tx = &txs[0]; let tx = &txs[0];

View File

@ -9,6 +9,7 @@ use zcash_client_backend::{
proto::compact_formats::CompactBlock, welding_rig::scan_block, proto::compact_formats::CompactBlock, welding_rig::scan_block,
}; };
use zcash_primitives::{ use zcash_primitives::{
consensus,
merkle_tree::{CommitmentTree, IncrementalWitness}, merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::Node, sapling::Node,
transaction::Transaction, transaction::Transaction,
@ -188,6 +189,7 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect(); 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(); let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect();
scan_block( scan_block(
&consensus::MainNetwork,
block, block,
&extfvks[..], &extfvks[..],
&nf_refs, &nf_refs,
@ -372,7 +374,7 @@ pub fn decrypt_and_store_transaction<P: AsRef<Path>>(
.collect::<Result<Result<Option<_>, _>, _>>()?? .collect::<Result<Result<Option<_>, _>, _>>()??
.ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?;
let outputs = decrypt_transaction(tx, &extfvks); let outputs = decrypt_transaction(&consensus::MainNetwork, tx, &extfvks);
if outputs.is_empty() { if outputs.is_empty() {
// Nothing to see here // Nothing to see here

View File

@ -5,10 +5,10 @@ use std::fmt;
/// Zcash consensus parameters. /// Zcash consensus parameters.
pub trait Parameters { pub trait Parameters {
fn activation_height(nu: NetworkUpgrade) -> Option<u32>; fn activation_height(&self, nu: NetworkUpgrade) -> Option<u32>;
fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool { fn is_nu_active(&self, nu: NetworkUpgrade, height: u32) -> bool {
match Self::activation_height(nu) { match self.activation_height(nu) {
Some(h) if h <= height => true, Some(h) if h <= height => true,
_ => false, _ => false,
} }
@ -20,7 +20,7 @@ pub trait Parameters {
pub struct MainNetwork; pub struct MainNetwork;
impl Parameters for MainNetwork { impl Parameters for MainNetwork {
fn activation_height(nu: NetworkUpgrade) -> Option<u32> { fn activation_height(&self, nu: NetworkUpgrade) -> Option<u32> {
match nu { match nu {
NetworkUpgrade::Overwinter => Some(347_500), NetworkUpgrade::Overwinter => Some(347_500),
NetworkUpgrade::Sapling => Some(419_200), NetworkUpgrade::Sapling => Some(419_200),
@ -36,7 +36,7 @@ impl Parameters for MainNetwork {
pub struct TestNetwork; pub struct TestNetwork;
impl Parameters for TestNetwork { impl Parameters for TestNetwork {
fn activation_height(nu: NetworkUpgrade) -> Option<u32> { fn activation_height(&self, nu: NetworkUpgrade) -> Option<u32> {
match nu { match nu {
NetworkUpgrade::Overwinter => Some(207_500), NetworkUpgrade::Overwinter => Some(207_500),
NetworkUpgrade::Sapling => Some(280_000), NetworkUpgrade::Sapling => Some(280_000),
@ -174,9 +174,9 @@ impl BranchId {
/// the given height. /// the given height.
/// ///
/// This is the branch ID that should be used when creating transactions. /// This is the branch ID that should be used when creating transactions.
pub fn for_height<C: Parameters>(height: u32) -> Self { pub fn for_height<C: Parameters>(parameters: C, height: u32) -> Self {
for nu in UPGRADES_IN_ORDER.iter().rev() { for nu in UPGRADES_IN_ORDER.iter().rev() {
if C::is_nu_active(*nu, height) { if parameters.is_nu_active(*nu, height) {
return nu.branch_id(); return nu.branch_id();
} }
} }

View File

@ -1,6 +1,8 @@
//! Implementation of in-band secret distribution for Zcash transactions. //! Implementation of in-band secret distribution for Zcash transactions.
use crate::{ use crate::{
consensus,
consensus::NetworkUpgrade,
jubjub::{ jubjub::{
edwards, edwards,
fs::{Fs, FsRepr}, fs::{Fs, FsRepr},
@ -335,7 +337,9 @@ impl SaplingNoteEncryption {
} }
} }
fn parse_note_plaintext_without_memo( fn parse_note_plaintext_without_memo<P: consensus::Parameters>(
parameters: &P,
height: u32,
ivk: &Fs, ivk: &Fs,
cmu: &Fr, cmu: &Fr,
plaintext: &[u8], plaintext: &[u8],
@ -380,7 +384,9 @@ fn parse_note_plaintext_without_memo(
/// `PaymentAddress` to which the note was sent. /// `PaymentAddress` to which the note was sent.
/// ///
/// Implements section 4.17.2 of the Zcash Protocol Specification. /// Implements section 4.17.2 of the Zcash Protocol Specification.
pub fn try_sapling_note_decryption( pub fn try_sapling_note_decryption<P: consensus::Parameters>(
parameters: &P,
height: u32,
ivk: &Fs, ivk: &Fs,
epk: &edwards::Point<Bls12, PrimeOrder>, epk: &edwards::Point<Bls12, PrimeOrder>,
cmu: &Fr, cmu: &Fr,
@ -405,7 +411,7 @@ pub fn try_sapling_note_decryption(
NOTE_PLAINTEXT_SIZE NOTE_PLAINTEXT_SIZE
); );
let (note, to) = parse_note_plaintext_without_memo(ivk, cmu, &plaintext)?; let (note, to) = parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext)?;
let mut memo = [0u8; 512]; let mut memo = [0u8; 512];
memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]);
@ -422,7 +428,9 @@ pub fn try_sapling_note_decryption(
/// Implements the procedure specified in [`ZIP 307`]. /// Implements the procedure specified in [`ZIP 307`].
/// ///
/// [`ZIP 307`]: https://github.com/zcash/zips/pull/226 /// [`ZIP 307`]: https://github.com/zcash/zips/pull/226
pub fn try_sapling_compact_note_decryption( pub fn try_sapling_compact_note_decryption<P: consensus::Parameters>(
parameters: &P,
height: u32,
ivk: &Fs, ivk: &Fs,
epk: &edwards::Point<Bls12, PrimeOrder>, epk: &edwards::Point<Bls12, PrimeOrder>,
cmu: &Fr, cmu: &Fr,
@ -438,7 +446,7 @@ pub fn try_sapling_compact_note_decryption(
plaintext.copy_from_slice(&enc_ciphertext); plaintext.copy_from_slice(&enc_ciphertext);
ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext); ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext);
parse_note_plaintext_without_memo(ivk, cmu, &plaintext) parse_note_plaintext_without_memo(parameters, height, ivk, cmu, &plaintext)
} }
/// Recovery of the full note plaintext by the sender. /// Recovery of the full note plaintext by the sender.
@ -448,7 +456,9 @@ pub fn try_sapling_compact_note_decryption(
/// `PaymentAddress` to which the note was sent. /// `PaymentAddress` to which the note was sent.
/// ///
/// Implements section 4.17.3 of the Zcash Protocol Specification. /// Implements section 4.17.3 of the Zcash Protocol Specification.
pub fn try_sapling_output_recovery( pub fn try_sapling_output_recovery<P: consensus::Parameters>(
parameters: &P,
height: u32,
ovk: &OutgoingViewingKey, ovk: &OutgoingViewingKey,
cv: &edwards::Point<Bls12, Unknown>, cv: &edwards::Point<Bls12, Unknown>,
cmu: &Fr, cmu: &Fr,
@ -717,6 +727,7 @@ mod tests {
} }
fn random_enc_ciphertext_with<R: RngCore + CryptoRng>( fn random_enc_ciphertext_with<R: RngCore + CryptoRng>(
height: u32,
ivk: Fs, ivk: Fs,
mut rng: &mut R, mut rng: &mut R,
) -> ( ) -> (

View File

@ -13,6 +13,7 @@ use std::fmt;
use crate::{ use crate::{
consensus, consensus,
consensus::NetworkUpgrade,
keys::OutgoingViewingKey, keys::OutgoingViewingKey,
legacy::TransparentAddress, legacy::TransparentAddress,
merkle_tree::MerklePath, merkle_tree::MerklePath,
@ -86,7 +87,9 @@ pub struct SaplingOutput {
} }
impl SaplingOutput { impl SaplingOutput {
pub fn new<R: RngCore + CryptoRng>( pub fn new<R: RngCore + CryptoRng, P: consensus::Parameters>(
parameters: P,
height: u32,
rng: &mut R, rng: &mut R,
ovk: OutgoingViewingKey, ovk: OutgoingViewingKey,
to: PaymentAddress<Bls12>, to: PaymentAddress<Bls12>,
@ -304,6 +307,7 @@ impl TransactionMetadata {
/// Generates a [`Transaction`] from its inputs and outputs. /// Generates a [`Transaction`] from its inputs and outputs.
pub struct Builder<R: RngCore + CryptoRng> { pub struct Builder<R: RngCore + CryptoRng> {
rng: R, rng: R,
height: u32,
mtx: TransactionData, mtx: TransactionData,
fee: Amount, fee: Amount,
anchor: Option<Fr>, anchor: Option<Fr>,
@ -344,6 +348,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
Builder { Builder {
rng, rng,
height,
mtx, mtx,
fee: DEFAULT_FEE, fee: DEFAULT_FEE,
anchor: None, anchor: None,
@ -399,7 +404,15 @@ impl<R: RngCore + CryptoRng> Builder<R> {
value: Amount, value: Amount,
memo: Option<Memo>, memo: Option<Memo>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let output = SaplingOutput::new(&mut self.rng, ovk, to, value, memo)?; let output = SaplingOutput::new(
consensus::MainNetwork,
self.height,
&mut self.rng,
ovk,
to,
value,
memo,
)?;
self.mtx.value_balance -= value; self.mtx.value_balance -= value;