From efaba87681bb382fd3db20a9c026b9db92342541 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 5 Aug 2020 14:08:58 -0600 Subject: [PATCH 1/7] Add newtype for block height. --- zcash_client_backend/src/decrypt.rs | 4 +- zcash_client_backend/src/proto/mod.rs | 15 +- zcash_client_backend/src/welding_rig.rs | 20 +-- zcash_client_sqlite/src/chain.rs | 38 +++-- zcash_client_sqlite/src/error.rs | 9 +- zcash_client_sqlite/src/lib.rs | 32 ++-- zcash_client_sqlite/src/query.rs | 2 +- zcash_client_sqlite/src/scan.rs | 62 ++++--- zcash_client_sqlite/src/transact.rs | 13 +- zcash_primitives/src/consensus.rs | 172 +++++++++++++++++--- zcash_primitives/src/note_encryption.rs | 23 +-- zcash_primitives/src/transaction/builder.rs | 28 ++-- zcash_primitives/src/transaction/mod.rs | 15 +- zcash_primitives/src/transaction/sighash.rs | 2 +- zcash_primitives/src/util.rs | 8 +- 15 files changed, 307 insertions(+), 136 deletions(-) diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index c20626fca..2c8fa2596 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -1,6 +1,6 @@ use group::cofactor::CofactorGroup; use zcash_primitives::{ - consensus, + consensus::{self, BlockHeight}, note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo}, primitives::{Note, PaymentAddress}, transaction::Transaction, @@ -31,7 +31,7 @@ pub struct DecryptedOutput { /// Scans a [`Transaction`] for any information that can be decrypted by the set of /// [`ExtendedFullViewingKey`]s. pub fn decrypt_transaction( - height: u32, + height: BlockHeight, tx: &Transaction, extfvks: &[ExtendedFullViewingKey], ) -> Vec { diff --git a/zcash_client_backend/src/proto/mod.rs b/zcash_client_backend/src/proto/mod.rs index ee3b8bcdc..f74742bbd 100644 --- a/zcash_client_backend/src/proto/mod.rs +++ b/zcash_client_backend/src/proto/mod.rs @@ -3,7 +3,11 @@ use ff::PrimeField; use group::GroupEncoding; use std::convert::TryInto; -use zcash_primitives::block::{BlockHash, BlockHeader}; + +use zcash_primitives::{ + block::{BlockHash, BlockHeader}, + consensus::BlockHeight, +}; pub mod compact_formats; @@ -54,6 +58,15 @@ impl compact_formats::CompactBlock { BlockHeader::read(&self.header[..]).ok() } } + + /// Returns the [`BlockHeight`] for this block. + /// + /// A convenience method that wraps [`CompactBlock.height`] + /// + /// [`CompactBlock.height`]: #structfield.height + pub fn height(&self) -> BlockHeight { + BlockHeight::from(self.height) + } } impl compact_formats::CompactOutput { diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 0772a45fd..eecab512a 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -4,7 +4,7 @@ use ff::PrimeField; use std::collections::HashSet; use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_primitives::{ - consensus, + consensus::{self, BlockHeight}, merkle_tree::{CommitmentTree, IncrementalWitness}, note_encryption::try_sapling_compact_note_decryption, sapling::Node, @@ -23,7 +23,7 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx}; /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented /// with this output's commitment. fn scan_output( - height: u32, + height: BlockHeight, (index, output): (usize, CompactOutput), ivks: &[jubjub::Fr], spent_from_accounts: &HashSet, @@ -153,7 +153,7 @@ pub fn scan_block( .collect(); if let Some(output) = scan_output::

( - block.height as u32, + block.height.into(), to_scan, &ivks, &spent_from_accounts, @@ -190,7 +190,7 @@ mod tests { use group::GroupEncoding; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus::TestNetwork, + consensus::{BlockHeight, TestNetwork}, constants::SPENDING_KEY_GENERATOR, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, @@ -239,7 +239,7 @@ mod tests { /// single spend of the given nullifier and a single output paying the given address. /// Returns the CompactBlock. fn fake_compact_block( - height: i32, + height: BlockHeight, nf: [u8; 32], extfvk: ExtendedFullViewingKey, value: Amount, @@ -249,7 +249,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::(height as u32, &mut rng); + let rseed = generate_random_rseed::(height, &mut rng); let note = Note { g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), @@ -269,7 +269,7 @@ mod tests { // Create a fake CompactBlock containing the note let mut cb = CompactBlock::new(); - cb.set_height(height as u64); + cb.set_height(height.into()); // Add a random Sapling tx before ours { @@ -309,7 +309,7 @@ mod tests { let extfvk = ExtendedFullViewingKey::from(&extsk); let cb = fake_compact_block( - 1, + 1u32.into(), [0; 32], extfvk.clone(), Amount::from_u64(5).unwrap(), @@ -341,7 +341,7 @@ mod tests { let extfvk = ExtendedFullViewingKey::from(&extsk); let cb = fake_compact_block( - 1, + 1u32.into(), [0; 32], extfvk.clone(), Amount::from_u64(5).unwrap(), @@ -374,7 +374,7 @@ mod tests { let nf = [7; 32]; let account = 12; - let cb = fake_compact_block(1, nf, extfvk, Amount::from_u64(5).unwrap(), false); + let cb = fake_compact_block(1u32.into(), nf, extfvk, Amount::from_u64(5).unwrap(), false); assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index dfcaf265e..90a0b1a05 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -3,6 +3,10 @@ //! # Examples //! //! ``` +//! use std::ops::{Sub}; +//! use zcash_primitives::{ +//! consensus::BlockHeight, +//! }; //! use zcash_client_sqlite::{ //! chain::{rewind_to_height, validate_combined_chain}, //! error::ErrorKind, @@ -26,7 +30,7 @@ //! // This might be informed by some external chain reorg information, or //! // heuristics such as the platform, available bandwidth, size of recent //! // CompactBlocks, etc. -//! let rewind_height = upper_bound - 10; +//! let rewind_height = upper_bound.sub(10); //! //! // b) Rewind scanned block information. //! rewind_to_height(&db_data, rewind_height); @@ -58,6 +62,9 @@ use protobuf::parse_from_bytes; use rusqlite::{Connection, NO_PARAMS}; use std::path::Path; + +use zcash_primitives::consensus::BlockHeight; + use zcash_client_backend::proto::compact_formats::CompactBlock; use crate::{ @@ -69,11 +76,11 @@ use crate::{ pub enum ChainInvalidCause { PrevHashMismatch, /// (expected_height, actual_height) - HeightMismatch(i32, i32), + HeightMismatch(BlockHeight, BlockHeight), } struct CompactBlockRow { - height: i32, + height: BlockHeight, data: Vec, } @@ -105,16 +112,16 @@ pub fn validate_combined_chain, Q: AsRef>( let (have_scanned, last_scanned_height) = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { row.get(0) - .map(|h| (true, h)) + .map(|h: u32| (true, h.into())) .or(Ok((false, SAPLING_ACTIVATION_HEIGHT - 1))) })?; // Fetch the CompactBlocks we need to validate let mut stmt_blocks = cache .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height DESC")?; - let mut rows = stmt_blocks.query_map(&[last_scanned_height], |row| { + let mut rows = stmt_blocks.query_map(&[u32::from(last_scanned_height)], |row| { Ok(CompactBlockRow { - height: row.get(0)?, + height: row.get(0).map(u32::into)?, data: row.get(1)?, }) })?; @@ -132,7 +139,7 @@ pub fn validate_combined_chain, Q: AsRef>( } }; let block: CompactBlock = parse_from_bytes(&assumed_correct.data)?; - (block.height as i32, block.prev_hash()) + (block.height(), block.prev_hash()) }; for row in rows { @@ -163,7 +170,7 @@ pub fn validate_combined_chain, Q: AsRef>( // Cached blocks MUST hash-chain to the last scanned block. let last_scanned_hash = data.query_row( "SELECT hash FROM blocks WHERE height = ?", - &[last_scanned_height], + &[u32::from(last_scanned_height)], |row| row.get::<_, Vec<_>>(0), )?; if &last_scanned_hash[..] != &last_prev_hash.0[..] { @@ -182,14 +189,16 @@ pub fn validate_combined_chain, Q: AsRef>( /// /// If the requested height is greater than or equal to the height of the last scanned /// block, this function does nothing. -pub fn rewind_to_height>(db_data: P, height: i32) -> Result<(), Error> { +pub fn rewind_to_height>(db_data: P, height: BlockHeight) -> Result<(), Error> { let data = Connection::open(db_data)?; // Recall where we synced up to previously. // If we have never synced, use Sapling activation height. let last_scanned_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + row.get(0) + .map(u32::into) + .or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) })?; if height >= last_scanned_height { @@ -201,16 +210,19 @@ pub fn rewind_to_height>(db_data: P, height: i32) -> Result<(), E data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; // Decrement witnesses. - data.execute("DELETE FROM sapling_witnesses WHERE block > ?", &[height])?; + data.execute( + "DELETE FROM sapling_witnesses WHERE block > ?", + &[u32::from(height)], + )?; // Un-mine transactions. data.execute( "UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?", - &[height], + &[u32::from(height)], )?; // Now that they aren't depended on, delete scanned blocks. - data.execute("DELETE FROM blocks WHERE height > ?", &[height])?; + data.execute("DELETE FROM blocks WHERE height > ?", &[u32::from(height)])?; // Commit the SQL transaction, rewinding atomically. data.execute("COMMIT", NO_PARAMS)?; diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index ed33922b7..90a2922ef 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -1,6 +1,7 @@ use std::error; use std::fmt; use zcash_primitives::{ + consensus::BlockHeight, sapling::Node, transaction::{builder, TxId}, }; @@ -10,13 +11,13 @@ pub enum ErrorKind { CorruptedData(&'static str), IncorrectHRPExtFVK, InsufficientBalance(u64, u64), - InvalidChain(i32, crate::chain::ChainInvalidCause), + InvalidChain(BlockHeight, crate::chain::ChainInvalidCause), InvalidExtSK(u32), - InvalidHeight(i32, i32), + InvalidHeight(BlockHeight, BlockHeight), InvalidMemo(std::str::Utf8Error), - InvalidNewWitnessAnchor(usize, TxId, i32, Node), + InvalidNewWitnessAnchor(usize, TxId, BlockHeight, Node), InvalidNote, - InvalidWitnessAnchor(i64, i32), + InvalidWitnessAnchor(i64, BlockHeight), ScanRequired, TableNotEmpty, Bech32(bech32::Error), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 9ecaafeec..2d451e12a 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -27,7 +27,7 @@ use rusqlite::{Connection, NO_PARAMS}; use std::cmp; use zcash_client_backend::encoding::encode_payment_address; -use zcash_primitives::zip32::ExtendedFullViewingKey; +use zcash_primitives::{consensus::BlockHeight, zip32::ExtendedFullViewingKey}; #[cfg(feature = "mainnet")] use zcash_client_backend::constants::mainnet::{ @@ -55,10 +55,10 @@ pub mod transact; const ANCHOR_OFFSET: u32 = 10; #[cfg(feature = "mainnet")] -const SAPLING_ACTIVATION_HEIGHT: i32 = 419_200; +const SAPLING_ACTIVATION_HEIGHT: BlockHeight = BlockHeight::from_u32(419_200); #[cfg(not(feature = "mainnet"))] -const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; +const SAPLING_ACTIVATION_HEIGHT: BlockHeight = BlockHeight::from_u32(280_000); fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { let addr = extfvk.default_address().unwrap().1; @@ -67,7 +67,9 @@ fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { /// Determines the target height for a transaction, and the height from which to /// select anchors, based on the current synchronised block chain. -fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error::Error> { +fn get_target_and_anchor_heights( + data: &Connection, +) -> Result<(BlockHeight, BlockHeight), error::Error> { data.query_row_and_then( "SELECT MIN(height), MAX(height) FROM blocks", NO_PARAMS, @@ -86,7 +88,10 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error: let anchor_height = cmp::max(target_height.saturating_sub(ANCHOR_OFFSET), min_height); - Ok((target_height, anchor_height)) + Ok(( + BlockHeight::from(target_height), + BlockHeight::from(anchor_height), + )) } }, ) @@ -106,6 +111,7 @@ mod tests { }; use zcash_primitives::{ block::BlockHash, + consensus::BlockHeight, note_encryption::{Memo, SaplingNoteEncryption}, primitives::{Note, PaymentAddress}, transaction::components::Amount, @@ -116,7 +122,7 @@ mod tests { /// Create a fake CompactBlock at the given height, containing a single output paying /// the given address. Returns the CompactBlock and the nullifier for the new note. pub(crate) fn fake_compact_block( - height: i32, + height: BlockHeight, prev_hash: BlockHash, extfvk: ExtendedFullViewingKey, value: Amount, @@ -125,7 +131,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::(height as u32, &mut rng); + let rseed = generate_random_rseed::(height, &mut rng); let note = Note { g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), @@ -154,7 +160,7 @@ mod tests { ctx.set_hash(txid); ctx.outputs.push(cout); let mut cb = CompactBlock::new(); - cb.set_height(height as u64); + cb.set_height(u64::from(height)); cb.hash.resize(32, 0); rng.fill_bytes(&mut cb.hash); cb.prevHash.extend_from_slice(&prev_hash.0); @@ -165,7 +171,7 @@ mod tests { /// Create a fake CompactBlock at the given height, spending a single note from the /// given address. pub(crate) fn fake_compact_block_spending( - height: i32, + height: BlockHeight, prev_hash: BlockHash, (nf, in_value): (Vec, Amount), extfvk: ExtendedFullViewingKey, @@ -173,7 +179,7 @@ mod tests { value: Amount, ) -> CompactBlock { let mut rng = OsRng; - let rseed = generate_random_rseed::(height as u32, &mut rng); + let rseed = generate_random_rseed::(height, &mut rng); // Create a fake CompactBlock containing the note let mut cspend = CompactSpend::new(); @@ -213,7 +219,7 @@ 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::(height as u32, &mut rng); + let rseed = generate_random_rseed::(height, &mut rng); let note = Note { g_d: change_addr.diversifier().g_d().unwrap(), pk_d: change_addr.pk_d().clone(), @@ -239,7 +245,7 @@ mod tests { }); let mut cb = CompactBlock::new(); - cb.set_height(height as u64); + cb.set_height(u64::from(height)); cb.hash.resize(32, 0); rng.fill_bytes(&mut cb.hash); cb.prevHash.extend_from_slice(&prev_hash.0); @@ -255,7 +261,7 @@ mod tests { .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") .unwrap() .execute(&[ - (cb.height as i32).to_sql().unwrap(), + (cb.height as u32).to_sql().unwrap(), cb_bytes.to_sql().unwrap(), ]) .unwrap(); diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs index c9bd5c4a9..4bf478dc5 100644 --- a/zcash_client_sqlite/src/query.rs +++ b/zcash_client_sqlite/src/query.rs @@ -84,7 +84,7 @@ pub fn get_verified_balance>(db_data: P, account: u32) -> Result< "SELECT SUM(value) FROM received_notes INNER JOIN transactions ON transactions.id_tx = received_notes.tx WHERE account = ? AND spent IS NULL AND transactions.block <= ?", - &[account, anchor_height], + &[account, u32::from(anchor_height)], |row| row.get(0).or(Ok(0)), )?; diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 318b33b67..fe49de97b 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -1,14 +1,17 @@ //! Functions for scanning the chain and extracting relevant information. +use std::path::Path; + use ff::PrimeField; use protobuf::parse_from_bytes; use rusqlite::{types::ToSql, Connection, OptionalExtension, NO_PARAMS}; -use std::path::Path; + use zcash_client_backend::{ decrypt_transaction, encoding::decode_extended_full_viewing_key, proto::compact_formats::CompactBlock, welding_rig::scan_block, }; use zcash_primitives::{ + consensus::BlockHeight, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::Node, transaction::Transaction, @@ -21,7 +24,7 @@ use crate::{ }; struct CompactBlockRow { - height: i32, + height: BlockHeight, data: Vec, } @@ -63,7 +66,7 @@ struct WitnessRow { pub fn scan_cached_blocks, Q: AsRef>( db_cache: P, db_data: Q, - limit: Option, + limit: Option, ) -> Result<(), Error> { let cache = Connection::open(db_cache)?; let data = Connection::open(db_data)?; @@ -71,19 +74,24 @@ pub fn scan_cached_blocks, Q: AsRef>( // Recall where we synced up to previously. // If we have never synced, use sapling activation height to select all cached CompactBlocks. let mut last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + row.get::<_, u32>(0) + .map(BlockHeight::from) + .or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) })?; // Fetch the CompactBlocks we need to scan let mut stmt_blocks = cache.prepare( "SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC LIMIT ?", )?; - let rows = stmt_blocks.query_map(&[last_height, limit.unwrap_or(i32::max_value())], |row| { - Ok(CompactBlockRow { - height: row.get(0)?, - data: row.get(1)?, - }) - })?; + let rows = stmt_blocks.query_map( + &[u32::from(last_height), limit.unwrap_or(u32::max_value())], + |row| { + Ok(CompactBlockRow { + height: row.get::<_, u32>(0).map(BlockHeight::from)?, + data: row.get(1)?, + }) + }, + )?; // Fetch the ExtendedFullViewingKeys we are tracking let mut stmt_fetch_accounts = @@ -101,7 +109,7 @@ pub fn scan_cached_blocks, Q: AsRef>( // Get the most recent CommitmentTree let mut stmt_fetch_tree = data.prepare("SELECT sapling_tree FROM blocks WHERE height = ?")?; let mut tree = stmt_fetch_tree - .query_row(&[last_height], |row| { + .query_row(&[u32::from(last_height)], |row| { row.get(0).map(|data: Vec<_>| { CommitmentTree::read(&data[..]).unwrap_or_else(|_| CommitmentTree::new()) }) @@ -111,7 +119,7 @@ pub fn scan_cached_blocks, Q: AsRef>( // Get most recent incremental witnesses for the notes we are tracking let mut stmt_fetch_witnesses = data.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?; - let witnesses = stmt_fetch_witnesses.query_map(&[last_height], |row| { + let witnesses = stmt_fetch_witnesses.query_map(&[u32::from(last_height)], |row| { let id_note = row.get(0)?; let data: Vec<_> = row.get(1)?; Ok(IncrementalWitness::read(&data[..]).map(|witness| WitnessRow { id_note, witness })) @@ -226,7 +234,7 @@ pub fn scan_cached_blocks, Q: AsRef>( tree.write(&mut encoded_tree) .expect("Should be able to write to a Vec"); stmt_insert_block.execute(&[ - row.height.to_sql()?, + u32::from(row.height).to_sql()?, block_hash.to_sql()?, block_time.to_sql()?, encoded_tree.to_sql()?, @@ -236,7 +244,7 @@ pub fn scan_cached_blocks, Q: AsRef>( // First try update an existing transaction in the database. let txid = tx.txid.0.to_vec(); let tx_row = if stmt_update_tx.execute(&[ - row.height.to_sql()?, + u32::from(row.height).to_sql()?, (tx.index as i64).to_sql()?, txid.to_sql()?, ])? == 0 @@ -244,7 +252,7 @@ pub fn scan_cached_blocks, Q: AsRef>( // It isn't there, so insert our transaction into the database. stmt_insert_tx.execute(&[ txid.to_sql()?, - row.height.to_sql()?, + u32::from(row.height).to_sql()?, (tx.index as i64).to_sql()?, ])?; data.last_insert_rowid() @@ -331,16 +339,16 @@ pub fn scan_cached_blocks, Q: AsRef>( .expect("Should be able to write to a Vec"); stmt_insert_witness.execute(&[ witness_row.id_note.to_sql()?, - last_height.to_sql()?, + u32::from(last_height).to_sql()?, encoded.to_sql()?, ])?; } // Prune the stored witnesses (we only expect rollbacks of at most 100 blocks). - stmt_prune_witnesses.execute(&[last_height - 100])?; + stmt_prune_witnesses.execute(&[u32::from(last_height - 100)])?; // Update now-expired transactions that didn't get mined. - stmt_update_expired.execute(&[last_height])?; + stmt_update_expired.execute(&[u32::from(last_height)])?; // Commit the SQL transaction, writing this block's data atomically. data.execute("COMMIT", NO_PARAMS)?; @@ -373,7 +381,9 @@ pub fn decrypt_and_store_transaction>( // 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)) + .query_row(&[tx.txid().0.to_vec()], |row| { + row.get::<_, u32>(0).map(BlockHeight::from) + }) .optional()? { Some(height) => height, @@ -382,11 +392,11 @@ pub fn decrypt_and_store_transaction>( row.get(0) }) .optional()? - .map(|last_height: u32| last_height + 1) - .unwrap_or(SAPLING_ACTIVATION_HEIGHT as u32), + .map(|last_height: u32| BlockHeight::from(last_height + 1)) + .unwrap_or(SAPLING_ACTIVATION_HEIGHT), }; - let outputs = decrypt_transaction::(height as u32, tx, &extfvks); + let outputs = decrypt_transaction::(height, tx, &extfvks); if outputs.is_empty() { // Nothing to see here @@ -429,13 +439,17 @@ pub fn decrypt_and_store_transaction>( let mut raw_tx = vec![]; tx.write(&mut raw_tx)?; let tx_row = if stmt_update_tx.execute(&[ - tx.expiry_height.to_sql()?, + u32::from(tx.expiry_height).to_sql()?, raw_tx.to_sql()?, txid.to_sql()?, ])? == 0 { // It isn't there, so insert our transaction into the database. - stmt_insert_tx.execute(&[txid.to_sql()?, tx.expiry_height.to_sql()?, raw_tx.to_sql()?])?; + stmt_insert_tx.execute(&[ + txid.to_sql()?, + u32::from(tx.expiry_height).to_sql()?, + raw_tx.to_sql()?, + ])?; data.last_insert_rowid() } else { // It was there, so grab its row number. diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 9feab59e8..03a17e33a 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -154,10 +154,7 @@ pub fn create_to_address>( }; // Target the next block, assuming we are up-to-date. - let (height, anchor_height) = { - let (target_height, anchor_height) = get_target_and_anchor_heights(&data)?; - (target_height, i64::from(anchor_height)) - }; + let (height, anchor_height) = get_target_and_anchor_heights(&data)?; // The goal of this SQL statement is to select the oldest notes until the required // value has been reached, and then fetch the witnesses at the desired height for the @@ -204,10 +201,10 @@ pub fn create_to_address>( let notes = stmt_select_notes.query_and_then::<_, Error, _, _>( &[ i64::from(account), - anchor_height, + i64::from(anchor_height), target_value, target_value, - anchor_height, + i64::from(anchor_height), ], |row| { let diversifier = { @@ -309,7 +306,7 @@ pub fn create_to_address>( stmt_insert_tx.execute(&[ tx.txid().0.to_sql()?, created.to_sql()?, - tx.expiry_height.to_sql()?, + i64::from(tx.expiry_height).to_sql()?, raw_tx.to_sql()?, ])?; let id_tx = data.last_insert_rowid(); @@ -808,7 +805,7 @@ mod tests { let output = &tx.shielded_outputs[output_index as usize]; try_sapling_output_recovery::( - SAPLING_ACTIVATION_HEIGHT as u32, + SAPLING_ACTIVATION_HEIGHT, &extfvk.fvk.ovk, &output.cv, &output.cmu, diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 4cc67f49b..957ef9fd5 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -1,13 +1,127 @@ //! Consensus parameters. +use std::cmp::{Ord, Ordering}; use std::convert::TryFrom; use std::fmt; +use std::ops::{Add, Sub}; + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct BlockHeight(u32); + +pub const H0: BlockHeight = BlockHeight(0); + +impl BlockHeight { + pub const fn from_u32(v: u32) -> BlockHeight { + BlockHeight(v) + } +} + +impl fmt::Display for BlockHeight { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(formatter) + } +} + +impl Ord for BlockHeight { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for BlockHeight { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From for BlockHeight { + fn from(value: u32) -> Self { + BlockHeight(value) + } +} + +impl From for BlockHeight { + fn from(value: u64) -> Self { + BlockHeight(value as u32) + } +} + +impl TryFrom for BlockHeight { + type Error = std::num::TryFromIntError; + + fn try_from(value: i32) -> Result { + u32::try_from(value).map(BlockHeight) + } +} + +impl TryFrom for BlockHeight { + type Error = std::num::TryFromIntError; + + fn try_from(value: i64) -> Result { + u32::try_from(value).map(BlockHeight) + } +} + +impl From for u32 { + fn from(value: BlockHeight) -> u32 { + value.0 + } +} + +impl From for u64 { + fn from(value: BlockHeight) -> u64 { + value.0 as u64 + } +} + +impl From for i64 { + fn from(value: BlockHeight) -> i64 { + value.0 as i64 + } +} + +impl Add for BlockHeight { + type Output = Self; + + fn add(self, other: u32) -> Self { + BlockHeight(self.0 + other) + } +} + +impl Add for BlockHeight { + type Output = Self; + + fn add(self, other: Self) -> Self { + self + other.0 + } +} + +impl Sub for BlockHeight { + type Output = Self; + + fn sub(self, other: u32) -> Self { + if other > self.0 { + panic!("Subtraction resulted in negative block height."); + } + + BlockHeight(self.0 - other) + } +} + +impl Sub for BlockHeight { + type Output = Self; + + fn sub(self, other: Self) -> Self { + self - other.0 + } +} /// Zcash consensus parameters. pub trait Parameters { - fn activation_height(nu: NetworkUpgrade) -> Option; + fn activation_height(nu: NetworkUpgrade) -> Option; - fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool { + fn is_nu_active(nu: NetworkUpgrade, height: BlockHeight) -> bool { match Self::activation_height(nu) { Some(h) if h <= height => true, _ => false, @@ -20,13 +134,13 @@ pub trait Parameters { pub struct MainNetwork; impl Parameters for MainNetwork { - fn activation_height(nu: NetworkUpgrade) -> Option { + fn activation_height(nu: NetworkUpgrade) -> Option { match nu { - NetworkUpgrade::Overwinter => Some(347_500), - NetworkUpgrade::Sapling => Some(419_200), - NetworkUpgrade::Blossom => Some(653_600), - NetworkUpgrade::Heartwood => Some(903_000), - NetworkUpgrade::Canopy => Some(1_046_400), + NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)), + NetworkUpgrade::Sapling => Some(BlockHeight(419_200)), + NetworkUpgrade::Blossom => Some(BlockHeight(653_600)), + NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)), + NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)), } } } @@ -36,13 +150,13 @@ impl Parameters for MainNetwork { pub struct TestNetwork; impl Parameters for TestNetwork { - fn activation_height(nu: NetworkUpgrade) -> Option { + fn activation_height(nu: NetworkUpgrade) -> Option { match nu { - NetworkUpgrade::Overwinter => Some(207_500), - NetworkUpgrade::Sapling => Some(280_000), - NetworkUpgrade::Blossom => Some(584_000), - NetworkUpgrade::Heartwood => Some(903_800), - NetworkUpgrade::Canopy => Some(1_028_500), + NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)), + NetworkUpgrade::Sapling => Some(BlockHeight(280_000)), + NetworkUpgrade::Blossom => Some(BlockHeight(584_000)), + NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)), + NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)), } } } @@ -176,7 +290,7 @@ impl BranchId { /// the given height. /// /// This is the branch ID that should be used when creating transactions. - pub fn for_height(height: u32) -> Self { + pub fn for_height(height: BlockHeight) -> Self { for nu in UPGRADES_IN_ORDER.iter().rev() { if C::is_nu_active(*nu, height) { return nu.branch_id(); @@ -192,7 +306,9 @@ impl BranchId { mod tests { use std::convert::TryFrom; - use super::{BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER}; + use super::{ + BlockHeight, BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER, + }; #[test] fn nu_ordering() { @@ -216,14 +332,17 @@ mod tests { #[test] fn nu_is_active() { - assert!(!MainNetwork::is_nu_active(NetworkUpgrade::Overwinter, 0)); assert!(!MainNetwork::is_nu_active( NetworkUpgrade::Overwinter, - 347_499 + BlockHeight(0) + )); + assert!(!MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + BlockHeight(347_499) )); assert!(MainNetwork::is_nu_active( NetworkUpgrade::Overwinter, - 347_500 + BlockHeight(347_500) )); } @@ -235,25 +354,28 @@ mod tests { #[test] fn branch_id_for_height() { - assert_eq!(BranchId::for_height::(0), BranchId::Sprout,); assert_eq!( - BranchId::for_height::(419_199), + BranchId::for_height::(BlockHeight(0)), + BranchId::Sprout, + ); + assert_eq!( + BranchId::for_height::(BlockHeight(419_199)), BranchId::Overwinter, ); assert_eq!( - BranchId::for_height::(419_200), + BranchId::for_height::(BlockHeight(419_200)), BranchId::Sapling, ); assert_eq!( - BranchId::for_height::(903_000), + BranchId::for_height::(BlockHeight(903_000)), BranchId::Heartwood, ); assert_eq!( - BranchId::for_height::(1_046_400), + BranchId::for_height::(BlockHeight(1_046_400)), BranchId::Canopy, ); assert_eq!( - BranchId::for_height::(5_000_000), + BranchId::for_height::(BlockHeight(5_000_000)), BranchId::Canopy, ); } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index bb25defdf..b40e9d820 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1,7 +1,7 @@ //! Implementation of in-band secret distribution for Zcash transactions. use crate::{ - consensus::{self, NetworkUpgrade, ZIP212_GRACE_PERIOD}, + consensus::{self, BlockHeight, NetworkUpgrade, ZIP212_GRACE_PERIOD}, primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; @@ -359,7 +359,7 @@ impl SaplingNoteEncryption { } fn parse_note_plaintext_without_memo( - height: u32, + height: BlockHeight, ivk: &jubjub::Fr, epk: &jubjub::SubgroupPoint, cmu: &bls12_381::Scalar, @@ -406,7 +406,10 @@ fn parse_note_plaintext_without_memo( Some((note, to)) } -pub fn plaintext_version_is_valid(height: u32, leadbyte: u8) -> bool { +pub fn plaintext_version_is_valid( + height: BlockHeight, + leadbyte: u8, +) -> bool { if P::is_nu_active(NetworkUpgrade::Canopy, height) { let grace_period_end_height = P::activation_height(NetworkUpgrade::Canopy) .expect("Should have Canopy activation height") @@ -435,7 +438,7 @@ pub fn plaintext_version_is_valid(height: u32, leadbyt /// /// Implements section 4.17.2 of the Zcash Protocol Specification. pub fn try_sapling_note_decryption( - height: u32, + height: BlockHeight, ivk: &jubjub::Fr, epk: &jubjub::SubgroupPoint, cmu: &bls12_381::Scalar, @@ -478,7 +481,7 @@ pub fn try_sapling_note_decryption( /// /// [`ZIP 307`]: https://zips.z.cash/zip-0307 pub fn try_sapling_compact_note_decryption( - height: u32, + height: BlockHeight, ivk: &jubjub::Fr, epk: &jubjub::SubgroupPoint, cmu: &bls12_381::Scalar, @@ -506,7 +509,7 @@ pub fn try_sapling_compact_note_decryption( /// Implements part of section 4.17.3 of the Zcash Protocol Specification. /// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`]. pub fn try_sapling_output_recovery_with_ock( - height: u32, + height: BlockHeight, ock: &OutgoingCipherKey, cmu: &bls12_381::Scalar, epk: &jubjub::SubgroupPoint, @@ -612,7 +615,7 @@ pub fn try_sapling_output_recovery_with_ock( /// /// Implements section 4.17.3 of the Zcash Protocol Specification. pub fn try_sapling_output_recovery( - height: u32, + height: BlockHeight, ovk: &OutgoingViewingKey, cv: &jubjub::ExtendedPoint, cmu: &bls12_381::Scalar, @@ -650,7 +653,7 @@ mod tests { }; use crate::{ consensus::{ - NetworkUpgrade, + BlockHeight, NetworkUpgrade, NetworkUpgrade::{Canopy, Sapling}, Parameters, TestNetwork, ZIP212_GRACE_PERIOD, }, @@ -776,7 +779,7 @@ mod tests { } fn random_enc_ciphertext( - height: u32, + height: BlockHeight, mut rng: &mut R, ) -> ( OutgoingViewingKey, @@ -835,7 +838,7 @@ mod tests { } fn random_enc_ciphertext_with( - height: u32, + height: BlockHeight, ivk: jubjub::Fr, mut rng: &mut R, ) -> ( diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 616179245..1916d773b 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -9,7 +9,7 @@ use std::fmt; use std::marker::PhantomData; use crate::{ - consensus, + consensus::{self, BlockHeight}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, @@ -85,7 +85,7 @@ pub struct SaplingOutput { impl SaplingOutput { pub fn new( - height: u32, + height: BlockHeight, rng: &mut R, ovk: Option, to: PaymentAddress, @@ -303,7 +303,7 @@ impl TransactionMetadata { /// Generates a [`Transaction`] from its inputs and outputs. pub struct Builder { rng: R, - height: u32, + height: BlockHeight, mtx: TransactionData, fee: Amount, anchor: Option, @@ -324,7 +324,7 @@ impl Builder { /// expiry delta (20 blocks). /// /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new(height: u32) -> Self { + pub fn new(height: BlockHeight) -> Self { Builder::new_with_rng(height, OsRng) } } @@ -339,7 +339,7 @@ impl Builder { /// 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 { + pub fn new_with_rng(height: BlockHeight, rng: R) -> Builder { let mut mtx = TransactionData::new(); mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; @@ -709,8 +709,8 @@ mod tests { use super::{Builder, Error}; use crate::{ - consensus, consensus::TestNetwork, + consensus::{self, H0}, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, primitives::Rseed, @@ -727,7 +727,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::::new(H0); assert_eq!( builder.add_sapling_output(Some(ovk), to, Amount::from_i64(-1).unwrap(), None), Err(Error::InvalidAmount) @@ -787,7 +787,7 @@ mod tests { tree.append(cmu1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); - let mut builder = Builder::::new(0); + let mut builder = Builder::::new(H0); // Create a tx with a sapling spend. binding_sig should be present builder @@ -813,7 +813,7 @@ mod tests { #[test] fn fails_on_negative_transparent_output() { - let mut builder = Builder::::new(0); + let mut builder = Builder::::new(H0); assert_eq!( builder.add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -833,7 +833,7 @@ mod tests { // Fails with no inputs or outputs // 0.0001 t-ZEC fee { - let builder = Builder::::new(0); + let builder = Builder::::new(H0); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap())) @@ -847,7 +847,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::::new(H0); builder .add_sapling_output( ovk.clone(), @@ -865,7 +865,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::::new(H0); builder .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -889,7 +889,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::::new(H0); builder .add_sapling_spend( extsk.clone(), @@ -932,7 +932,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::::new(H0); builder .add_sapling_spend( extsk.clone(), diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 24b812703..804fb153d 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -7,8 +7,7 @@ use std::fmt; use std::io::{self, Read, Write}; use std::ops::Deref; -use crate::redjubjub::Signature; -use crate::serialize::Vector; +use crate::{consensus::BlockHeight, redjubjub::Signature, serialize::Vector}; pub mod builder; pub mod components; @@ -65,7 +64,7 @@ pub struct TransactionData { pub vin: Vec, pub vout: Vec, pub lock_time: u32, - pub expiry_height: u32, + pub expiry_height: BlockHeight, pub value_balance: Amount, pub shielded_spends: Vec, pub shielded_outputs: Vec, @@ -119,7 +118,7 @@ impl TransactionData { vin: vec![], vout: vec![], lock_time: 0, - expiry_height: 0, + expiry_height: 0u32.into(), value_balance: Amount::zero(), shielded_spends: vec![], shielded_outputs: vec![], @@ -188,10 +187,10 @@ impl Transaction { let vin = Vector::read(&mut reader, TxIn::read)?; let vout = Vector::read(&mut reader, TxOut::read)?; let lock_time = reader.read_u32::()?; - let expiry_height = if is_overwinter_v3 || is_sapling_v4 { - reader.read_u32::()? + let expiry_height: BlockHeight = if is_overwinter_v3 || is_sapling_v4 { + reader.read_u32::()?.into() } else { - 0 + 0u32.into() }; let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 { @@ -274,7 +273,7 @@ impl Transaction { Vector::write(&mut writer, &self.vout, |w, e| e.write(w))?; writer.write_u32::(self.lock_time)?; if is_overwinter_v3 || is_sapling_v4 { - writer.write_u32::(self.expiry_height)?; + writer.write_u32::(u32::from(self.expiry_height))?; } if is_sapling_v4 { diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 2cc847b59..1340227dd 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -206,7 +206,7 @@ pub fn signature_hash_data( ); } update_u32!(h, tx.lock_time, tmp); - update_u32!(h, tx.expiry_height, tmp); + update_u32!(h, tx.expiry_height.into(), tmp); if sigversion == SigHashVersion::Sapling { h.update(&tx.value_balance.to_i64_le_bytes()); } diff --git a/zcash_primitives/src/util.rs b/zcash_primitives/src/util.rs index c5442049d..307e530aa 100644 --- a/zcash_primitives/src/util.rs +++ b/zcash_primitives/src/util.rs @@ -1,6 +1,10 @@ use blake2b_simd::Params; -use crate::{consensus, consensus::NetworkUpgrade, primitives::Rseed}; +use crate::{ + consensus::{self, BlockHeight, NetworkUpgrade}, + primitives::Rseed, +}; + use ff::Field; use rand_core::{CryptoRng, RngCore}; @@ -13,7 +17,7 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr { } pub fn generate_random_rseed( - height: u32, + height: BlockHeight, rng: &mut R, ) -> Rseed { if P::is_nu_active(NetworkUpgrade::Canopy, height) { From 1ad9294933708df968babac551b030582ca39471 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 5 Aug 2020 14:27:40 -0600 Subject: [PATCH 2/7] Remove static determination of network state. In the interest of making the library usable for both testnet and mainnet without recompilation, static resolution of network parameters has been replaced with a parameter passed to the relevant functions. This also moves addres prefix constants into the network parameters. --- zcash_client_backend/src/constants.rs | 5 - zcash_client_backend/src/decrypt.rs | 7 +- zcash_client_backend/src/encoding.rs | 36 ++- zcash_client_backend/src/keys.rs | 3 +- zcash_client_backend/src/lib.rs | 1 - zcash_client_backend/src/welding_rig.rs | 38 ++- zcash_client_sqlite/src/address.rs | 34 +-- zcash_client_sqlite/src/chain.rs | 117 ++++---- zcash_client_sqlite/src/error.rs | 2 + zcash_client_sqlite/src/init.rs | 49 ++-- zcash_client_sqlite/src/lib.rs | 70 +++-- zcash_client_sqlite/src/query.rs | 3 +- zcash_client_sqlite/src/scan.rs | 96 ++++--- zcash_client_sqlite/src/transact.rs | 109 +++++--- zcash_primitives/src/consensus.rs | 131 +++++---- zcash_primitives/src/constants.rs | 4 + .../src/constants/mainnet.rs | 0 .../src/constants/regtest.rs | 0 .../src/constants/testnet.rs | 0 zcash_primitives/src/note_encryption.rs | 262 +++++++++++------- zcash_primitives/src/transaction/builder.rs | 50 ++-- zcash_primitives/src/util.rs | 3 +- 22 files changed, 626 insertions(+), 394 deletions(-) delete mode 100644 zcash_client_backend/src/constants.rs rename {zcash_client_backend => zcash_primitives}/src/constants/mainnet.rs (100%) rename {zcash_client_backend => zcash_primitives}/src/constants/regtest.rs (100%) rename {zcash_client_backend => zcash_primitives}/src/constants/testnet.rs (100%) diff --git a/zcash_client_backend/src/constants.rs b/zcash_client_backend/src/constants.rs deleted file mode 100644 index 627bbd900..000000000 --- a/zcash_client_backend/src/constants.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Zcash global and per-network constants. - -pub mod mainnet; -pub mod regtest; -pub mod testnet; diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index 2c8fa2596..ce12ecc67 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -31,6 +31,7 @@ pub struct DecryptedOutput { /// Scans a [`Transaction`] for any information that can be decrypted by the set of /// [`ExtendedFullViewingKey`]s. pub fn decrypt_transaction( + params: &P, height: BlockHeight, tx: &Transaction, extfvks: &[ExtendedFullViewingKey], @@ -51,7 +52,8 @@ pub fn decrypt_transaction( let epk = epk.unwrap(); for (account, (ivk, ovk)) in vks.iter().enumerate() { - let ((note, to, memo), outgoing) = match try_sapling_note_decryption::

( + let ((note, to, memo), outgoing) = match try_sapling_note_decryption( + params, height, ivk, &epk, @@ -59,7 +61,8 @@ pub fn decrypt_transaction( &output.enc_ciphertext, ) { Some(ret) => (ret, false), - None => match try_sapling_output_recovery::

( + None => match try_sapling_output_recovery( + params, height, ovk, &output.cv, diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 9368132e3..1548d6575 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -1,9 +1,9 @@ //! Encoding and decoding functions for Zcash key and address structs. //! -//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`] +//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`zcash_primitives::constants`] //! module. //! -//! [`constants`]: crate::constants +//! [`constants`]: zcash_primitives::constants use bech32::{self, Error, FromBase32, ToBase32}; use bs58::{self, decode::Error as Bs58Error}; @@ -41,8 +41,10 @@ where /// # Examples /// /// ``` -/// use zcash_client_backend::{ +/// use zcash_primitives::{ /// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_SPENDING_KEY}, +/// }; +/// use zcash_client_backend::{ /// encoding::encode_extended_spending_key, /// keys::spending_key, /// }; @@ -67,8 +69,10 @@ pub fn decode_extended_spending_key( /// # Examples /// /// ``` -/// use zcash_client_backend::{ +/// use zcash_primitives::{ /// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY}, +/// }; +/// use zcash_client_backend::{ /// encoding::encode_extended_full_viewing_key, /// keys::spending_key, /// }; @@ -100,10 +104,12 @@ pub fn decode_extended_full_viewing_key( /// use rand_core::SeedableRng; /// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ -/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, /// encoding::encode_payment_address, /// }; -/// use zcash_primitives::primitives::{Diversifier, PaymentAddress}; +/// use zcash_primitives::{ +/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, +/// primitives::{Diversifier, PaymentAddress}, +/// }; /// /// let rng = &mut XorShiftRng::from_seed([ /// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, @@ -135,10 +141,12 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { /// use rand_core::SeedableRng; /// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ -/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, /// encoding::decode_payment_address, /// }; -/// use zcash_primitives::primitives::{Diversifier, PaymentAddress}; +/// use zcash_primitives::{ +/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, +/// primitives::{Diversifier, PaymentAddress}, +/// }; /// /// let rng = &mut XorShiftRng::from_seed([ /// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, @@ -177,10 +185,12 @@ pub fn decode_payment_address(hrp: &str, s: &str) -> Result( + params: &P, height: BlockHeight, (index, output): (usize, CompactOutput), ivks: &[jubjub::Fr], @@ -51,7 +52,7 @@ fn scan_output( for (account, ivk) in ivks.iter().enumerate() { let (note, to) = - match try_sapling_compact_note_decryption::

(height, ivk, &epk, &cmu, &ct) { + match try_sapling_compact_note_decryption(params, height, ivk, &epk, &cmu, &ct) { Some(ret) => ret, None => continue, }; @@ -86,6 +87,7 @@ fn scan_output( /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are /// incremented appropriately. pub fn scan_block( + params: &P, block: CompactBlock, extfvks: &[ExtendedFullViewingKey], nullifiers: &[(&[u8], usize)], @@ -152,7 +154,8 @@ pub fn scan_block( .map(|output| &mut output.witness) .collect(); - if let Some(output) = scan_output::

( + if let Some(output) = scan_output( + params, block.height.into(), to_scan, &ivks, @@ -190,7 +193,7 @@ mod tests { use group::GroupEncoding; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - consensus::{BlockHeight, TestNetwork}, + consensus::{BlockHeight, Network}, constants::SPENDING_KEY_GENERATOR, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, @@ -249,7 +252,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::(height, &mut rng); + let rseed = generate_random_rseed(&Network::TestNetwork, height, &mut rng); let note = Note { g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), @@ -318,7 +321,14 @@ 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( + &Network::TestNetwork, + cb, + &[extfvk], + &[], + &mut tree, + &mut [], + ); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -350,7 +360,14 @@ 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( + &Network::TestNetwork, + cb, + &[extfvk], + &[], + &mut tree, + &mut [], + ); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -378,7 +395,14 @@ 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( + &Network::TestNetwork, + cb, + &[], + &[(&nf, account)], + &mut tree, + &mut [], + ); assert_eq!(txs.len(), 1); let tx = &txs[0]; diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs index bcd5a7e32..8b7bdc39c 100644 --- a/zcash_client_sqlite/src/address.rs +++ b/zcash_client_sqlite/src/address.rs @@ -4,16 +4,10 @@ use zcash_client_backend::encoding::{ decode_payment_address, decode_transparent_address, encode_payment_address, encode_transparent_address, }; -use zcash_primitives::{legacy::TransparentAddress, primitives::PaymentAddress}; - -#[cfg(feature = "mainnet")] -use zcash_client_backend::constants::mainnet::{ - B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, -}; - -#[cfg(not(feature = "mainnet"))] -use zcash_client_backend::constants::testnet::{ - B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +use zcash_primitives::{ + consensus::{self}, + legacy::TransparentAddress, + primitives::PaymentAddress, }; /// An address that funds can be sent to. @@ -35,26 +29,28 @@ impl From for RecipientAddress { } impl RecipientAddress { - pub fn from_str(s: &str) -> Option { - if let Ok(Some(pa)) = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) { + pub fn from_str(params: &P, s: &str) -> Option { + if let Ok(Some(pa)) = decode_payment_address(params.hrp_sapling_payment_address(), s) { Some(pa.into()) - } else if let Ok(Some(addr)) = - decode_transparent_address(&B58_PUBKEY_ADDRESS_PREFIX, &B58_SCRIPT_ADDRESS_PREFIX, s) - { + } else if let Ok(Some(addr)) = decode_transparent_address( + ¶ms.b58_pubkey_address_prefix(), + ¶ms.b58_script_address_prefix(), + s, + ) { Some(addr.into()) } else { None } } - pub fn to_string(&self) -> String { + pub fn to_string(&self, params: &P) -> String { match self { RecipientAddress::Shielded(pa) => { - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, pa) + encode_payment_address(params.hrp_sapling_payment_address(), pa) } RecipientAddress::Transparent(addr) => encode_transparent_address( - &B58_PUBKEY_ADDRESS_PREFIX, - &B58_SCRIPT_ADDRESS_PREFIX, + ¶ms.b58_pubkey_address_prefix(), + ¶ms.b58_script_address_prefix(), addr, ), } diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index 90a0b1a05..a15f51675 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -3,16 +3,19 @@ //! # Examples //! //! ``` -//! use std::ops::{Sub}; +//! use rusqlite::Connection; +//! use std::ops::Sub; //! use zcash_primitives::{ -//! consensus::BlockHeight, +//! consensus::{BlockHeight, Network, Parameters} //! }; +//! //! use zcash_client_sqlite::{ //! chain::{rewind_to_height, validate_combined_chain}, //! error::ErrorKind, //! scan::scan_cached_blocks, //! }; //! +//! let network = Network::TestNetwork; //! let db_cache = "/path/to/cache.db"; //! let db_data = "/path/to/data.db"; //! @@ -22,7 +25,7 @@ //! // //! // Given that we assume the server always gives us correct-at-the-time blocks, any //! // errors are in the blocks we have previously cached or scanned. -//! if let Err(e) = validate_combined_chain(&db_cache, &db_data) { +//! if let Err(e) = validate_combined_chain(network, &db_cache, &db_data) { //! match e.kind() { //! ErrorKind::InvalidChain(upper_bound, _) => { //! // a) Pick a height to rewind to. @@ -33,7 +36,7 @@ //! let rewind_height = upper_bound.sub(10); //! //! // b) Rewind scanned block information. -//! rewind_to_height(&db_data, rewind_height); +//! rewind_to_height(network, &db_data, rewind_height); //! //! // c) Delete cached blocks from rewind_height onwards. //! // @@ -56,21 +59,18 @@ //! // At this point, the cache and scanned data are locally consistent (though not //! // necessarily consistent with the latest chain tip - this would be discovered the //! // next time this codepath is executed after new blocks are received). -//! scan_cached_blocks(&db_cache, &db_data, None); +//! scan_cached_blocks(&network, &db_cache, &db_data, None); //! ``` use protobuf::parse_from_bytes; use rusqlite::{Connection, NO_PARAMS}; use std::path::Path; -use zcash_primitives::consensus::BlockHeight; +use zcash_primitives::consensus::{self, BlockHeight, NetworkUpgrade}; use zcash_client_backend::proto::compact_formats::CompactBlock; -use crate::{ - error::{Error, ErrorKind}, - SAPLING_ACTIVATION_HEIGHT, -}; +use crate::error::{Error, ErrorKind}; #[derive(Debug)] pub enum ChainInvalidCause { @@ -100,12 +100,16 @@ struct CompactBlockRow { /// - `Err(e)` if there was an error during validation unrelated to chain validity. /// /// This function does not mutate either of the databases. -pub fn validate_combined_chain, Q: AsRef>( +pub fn validate_combined_chain, Q: AsRef>( + parameters: Params, db_cache: P, db_data: Q, ) -> Result<(), Error> { let cache = Connection::open(db_cache)?; let data = Connection::open(db_data)?; + let sapling_activation_height = parameters + .activation_height(NetworkUpgrade::Sapling) + .ok_or(Error(ErrorKind::SaplingNotActive))?; // Recall where we synced up to previously. // If we have never synced, use Sapling activation height to select all cached CompactBlocks. @@ -113,7 +117,7 @@ pub fn validate_combined_chain, Q: AsRef>( data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { row.get(0) .map(|h: u32| (true, h.into())) - .or(Ok((false, SAPLING_ACTIVATION_HEIGHT - 1))) + .or(Ok((false, sapling_activation_height - 1))) })?; // Fetch the CompactBlocks we need to validate @@ -133,8 +137,7 @@ pub fn validate_combined_chain, Q: AsRef>( None => { // No cached blocks, and we've already validated the blocks we've scanned, // so there's nothing to validate. - // TODO: Maybe we still want to check if there are cached blocks that are - // at heights we previously scanned? Check scanning flow again. + // TODO: Maybe we still want to check if there are cached blocks that are at heights we previously scanned? Check scanning flow again. return Ok(()); } }; @@ -189,8 +192,15 @@ pub fn validate_combined_chain, Q: AsRef>( /// /// If the requested height is greater than or equal to the height of the last scanned /// block, this function does nothing. -pub fn rewind_to_height>(db_data: P, height: BlockHeight) -> Result<(), Error> { +pub fn rewind_to_height>( + parameters: Params, + db_data: P, + height: BlockHeight, +) -> Result<(), Error> { let data = Connection::open(db_data)?; + let sapling_activation_height = parameters + .activation_height(NetworkUpgrade::Sapling) + .ok_or(Error(ErrorKind::SaplingNotActive))?; // Recall where we synced up to previously. // If we have never synced, use Sapling activation height. @@ -198,7 +208,7 @@ pub fn rewind_to_height>(db_data: P, height: BlockHeight) -> Resu data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { row.get(0) .map(u32::into) - .or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + .or(Ok(sapling_activation_height - 1)) })?; if height >= last_scanned_height { @@ -240,13 +250,13 @@ mod tests { }; use super::{rewind_to_height, validate_combined_chain}; + use crate::{ error::ErrorKind, init::{init_accounts_table, init_cache_database, init_data_database}, query::get_balance, scan::scan_cached_blocks, - tests::{fake_compact_block, insert_into_cache}, - SAPLING_ACTIVATION_HEIGHT, + tests::{self, fake_compact_block, insert_into_cache, sapling_activation_height}, }; #[test] @@ -262,14 +272,14 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Empty chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); // Create a fake CompactBlock sending value to the address let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), Amount::from_u64(5).unwrap(), @@ -277,17 +287,17 @@ mod tests { insert_into_cache(db_cache, &cb); // Cache-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); // Scan the cache - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); // Create a second fake CompactBlock sending more value to the address let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, + sapling_activation_height() + 1, cb.hash(), extfvk, Amount::from_u64(7).unwrap(), @@ -295,13 +305,13 @@ mod tests { insert_into_cache(db_cache, &cb2); // Data+cache chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); // Scan the cache again - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); } #[test] @@ -317,17 +327,17 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Create some fake CompactBlocks let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), Amount::from_u64(5).unwrap(), ); let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, + sapling_activation_height() + 1, cb.hash(), extfvk.clone(), Amount::from_u64(7).unwrap(), @@ -336,20 +346,20 @@ mod tests { insert_into_cache(db_cache, &cb2); // Scan the cache - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); // Create more fake CompactBlocks that don't connect to the scanned ones let (cb3, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 2, + sapling_activation_height() + 2, BlockHash([1; 32]), extfvk.clone(), Amount::from_u64(8).unwrap(), ); let (cb4, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 3, + sapling_activation_height() + 3, cb3.hash(), extfvk.clone(), Amount::from_u64(3).unwrap(), @@ -358,10 +368,10 @@ mod tests { insert_into_cache(db_cache, &cb4); // Data+cache chain should be invalid at the data/cache boundary - match validate_combined_chain(db_cache, db_data) { + match validate_combined_chain(tests::network(), db_cache, db_data) { Err(e) => match e.kind() { ErrorKind::InvalidChain(upper_bound, _) => { - assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 1) + assert_eq!(*upper_bound, sapling_activation_height() + 1) } _ => panic!(), }, @@ -382,17 +392,17 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Create some fake CompactBlocks let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), Amount::from_u64(5).unwrap(), ); let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, + sapling_activation_height() + 1, cb.hash(), extfvk.clone(), Amount::from_u64(7).unwrap(), @@ -401,20 +411,20 @@ mod tests { insert_into_cache(db_cache, &cb2); // Scan the cache - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Data-only chain should be valid - validate_combined_chain(db_cache, db_data).unwrap(); + validate_combined_chain(tests::network(), db_cache, db_data).unwrap(); // Create more fake CompactBlocks that contain a reorg let (cb3, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 2, + sapling_activation_height() + 2, cb2.hash(), extfvk.clone(), Amount::from_u64(8).unwrap(), ); let (cb4, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 3, + sapling_activation_height() + 3, BlockHash([1; 32]), extfvk.clone(), Amount::from_u64(3).unwrap(), @@ -423,10 +433,10 @@ mod tests { insert_into_cache(db_cache, &cb4); // Data+cache chain should be invalid inside the cache - match validate_combined_chain(db_cache, db_data) { + match validate_combined_chain(tests::network(), db_cache, db_data) { Err(e) => match e.kind() { ErrorKind::InvalidChain(upper_bound, _) => { - assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 2) + assert_eq!(*upper_bound, sapling_activation_height() + 2) } _ => panic!(), }, @@ -447,7 +457,7 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Account balance should be zero assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); @@ -456,35 +466,36 @@ mod tests { let value = Amount::from_u64(5).unwrap(); let value2 = Amount::from_u64(7).unwrap(); let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, ); - let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); + let (cb2, _) = + fake_compact_block(sapling_activation_height() + 1, cb.hash(), extfvk, value2); insert_into_cache(db_cache, &cb); insert_into_cache(db_cache, &cb2); // Scan the cache - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Account balance should reflect both received notes assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); // "Rewind" to height of last scanned block - rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT + 1).unwrap(); + rewind_to_height(tests::network(), db_data, sapling_activation_height() + 1).unwrap(); // Account balance should be unaltered assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); // Rewind so that one block is dropped - rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT).unwrap(); + rewind_to_height(tests::network(), db_data, sapling_activation_height()).unwrap(); // Account balance should only contain the first received note assert_eq!(get_balance(db_data, 0).unwrap(), value); // Scan the cache again - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Account balance should again reflect both received notes assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs index 90a2922ef..bb7a4832c 100644 --- a/zcash_client_sqlite/src/error.rs +++ b/zcash_client_sqlite/src/error.rs @@ -26,6 +26,7 @@ pub enum ErrorKind { Database(rusqlite::Error), Io(std::io::Error), Protobuf(protobuf::ProtobufError), + SaplingNotActive, } #[derive(Debug)] @@ -72,6 +73,7 @@ impl fmt::Display for Error { ErrorKind::Database(e) => write!(f, "{}", e), ErrorKind::Io(e) => write!(f, "{}", e), ErrorKind::Protobuf(e) => write!(f, "{}", e), + ErrorKind::SaplingNotActive => write!(f, "Sapling activation height not specified for network."), } } } diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs index cf6d3f283..498ba4cd6 100644 --- a/zcash_client_sqlite/src/init.rs +++ b/zcash_client_sqlite/src/init.rs @@ -3,12 +3,16 @@ use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::path::Path; use zcash_client_backend::encoding::encode_extended_full_viewing_key; -use zcash_primitives::{block::BlockHash, zip32::ExtendedFullViewingKey}; + +use zcash_primitives::{ + block::BlockHash, + consensus::{self}, + zip32::ExtendedFullViewingKey, +}; use crate::{ address_from_extfvk, error::{Error, ErrorKind}, - HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, }; /// Sets up the internal structure of the cache database. @@ -141,7 +145,10 @@ pub fn init_data_database>(db_data: P) -> Result<(), Error> { /// ``` /// use tempfile::NamedTempFile; /// use zcash_client_sqlite::init::{init_accounts_table, init_data_database}; -/// use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +/// use zcash_primitives::{ +/// consensus::Network, +/// zip32::{ExtendedFullViewingKey, ExtendedSpendingKey} +/// }; /// /// let data_file = NamedTempFile::new().unwrap(); /// let db_data = data_file.path(); @@ -149,14 +156,15 @@ pub fn init_data_database>(db_data: P) -> Result<(), Error> { /// /// let extsk = ExtendedSpendingKey::master(&[]); /// let extfvks = [ExtendedFullViewingKey::from(&extsk)]; -/// init_accounts_table(&db_data, &extfvks).unwrap(); +/// init_accounts_table(&db_data, &Network::TestNetwork, &extfvks).unwrap(); /// ``` /// /// [`get_address`]: crate::query::get_address /// [`scan_cached_blocks`]: crate::scan::scan_cached_blocks /// [`create_to_address`]: crate::transact::create_to_address -pub fn init_accounts_table>( - db_data: P, +pub fn init_accounts_table, P: consensus::Parameters>( + db_data: D, + params: &P, extfvks: &[ExtendedFullViewingKey], ) -> Result<(), Error> { let data = Connection::open(db_data)?; @@ -169,9 +177,11 @@ pub fn init_accounts_table>( // Insert accounts atomically data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; for (account, extfvk) in extfvks.iter().enumerate() { - let address = address_from_extfvk(extfvk); - let extfvk = - encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, extfvk); + let address = address_from_extfvk(params, extfvk); + let extfvk = encode_extended_full_viewing_key( + params.hrp_sapling_extended_full_viewing_key(), + extfvk, + ); data.execute( "INSERT INTO accounts (account, extfvk, address) VALUES (?, ?, ?)", @@ -244,11 +254,15 @@ mod tests { use zcash_client_backend::encoding::decode_payment_address; use zcash_primitives::{ block::BlockHash, + consensus::Parameters, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; use super::{init_accounts_table, init_blocks_table, init_data_database}; - use crate::{query::get_address, HRP_SAPLING_PAYMENT_ADDRESS}; + use crate::{ + query::get_address, + tests::{self}, + }; #[test] fn init_accounts_table_only_works_once() { @@ -257,18 +271,18 @@ mod tests { init_data_database(&db_data).unwrap(); // We can call the function as many times as we want with no data - init_accounts_table(&db_data, &[]).unwrap(); - init_accounts_table(&db_data, &[]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[]).unwrap(); // First call with data should initialise the accounts table let extfvks = [ExtendedFullViewingKey::from(&ExtendedSpendingKey::master( &[], ))]; - init_accounts_table(&db_data, &extfvks).unwrap(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap(); // Subsequent calls should return an error - init_accounts_table(&db_data, &[]).unwrap_err(); - init_accounts_table(&db_data, &extfvks).unwrap_err(); + init_accounts_table(&db_data, &tests::network(), &[]).unwrap_err(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap_err(); } #[test] @@ -293,11 +307,12 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap(); // The account's address should be in the data DB let addr = get_address(&db_data, 0).unwrap(); - let pa = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr).unwrap(); + let pa = + decode_payment_address(tests::network().hrp_sapling_payment_address(), &addr).unwrap(); assert_eq!(pa.unwrap(), extsk.default_address().unwrap().1); } } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 2d451e12a..90ac5bdf7 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -26,23 +26,13 @@ use rusqlite::{Connection, NO_PARAMS}; use std::cmp; + +use zcash_primitives::{ + consensus::{self, BlockHeight}, + zip32::ExtendedFullViewingKey, +}; + use zcash_client_backend::encoding::encode_payment_address; -use zcash_primitives::{consensus::BlockHeight, zip32::ExtendedFullViewingKey}; - -#[cfg(feature = "mainnet")] -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; @@ -54,15 +44,12 @@ pub mod transact; const ANCHOR_OFFSET: u32 = 10; -#[cfg(feature = "mainnet")] -const SAPLING_ACTIVATION_HEIGHT: BlockHeight = BlockHeight::from_u32(419_200); - -#[cfg(not(feature = "mainnet"))] -const SAPLING_ACTIVATION_HEIGHT: BlockHeight = BlockHeight::from_u32(280_000); - -fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { +fn address_from_extfvk( + params: &P, + extfvk: &ExtendedFullViewingKey, +) -> String { let addr = extfvk.default_address().unwrap().1; - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr) + encode_payment_address(params.hrp_sapling_payment_address(), &addr) } /// Determines the target height for a transaction, and the height from which to @@ -99,19 +86,20 @@ fn get_target_and_anchor_heights( #[cfg(test)] mod tests { - use crate::Network; use ff::PrimeField; use group::GroupEncoding; use protobuf::Message; use rand_core::{OsRng, RngCore}; use rusqlite::{types::ToSql, Connection}; use std::path::Path; + use zcash_client_backend::proto::compact_formats::{ CompactBlock, CompactOutput, CompactSpend, CompactTx, }; + use zcash_primitives::{ block::BlockHash, - consensus::BlockHeight, + consensus::{BlockHeight, Network, NetworkUpgrade, Parameters}, note_encryption::{Memo, SaplingNoteEncryption}, primitives::{Note, PaymentAddress}, transaction::components::Amount, @@ -119,6 +107,30 @@ mod tests { zip32::ExtendedFullViewingKey, }; + #[cfg(feature = "mainnet")] + pub(crate) fn network() -> Network { + Network::MainNetwork + } + + #[cfg(not(feature = "mainnet"))] + pub(crate) fn network() -> Network { + Network::TestNetwork + } + + #[cfg(feature = "mainnet")] + pub(crate) fn sapling_activation_height() -> BlockHeight { + Network::MainNetwork + .activation_height(NetworkUpgrade::Sapling) + .unwrap() + } + + #[cfg(not(feature = "mainnet"))] + pub(crate) fn sapling_activation_height() -> BlockHeight { + Network::TestNetwork + .activation_height(NetworkUpgrade::Sapling) + .unwrap() + } + /// Create a fake CompactBlock at the given height, containing a single output paying /// the given address. Returns the CompactBlock and the nullifier for the new note. pub(crate) fn fake_compact_block( @@ -131,7 +143,7 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; - let rseed = generate_random_rseed::(height, &mut rng); + let rseed = generate_random_rseed(&network(), height, &mut rng); let note = Note { g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), @@ -179,7 +191,7 @@ mod tests { value: Amount, ) -> CompactBlock { let mut rng = OsRng; - let rseed = generate_random_rseed::(height, &mut rng); + let rseed = generate_random_rseed(&network(), height, &mut rng); // Create a fake CompactBlock containing the note let mut cspend = CompactSpend::new(); @@ -219,7 +231,7 @@ 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::(height, &mut rng); + let rseed = generate_random_rseed(&network(), height, &mut rng); let note = Note { g_d: change_addr.diversifier().g_d().unwrap(), pk_d: change_addr.pk_d().clone(), diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs index 4bf478dc5..80185da6e 100644 --- a/zcash_client_sqlite/src/query.rs +++ b/zcash_client_sqlite/src/query.rs @@ -176,6 +176,7 @@ mod tests { use crate::{ error::ErrorKind, init::{init_accounts_table, init_data_database}, + tests::{self}, }; #[test] @@ -187,7 +188,7 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap(); // The account should be empty assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index fe49de97b..fc108d892 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -11,7 +11,7 @@ use zcash_client_backend::{ proto::compact_formats::CompactBlock, welding_rig::scan_block, }; use zcash_primitives::{ - consensus::BlockHeight, + consensus::{self, BlockHeight, NetworkUpgrade}, merkle_tree::{CommitmentTree, IncrementalWitness}, sapling::Node, transaction::Transaction, @@ -20,7 +20,6 @@ use zcash_primitives::{ use crate::{ address::RecipientAddress, error::{Error, ErrorKind}, - Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, }; struct CompactBlockRow { @@ -57,27 +56,36 @@ struct WitnessRow { /// # Examples /// /// ``` +/// use zcash_primitives::consensus::{ +/// Network, +/// Parameters, +/// }; /// use zcash_client_sqlite::scan::scan_cached_blocks; /// -/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db", None); +/// scan_cached_blocks(&Network::TestNetwork, "/path/to/cache.db", "/path/to/data.db", None); /// ``` /// /// [`init_blocks_table`]: crate::init::init_blocks_table -pub fn scan_cached_blocks, Q: AsRef>( +pub fn scan_cached_blocks, Q: AsRef>( + params: &Params, db_cache: P, db_data: Q, limit: Option, ) -> Result<(), Error> { let cache = Connection::open(db_cache)?; let data = Connection::open(db_data)?; + let sapling_activation_height = params + .activation_height(NetworkUpgrade::Sapling) + .ok_or(Error(ErrorKind::SaplingNotActive))?; // Recall where we synced up to previously. // If we have never synced, use sapling activation height to select all cached CompactBlocks. - let mut last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { - row.get::<_, u32>(0) - .map(BlockHeight::from) - .or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) - })?; + let mut last_height: BlockHeight = + data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get::<_, u32>(0) + .map(BlockHeight::from) + .or(Ok(sapling_activation_height - 1)) + })?; // Fetch the CompactBlocks we need to scan let mut stmt_blocks = cache.prepare( @@ -98,7 +106,10 @@ pub fn scan_cached_blocks, Q: AsRef>( data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { row.get(0).map(|extfvk: String| { - decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + decode_extended_full_viewing_key( + params.hrp_sapling_extended_full_viewing_key(), + &extfvk, + ) }) })?; // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. @@ -194,7 +205,8 @@ pub fn scan_cached_blocks, Q: AsRef>( 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( + params, block, &extfvks[..], &nf_refs, @@ -359,8 +371,9 @@ pub fn scan_cached_blocks, Q: AsRef>( /// Scans a [`Transaction`] for any information that can be decrypted by the accounts in /// the wallet, and saves it to the wallet. -pub fn decrypt_and_store_transaction>( - db_data: P, +pub fn decrypt_and_store_transaction, P: consensus::Parameters>( + db_data: D, + params: &P, tx: &Transaction, ) -> Result<(), Error> { let data = Connection::open(db_data)?; @@ -370,7 +383,10 @@ pub fn decrypt_and_store_transaction>( data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { row.get(0).map(|extfvk: String| { - decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + decode_extended_full_viewing_key( + params.hrp_sapling_extended_full_viewing_key(), + &extfvk, + ) }) })?; // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. @@ -393,10 +409,11 @@ pub fn decrypt_and_store_transaction>( }) .optional()? .map(|last_height: u32| BlockHeight::from(last_height + 1)) - .unwrap_or(SAPLING_ACTIVATION_HEIGHT), + .and(params.activation_height(NetworkUpgrade::Sapling)) + .ok_or(Error(ErrorKind::SaplingNotActive))?, }; - let outputs = decrypt_transaction::(height, tx, &extfvks); + let outputs = decrypt_transaction(params, height, tx, &extfvks); if outputs.is_empty() { // Nothing to see here @@ -462,7 +479,7 @@ pub fn decrypt_and_store_transaction>( let value = output.note.value as i64; if output.outgoing { - let to_str = RecipientAddress::from(output.to).to_string(); + let to_str = RecipientAddress::from(output.to).to_string(params); // Try updating an existing sent note. if stmt_update_sent_note.execute(&[ @@ -530,8 +547,10 @@ mod tests { use crate::{ init::{init_accounts_table, init_cache_database, init_data_database}, query::get_balance, - tests::{fake_compact_block, fake_compact_block_spending, insert_into_cache}, - SAPLING_ACTIVATION_HEIGHT, + tests::{ + self, fake_compact_block, fake_compact_block_spending, insert_into_cache, + sapling_activation_height, + }, }; #[test] @@ -547,49 +566,49 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Create a block with height SAPLING_ACTIVATION_HEIGHT let value = Amount::from_u64(50000).unwrap(); let (cb1, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb1); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); assert_eq!(get_balance(db_data, 0).unwrap(), value); // We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next let (cb2, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, + sapling_activation_height() + 1, cb1.hash(), extfvk.clone(), value, ); let (cb3, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 2, + sapling_activation_height() + 2, cb2.hash(), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb3); - match scan_cached_blocks(db_cache, db_data, None) { + match scan_cached_blocks(&tests::network(), db_cache, db_data, None) { Ok(_) => panic!("Should have failed"), Err(e) => assert_eq!( e.to_string(), format!( "Expected height of next CompactBlock to be {}, but was {}", - SAPLING_ACTIVATION_HEIGHT + 1, - SAPLING_ACTIVATION_HEIGHT + 2 + sapling_activation_height() + 1, + sapling_activation_height() + 2 ) ), } // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both insert_into_cache(db_cache, &cb2); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); assert_eq!( get_balance(db_data, 0).unwrap(), Amount::from_u64(150_000).unwrap() @@ -609,7 +628,7 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Account balance should be zero assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); @@ -617,7 +636,7 @@ mod tests { // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, @@ -625,18 +644,19 @@ mod tests { insert_into_cache(db_cache, &cb); // Scan the cache - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Account balance should reflect the received note assert_eq!(get_balance(db_data, 0).unwrap(), value); // Create a second fake CompactBlock sending more value to the address let value2 = Amount::from_u64(7).unwrap(); - let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); + let (cb2, _) = + fake_compact_block(sapling_activation_height() + 1, cb.hash(), extfvk, value2); insert_into_cache(db_cache, &cb2); // Scan the cache again - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Account balance should reflect both received notes assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); @@ -655,7 +675,7 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Account balance should be zero assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); @@ -663,7 +683,7 @@ mod tests { // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); let (cb, nf) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, @@ -671,7 +691,7 @@ mod tests { insert_into_cache(db_cache, &cb); // Scan the cache - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Account balance should reflect the received note assert_eq!(get_balance(db_data, 0).unwrap(), value); @@ -683,7 +703,7 @@ mod tests { insert_into_cache( db_cache, &fake_compact_block_spending( - SAPLING_ACTIVATION_HEIGHT + 1, + sapling_activation_height() + 1, cb.hash(), (nf, value), extfvk, @@ -693,7 +713,7 @@ mod tests { ); // Scan the cache again - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Account balance should equal the change assert_eq!(get_balance(db_data, 0).unwrap(), value - value2); diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 03a17e33a..07ee510eb 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -1,13 +1,12 @@ //! Functions for creating transactions. use ff::PrimeField; -use rand_core::OsRng; use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::convert::TryInto; use std::path::Path; use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ - consensus::{self, NetworkUpgrade, Parameters}, + consensus::{self, NetworkUpgrade}, keys::OutgoingViewingKey, merkle_tree::{IncrementalWitness, MerklePath}, note_encryption::Memo, @@ -24,7 +23,7 @@ use zcash_primitives::{ use crate::{ address::RecipientAddress, error::{Error, ErrorKind}, - get_target_and_anchor_heights, Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + get_target_and_anchor_heights, }; /// Describes a policy for which outgoing viewing key should be able to decrypt @@ -88,13 +87,16 @@ struct SelectedNoteRow { /// # Examples /// /// ``` -/// use zcash_client_backend::{ +/// use zcash_primitives::{ +/// consensus::{self, Network}, /// constants::testnet::COIN_TYPE, +/// transaction::components::Amount +/// }; +/// use zcash_proofs::prover::LocalTxProver; +/// use zcash_client_backend::{ /// keys::spending_key, /// }; /// use zcash_client_sqlite::transact::{create_to_address, OvkPolicy}; -/// use zcash_primitives::{consensus, transaction::components::Amount}; -/// use zcash_proofs::prover::LocalTxProver; /// /// let tx_prover = match LocalTxProver::with_default_location() { /// Some(tx_prover) => tx_prover, @@ -108,6 +110,7 @@ struct SelectedNoteRow { /// let to = extsk.default_address().unwrap().1.into(); /// match create_to_address( /// "/path/to/data.db", +/// &Network::TestNetwork, /// consensus::BranchId::Sapling, /// tx_prover, /// (account, &extsk), @@ -120,8 +123,9 @@ struct SelectedNoteRow { /// Err(e) => (), /// } /// ``` -pub fn create_to_address>( - db_data: P, +pub fn create_to_address, P: consensus::Parameters + Clone>( + db_data: DB, + params: &P, consensus_branch_id: consensus::BranchId, prover: impl TxProver, (account, extsk): (u32, &ExtendedSpendingKey), @@ -139,8 +143,11 @@ pub fn create_to_address>( .prepare("SELECT * FROM accounts WHERE account = ? AND extfvk = ?")? .exists(&[ account.to_sql()?, - encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) - .to_sql()?, + encode_extended_full_viewing_key( + params.hrp_sapling_extended_full_viewing_key(), + &extfvk, + ) + .to_sql()?, ])? { return Err(Error(ErrorKind::InvalidExtSK(account))); @@ -224,7 +231,7 @@ pub fn create_to_address>( let rseed = { let d: Vec<_> = row.get(2)?; - if Network::is_nu_active(NetworkUpgrade::Canopy, height) { + if params.is_nu_active(NetworkUpgrade::Canopy, height) { let mut r = [0u8; 32]; r.copy_from_slice(&d[..]); Rseed::AfterZip212(r) @@ -270,7 +277,7 @@ pub fn create_to_address>( } // Create the transaction - let mut builder = Builder::::new(height); + let mut builder = Builder::new(params.clone(), height); for selected in notes { builder.add_sapling_spend( extsk.clone(), @@ -327,7 +334,7 @@ pub fn create_to_address>( // Save the sent note in the database. // TODO: Decide how to save transparent output information. - let to_str = to.to_string(); + let to_str = to.to_string(params); if let Some(memo) = memo { let mut stmt_insert_sent_note = data.prepare( "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) @@ -366,25 +373,27 @@ mod tests { use group::cofactor::CofactorGroup; use rusqlite::Connection; use tempfile::NamedTempFile; + use zcash_primitives::{ block::BlockHash, - consensus, + consensus::{self}, note_encryption::try_sapling_output_recovery, prover::TxProver, transaction::{components::Amount, Transaction}, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; + use zcash_proofs::prover::LocalTxProver; - use super::{create_to_address, OvkPolicy}; use crate::{ init::{init_accounts_table, init_blocks_table, init_cache_database, init_data_database}, query::{get_balance, get_verified_balance}, scan::scan_cached_blocks, - tests::{fake_compact_block, insert_into_cache}, - Network, SAPLING_ACTIVATION_HEIGHT, + tests::{self, fake_compact_block, insert_into_cache, sapling_activation_height}, }; + use super::{create_to_address, OvkPolicy}; + fn test_prover() -> impl TxProver { match LocalTxProver::with_default_location() { Some(tx_prover) => tx_prover, @@ -407,12 +416,13 @@ mod tests { ExtendedFullViewingKey::from(&extsk0), ExtendedFullViewingKey::from(&extsk1), ]; - init_accounts_table(&db_data, &extfvks).unwrap(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap(); let to = extsk0.default_address().unwrap().1.into(); // Invalid extsk for the given account should cause an error match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk1), @@ -426,6 +436,7 @@ mod tests { } match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (1, &extsk0), @@ -448,12 +459,13 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap(); let to = extsk.default_address().unwrap().1.into(); // We cannot do anything if we aren't synchronised match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -477,7 +489,7 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvks = [ExtendedFullViewingKey::from(&extsk)]; - init_accounts_table(&db_data, &extfvks).unwrap(); + init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap(); let to = extsk.default_address().unwrap().1.into(); // Account balance should be zero @@ -486,6 +498,7 @@ mod tests { // We cannot spend anything match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -515,18 +528,18 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Verified balance matches total balance assert_eq!(get_balance(db_data, 0).unwrap(), value); @@ -534,13 +547,13 @@ mod tests { // Add more funds to the wallet in a second note let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 1, + sapling_activation_height() + 1, cb.hash(), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Verified balance does not include the second note assert_eq!(get_balance(db_data, 0).unwrap(), value + value); @@ -551,6 +564,7 @@ mod tests { let to = extsk2.default_address().unwrap().1.into(); match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -570,18 +584,19 @@ mod tests { // note is verified for i in 2..10 { let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + i, + sapling_activation_height() + i, cb.hash(), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb); } - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Second spend still fails match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -599,17 +614,18 @@ mod tests { // Mine block 11 so that the second note becomes verified let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 10, + sapling_activation_height() + 10, cb.hash(), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Second spend should now succeed create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -634,18 +650,18 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &tests::network(), &[extfvk.clone()]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); assert_eq!(get_balance(db_data, 0).unwrap(), value); // Send some of the funds to another address @@ -653,6 +669,7 @@ mod tests { let to = extsk2.default_address().unwrap().1.into(); create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -666,6 +683,7 @@ mod tests { // A second spend fails because there are no usable notes match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -685,18 +703,19 @@ mod tests { // until just before the first transaction expires for i in 1..22 { let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + i, + sapling_activation_height() + i, cb.hash(), ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), value, ); insert_into_cache(db_cache, &cb); } - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Second spend still fails match create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -714,17 +733,18 @@ mod tests { // Mine block SAPLING_ACTIVATION_HEIGHT + 22 so that the first transaction expires let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + 22, + sapling_activation_height() + 22, cb.hash(), ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])), value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&tests::network(), db_cache, db_data, None).unwrap(); // Second spend should now succeed create_to_address( db_data, + &tests::network(), consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -738,6 +758,7 @@ mod tests { #[test] fn ovk_policy_prevents_recovery_from_chain() { + let network = tests::network(); let cache_file = NamedTempFile::new().unwrap(); let db_cache = cache_file.path(); init_cache_database(&db_cache).unwrap(); @@ -749,18 +770,18 @@ mod tests { // Add an account to the wallet let extsk = ExtendedSpendingKey::master(&[]); let extfvk = ExtendedFullViewingKey::from(&extsk); - init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + init_accounts_table(&db_data, &network, &[extfvk.clone()]).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT, + sapling_activation_height(), BlockHash([0; 32]), extfvk.clone(), value, ); insert_into_cache(db_cache, &cb); - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&network, db_cache, db_data, None).unwrap(); assert_eq!(get_balance(db_data, 0).unwrap(), value); let extsk2 = ExtendedSpendingKey::master(&[]); @@ -770,6 +791,7 @@ mod tests { let send_and_recover_with_policy = |ovk_policy| { let tx_row = create_to_address( db_data, + &network, consensus::BranchId::Blossom, test_prover(), (0, &extsk), @@ -804,8 +826,9 @@ mod tests { .unwrap(); let output = &tx.shielded_outputs[output_index as usize]; - try_sapling_output_recovery::( - SAPLING_ACTIVATION_HEIGHT, + try_sapling_output_recovery( + &network, + sapling_activation_height(), &extfvk.fvk.ovk, &output.cv, &output.cmu, @@ -824,14 +847,14 @@ mod tests { // so that the first transaction expires for i in 1..=22 { let (cb, _) = fake_compact_block( - SAPLING_ACTIVATION_HEIGHT + i, + sapling_activation_height() + i, cb.hash(), ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), value, ); insert_into_cache(db_cache, &cb); } - scan_cached_blocks(db_cache, db_data, None).unwrap(); + scan_cached_blocks(&network, db_cache, db_data, None).unwrap(); // Send the funds again, discarding history. // Neither transaction output is decryptable by the sender. diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 957ef9fd5..9c3e6ecd6 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -5,6 +5,8 @@ use std::convert::TryFrom; use std::fmt; use std::ops::{Add, Sub}; +use crate::constants; + #[repr(transparent)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BlockHeight(u32); @@ -119,10 +121,18 @@ impl Sub for BlockHeight { /// Zcash consensus parameters. pub trait Parameters { - fn activation_height(nu: NetworkUpgrade) -> Option; + fn activation_height(&self, nu: NetworkUpgrade) -> Option; - fn is_nu_active(nu: NetworkUpgrade, height: BlockHeight) -> bool { - match Self::activation_height(nu) { + fn hrp_sapling_extended_full_viewing_key(&self) -> &str; + + fn hrp_sapling_payment_address(&self) -> &str; + + fn b58_pubkey_address_prefix(&self) -> [u8; 2]; + + fn b58_script_address_prefix(&self) -> [u8; 2]; + + fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool { + match self.activation_height(nu) { Some(h) if h <= height => true, _ => false, } @@ -131,32 +141,60 @@ pub trait Parameters { /// Marker struct for the production network. #[derive(Clone, Copy, Debug)] -pub struct MainNetwork; - -impl Parameters for MainNetwork { - fn activation_height(nu: NetworkUpgrade) -> Option { - match nu { - NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)), - NetworkUpgrade::Sapling => Some(BlockHeight(419_200)), - NetworkUpgrade::Blossom => Some(BlockHeight(653_600)), - NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)), - NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)), - } - } +pub enum Network { + MainNetwork, + TestNetwork, } -/// Marker struct for the test network. -#[derive(Clone, Copy, Debug)] -pub struct TestNetwork; +impl Parameters for Network { + fn activation_height(&self, nu: NetworkUpgrade) -> Option { + match self { + Network::MainNetwork => match nu { + NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)), + NetworkUpgrade::Sapling => Some(BlockHeight(419_200)), + NetworkUpgrade::Blossom => Some(BlockHeight(653_600)), + NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)), + NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)), + }, + Network::TestNetwork => match nu { + NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)), + NetworkUpgrade::Sapling => Some(BlockHeight(280_000)), + NetworkUpgrade::Blossom => Some(BlockHeight(584_000)), + NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)), + NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)), + }, + } + } -impl Parameters for TestNetwork { - fn activation_height(nu: NetworkUpgrade) -> Option { - match nu { - NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)), - NetworkUpgrade::Sapling => Some(BlockHeight(280_000)), - NetworkUpgrade::Blossom => Some(BlockHeight(584_000)), - NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)), - NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)), + fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + match self { + Network::MainNetwork => constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + + Network::TestNetwork => constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + } + } + + fn hrp_sapling_payment_address(&self) -> &str { + match self { + Network::MainNetwork => constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, + + Network::TestNetwork => constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, + } + } + + fn b58_pubkey_address_prefix(&self) -> [u8; 2] { + match self { + Network::MainNetwork => constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX, + + Network::TestNetwork => constants::testnet::B58_PUBKEY_ADDRESS_PREFIX, + } + } + + fn b58_script_address_prefix(&self) -> [u8; 2] { + match self { + Network::MainNetwork => constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX, + + Network::TestNetwork => constants::testnet::B58_SCRIPT_ADDRESS_PREFIX, } } } @@ -290,9 +328,9 @@ impl BranchId { /// the given height. /// /// This is the branch ID that should be used when creating transactions. - pub fn for_height(height: BlockHeight) -> Self { + pub fn for_height(parameters: &P, height: BlockHeight) -> Self { 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(); } } @@ -306,9 +344,7 @@ impl BranchId { mod tests { use std::convert::TryFrom; - use super::{ - BlockHeight, BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER, - }; + use super::{BlockHeight, BranchId, Network, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER}; #[test] fn nu_ordering() { @@ -316,8 +352,8 @@ mod tests { let nu_a = UPGRADES_IN_ORDER[i - 1]; let nu_b = UPGRADES_IN_ORDER[i]; match ( - MainNetwork::activation_height(nu_a), - MainNetwork::activation_height(nu_b), + Network::MainNetwork.activation_height(nu_a), + Network::MainNetwork.activation_height(nu_b), ) { (Some(a), Some(b)) if a < b => (), (Some(_), None) => (), @@ -332,18 +368,11 @@ mod tests { #[test] fn nu_is_active() { - assert!(!MainNetwork::is_nu_active( - NetworkUpgrade::Overwinter, - BlockHeight(0) - )); - assert!(!MainNetwork::is_nu_active( - NetworkUpgrade::Overwinter, - BlockHeight(347_499) - )); - assert!(MainNetwork::is_nu_active( - NetworkUpgrade::Overwinter, - BlockHeight(347_500) - )); + assert!(!Network::MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(0))); + assert!( + !Network::MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_499)) + ); + assert!(Network::MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_500))); } #[test] @@ -355,27 +384,27 @@ mod tests { #[test] fn branch_id_for_height() { assert_eq!( - BranchId::for_height::(BlockHeight(0)), + BranchId::for_height(&Network::MainNetwork, BlockHeight(0)), BranchId::Sprout, ); assert_eq!( - BranchId::for_height::(BlockHeight(419_199)), + BranchId::for_height(&Network::MainNetwork, BlockHeight(419_199)), BranchId::Overwinter, ); assert_eq!( - BranchId::for_height::(BlockHeight(419_200)), + BranchId::for_height(&Network::MainNetwork, BlockHeight(419_200)), BranchId::Sapling, ); assert_eq!( - BranchId::for_height::(BlockHeight(903_000)), + BranchId::for_height(&Network::MainNetwork, BlockHeight(903_000)), BranchId::Heartwood, ); assert_eq!( - BranchId::for_height::(BlockHeight(1_046_400)), + BranchId::for_height(&Network::MainNetwork, BlockHeight(1_046_400)), BranchId::Canopy, ); assert_eq!( - BranchId::for_height::(BlockHeight(5_000_000)), + BranchId::for_height(&Network::MainNetwork, BlockHeight(5_000_000)), BranchId::Canopy, ); } diff --git a/zcash_primitives/src/constants.rs b/zcash_primitives/src/constants.rs index ce8965af7..b51d04c2f 100644 --- a/zcash_primitives/src/constants.rs +++ b/zcash_primitives/src/constants.rs @@ -5,6 +5,10 @@ use group::Group; use jubjub::SubgroupPoint; use lazy_static::lazy_static; +pub mod mainnet; +pub mod regtest; +pub mod testnet; + /// First 64 bytes of the BLAKE2s input during group hash. /// This is chosen to be some random string that we couldn't have anticipated when we designed /// the algorithm, for rigidity purposes. diff --git a/zcash_client_backend/src/constants/mainnet.rs b/zcash_primitives/src/constants/mainnet.rs similarity index 100% rename from zcash_client_backend/src/constants/mainnet.rs rename to zcash_primitives/src/constants/mainnet.rs diff --git a/zcash_client_backend/src/constants/regtest.rs b/zcash_primitives/src/constants/regtest.rs similarity index 100% rename from zcash_client_backend/src/constants/regtest.rs rename to zcash_primitives/src/constants/regtest.rs diff --git a/zcash_client_backend/src/constants/testnet.rs b/zcash_primitives/src/constants/testnet.rs similarity index 100% rename from zcash_client_backend/src/constants/testnet.rs rename to zcash_primitives/src/constants/testnet.rs diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index b40e9d820..e7a2c67e9 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -359,6 +359,7 @@ impl SaplingNoteEncryption { } fn parse_note_plaintext_without_memo( + params: &P, height: BlockHeight, ivk: &jubjub::Fr, epk: &jubjub::SubgroupPoint, @@ -366,7 +367,7 @@ fn parse_note_plaintext_without_memo( plaintext: &[u8], ) -> Option<(Note, PaymentAddress)> { // Check note plaintext version - if !plaintext_version_is_valid::

(height, plaintext[0]) { + if !plaintext_version_is_valid(params, height, plaintext[0]) { return None; } @@ -407,11 +408,13 @@ fn parse_note_plaintext_without_memo( } pub fn plaintext_version_is_valid( + params: &P, height: BlockHeight, leadbyte: u8, ) -> bool { - if P::is_nu_active(NetworkUpgrade::Canopy, height) { - let grace_period_end_height = P::activation_height(NetworkUpgrade::Canopy) + if params.is_nu_active(NetworkUpgrade::Canopy, height) { + let grace_period_end_height = params + .activation_height(NetworkUpgrade::Canopy) .expect("Should have Canopy activation height") + ZIP212_GRACE_PERIOD; @@ -438,6 +441,7 @@ pub fn plaintext_version_is_valid( /// /// Implements section 4.17.2 of the Zcash Protocol Specification. pub fn try_sapling_note_decryption( + params: &P, height: BlockHeight, ivk: &jubjub::Fr, epk: &jubjub::SubgroupPoint, @@ -463,7 +467,7 @@ pub fn try_sapling_note_decryption( NOTE_PLAINTEXT_SIZE ); - let (note, to) = parse_note_plaintext_without_memo::

(height, ivk, epk, cmu, &plaintext)?; + let (note, to) = parse_note_plaintext_without_memo(params, height, ivk, epk, cmu, &plaintext)?; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -481,6 +485,7 @@ pub fn try_sapling_note_decryption( /// /// [`ZIP 307`]: https://zips.z.cash/zip-0307 pub fn try_sapling_compact_note_decryption( + params: &P, height: BlockHeight, ivk: &jubjub::Fr, epk: &jubjub::SubgroupPoint, @@ -497,7 +502,7 @@ pub fn try_sapling_compact_note_decryption( plaintext.copy_from_slice(&enc_ciphertext); ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext); - parse_note_plaintext_without_memo::

(height, ivk, epk, cmu, &plaintext) + parse_note_plaintext_without_memo(params, height, ivk, epk, cmu, &plaintext) } /// Recovery of the full note plaintext by the sender. @@ -509,6 +514,7 @@ pub fn try_sapling_compact_note_decryption( /// Implements part of section 4.17.3 of the Zcash Protocol Specification. /// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`]. pub fn try_sapling_output_recovery_with_ock( + params: &P, height: BlockHeight, ock: &OutgoingCipherKey, cmu: &bls12_381::Scalar, @@ -561,7 +567,7 @@ pub fn try_sapling_output_recovery_with_ock( ); // Check note plaintext version - if !plaintext_version_is_valid::

(height, plaintext[0]) { + if !plaintext_version_is_valid(params, height, plaintext[0]) { return None; } @@ -615,6 +621,7 @@ pub fn try_sapling_output_recovery_with_ock( /// /// Implements section 4.17.3 of the Zcash Protocol Specification. pub fn try_sapling_output_recovery( + params: &P, height: BlockHeight, ovk: &OutgoingViewingKey, cv: &jubjub::ExtendedPoint, @@ -624,6 +631,7 @@ pub fn try_sapling_output_recovery( out_ciphertext: &[u8], ) -> Option<(Note, PaymentAddress, Memo)> { try_sapling_output_recovery_with_ock::

( + params, height, &prf_ock(&ovk, &cv, &cmu, &epk), cmu, @@ -651,12 +659,20 @@ mod tests { COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE, }; + use crate::{ consensus::{ - BlockHeight, NetworkUpgrade, + BlockHeight, Network, + Network::TestNetwork, + NetworkUpgrade, NetworkUpgrade::{Canopy, Sapling}, - Parameters, TestNetwork, ZIP212_GRACE_PERIOD, + Parameters, ZIP212_GRACE_PERIOD, }, + // jubjub::{ + // edwards, + // fs::{Fs, FsRepr}, + // PrimeOrder, Unknown, + // }, keys::OutgoingViewingKey, primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, util::generate_random_rseed, @@ -796,7 +812,8 @@ mod tests { let (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext_with(height, ivk, rng); - assert!(try_sapling_note_decryption::( + assert!(try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -804,7 +821,8 @@ mod tests { &enc_ciphertext ) .is_some()); - assert!(try_sapling_compact_note_decryption::( + assert!(try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -813,7 +831,8 @@ mod tests { ) .is_some()); - let ovk_output_recovery = try_sapling_output_recovery::( + let ovk_output_recovery = try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -822,7 +841,9 @@ mod tests { &enc_ciphertext, &out_ciphertext, ); - let ock_output_recovery = try_sapling_output_recovery_with_ock::( + + let ock_output_recovery = try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -863,7 +884,7 @@ mod tests { }; let cv = value_commitment.commitment().into(); - let rseed = generate_random_rseed::(height, &mut rng); + let rseed = generate_random_rseed(&Network::TestNetwork, height, &mut rng); let note = pa.create_note(value, rseed).unwrap(); let cmu = note.cmu(); @@ -965,15 +986,16 @@ mod tests { fn decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { let (_, _, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &jubjub::Fr::random(&mut rng), &epk, @@ -989,15 +1011,16 @@ mod tests { fn decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { let (_, _, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &jubjub::SubgroupPoint::random(&mut rng), @@ -1013,15 +1036,16 @@ mod tests { fn decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { let (_, _, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1037,8 +1061,8 @@ mod tests { fn decryption_with_invalid_tag() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1047,7 +1071,8 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1062,7 +1087,7 @@ mod tests { #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; - let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); + let canopy_activation_height = TestNetwork.activation_height(Canopy).unwrap(); let heights = [ canopy_activation_height - 1, canopy_activation_height, @@ -1084,7 +1109,8 @@ mod tests { |pt| pt[0] = leadbyte, ); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1100,8 +1126,8 @@ mod tests { fn decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1118,7 +1144,8 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1134,8 +1161,8 @@ mod tests { fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1151,8 +1178,10 @@ mod tests { &out_ciphertext, |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); + assert_eq!( - try_sapling_note_decryption::( + try_sapling_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1168,15 +1197,16 @@ mod tests { fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { let (_, _, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &jubjub::Fr::random(&mut rng), &epk, @@ -1192,15 +1222,16 @@ mod tests { fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { let (_, _, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &jubjub::SubgroupPoint::random(&mut rng), @@ -1216,15 +1247,16 @@ mod tests { fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { let (_, _, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1239,7 +1271,7 @@ mod tests { #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; - let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); + let canopy_activation_height = TestNetwork.activation_height(Canopy).unwrap(); let heights = [ canopy_activation_height - 1, canopy_activation_height, @@ -1261,7 +1293,8 @@ mod tests { |pt| pt[0] = leadbyte, ); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1277,8 +1310,8 @@ mod tests { fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1295,7 +1328,8 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1311,8 +1345,8 @@ mod tests { fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1329,7 +1363,8 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_compact_note_decryption::( + try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1345,8 +1380,8 @@ mod tests { fn recovery_with_invalid_ovk() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1355,7 +1390,8 @@ mod tests { ovk.0[0] ^= 0xff; assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1373,8 +1409,8 @@ mod tests { fn recovery_with_invalid_ock() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1382,7 +1418,8 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &OutgoingCipherKey([0u8; 32]), &cmu, @@ -1399,8 +1436,8 @@ mod tests { fn recovery_with_invalid_cv() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1408,7 +1445,8 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &jubjub::ExtendedPoint::random(&mut rng), @@ -1426,8 +1464,8 @@ mod tests { fn recovery_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1435,7 +1473,8 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1446,8 +1485,10 @@ mod tests { ), None ); + assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &bls12_381::Scalar::random(&mut rng), @@ -1464,8 +1505,8 @@ mod tests { fn recovery_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1473,7 +1514,8 @@ mod tests { random_enc_ciphertext(height, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1484,8 +1526,10 @@ mod tests { ), None ); + assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1502,8 +1546,8 @@ mod tests { fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1512,7 +1556,8 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1524,7 +1569,8 @@ mod tests { None ); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1541,8 +1587,8 @@ mod tests { fn recovery_with_invalid_out_tag() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1551,7 +1597,8 @@ mod tests { out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1563,7 +1610,8 @@ mod tests { None ); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1579,7 +1627,7 @@ mod tests { #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; - let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); + let canopy_activation_height = TestNetwork.activation_height(Canopy).unwrap(); let heights = [ canopy_activation_height - 1, canopy_activation_height, @@ -1601,7 +1649,8 @@ mod tests { |pt| pt[0] = leadbyte, ); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1613,7 +1662,8 @@ mod tests { None ); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1630,8 +1680,8 @@ mod tests { fn recovery_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1648,7 +1698,8 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), ); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1660,7 +1711,8 @@ mod tests { None ); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1677,8 +1729,8 @@ mod tests { fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1695,7 +1747,8 @@ mod tests { |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), ); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1707,7 +1760,8 @@ mod tests { None ); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1724,8 +1778,8 @@ mod tests { fn recovery_with_invalid_pk_d() { let mut rng = OsRng; let heights = [ - TestNetwork::activation_height(Sapling).unwrap(), - TestNetwork::activation_height(Canopy).unwrap(), + TestNetwork.activation_height(Sapling).unwrap(), + TestNetwork.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1734,7 +1788,8 @@ mod tests { random_enc_ciphertext_with(height, ivk, &mut rng); assert_eq!( - try_sapling_output_recovery::( + try_sapling_output_recovery( + &Network::TestNetwork, height, &ovk, &cv, @@ -1746,7 +1801,8 @@ mod tests { None ); assert_eq!( - try_sapling_output_recovery_with_ock::( + try_sapling_output_recovery_with_ock( + &Network::TestNetwork, height, &ock, &cmu, @@ -1781,7 +1837,8 @@ mod tests { }; } - let height = TestNetwork::activation_height(NetworkUpgrade::Sapling) + let height = TestNetwork + .activation_height(NetworkUpgrade::Sapling) .expect("Should have Sapling activation height"); for tv in test_vectors { @@ -1820,7 +1877,14 @@ mod tests { // (Tested first because it only requires immutable references.) // - match try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &tv.c_enc) { + match try_sapling_note_decryption( + &Network::TestNetwork, + height, + &ivk, + &epk, + &cmu, + &tv.c_enc, + ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1829,7 +1893,8 @@ mod tests { None => panic!("Note decryption failed"), } - match try_sapling_compact_note_decryption::( + match try_sapling_compact_note_decryption( + &Network::TestNetwork, height, &ivk, &epk, @@ -1843,8 +1908,15 @@ mod tests { None => panic!("Compact note decryption failed"), } - match try_sapling_output_recovery::( - height, &ovk, &cv, &cmu, &epk, &tv.c_enc, &tv.c_out, + match try_sapling_output_recovery( + &Network::TestNetwork, + height, + &ovk, + &cv, + &cmu, + &epk, + &tv.c_enc, + &tv.c_out, ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 1916d773b..64fc115f0 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -85,6 +85,7 @@ pub struct SaplingOutput { impl SaplingOutput { pub fn new( + params: &P, height: BlockHeight, rng: &mut R, ovk: Option, @@ -100,7 +101,7 @@ impl SaplingOutput { return Err(Error::InvalidAmount); } - let rseed = generate_random_rseed::(height, rng); + let rseed = generate_random_rseed(params, height, rng); let note = Note { g_d, @@ -302,6 +303,7 @@ impl TransactionMetadata { /// Generates a [`Transaction`] from its inputs and outputs. pub struct Builder { + params: P, rng: R, height: BlockHeight, mtx: TransactionData, @@ -324,8 +326,8 @@ impl Builder { /// expiry delta (20 blocks). /// /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new(height: BlockHeight) -> Self { - Builder::new_with_rng(height, OsRng) + pub fn new(params: P, height: BlockHeight) -> Self { + Builder::new_with_rng(params, height, OsRng) } } @@ -339,11 +341,12 @@ impl Builder { /// expiry delta (20 blocks). /// /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new_with_rng(height: BlockHeight, rng: R) -> Builder { + pub fn new_with_rng(params: P, height: BlockHeight, rng: R) -> Builder { let mut mtx = TransactionData::new(); mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; Builder { + params, rng, height, mtx, @@ -402,7 +405,15 @@ impl Builder { value: Amount, memo: Option, ) -> Result<(), Error> { - let output = SaplingOutput::new::(self.height, &mut self.rng, ovk, to, value, memo)?; + let output = SaplingOutput::new( + &self.params, + self.height, + &mut self.rng, + ovk, + to, + value, + memo, + )?; self.mtx.value_balance -= value; @@ -613,7 +624,7 @@ impl Builder { } }; - let rseed = generate_random_rseed::(self.height, &mut self.rng); + let rseed = generate_random_rseed(&self.params, self.height, &mut self.rng); ( payment_address, @@ -709,8 +720,7 @@ mod tests { use super::{Builder, Error}; use crate::{ - consensus::TestNetwork, - consensus::{self, H0}, + consensus::{self, Network::TestNetwork, H0}, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, primitives::Rseed, @@ -727,7 +737,7 @@ mod tests { let ovk = extfvk.fvk.ovk; let to = extfvk.default_address().unwrap().1; - let mut builder = Builder::::new(H0); + let mut builder = Builder::new(TestNetwork, H0); assert_eq!( builder.add_sapling_output(Some(ovk), to, Amount::from_i64(-1).unwrap(), None), Err(Error::InvalidAmount) @@ -742,11 +752,13 @@ mod tests { TransactionData, }; - let sapling_activation_height = - TestNetwork::activation_height(NetworkUpgrade::Sapling).unwrap(); + 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 { + params: TestNetwork, rng: OsRng, height: sapling_activation_height, mtx: TransactionData::new(), @@ -787,7 +799,7 @@ mod tests { tree.append(cmu1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); - let mut builder = Builder::::new(H0); + let mut builder = Builder::new(TestNetwork, H0); // Create a tx with a sapling spend. binding_sig should be present builder @@ -813,7 +825,7 @@ mod tests { #[test] fn fails_on_negative_transparent_output() { - let mut builder = Builder::::new(H0); + let mut builder = Builder::new(TestNetwork, H0); assert_eq!( builder.add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -833,7 +845,7 @@ mod tests { // Fails with no inputs or outputs // 0.0001 t-ZEC fee { - let builder = Builder::::new(H0); + let builder = Builder::new(TestNetwork, H0); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap())) @@ -847,7 +859,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(H0); + let mut builder = Builder::new(TestNetwork, H0); builder .add_sapling_output( ovk.clone(), @@ -865,7 +877,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(H0); + let mut builder = Builder::new(TestNetwork, H0); builder .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -889,7 +901,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(H0); + let mut builder = Builder::new(TestNetwork, H0); builder .add_sapling_spend( extsk.clone(), @@ -932,7 +944,7 @@ mod tests { // (Still fails because we are using a MockTxProver which doesn't correctly // compute bindingSig.) { - let mut builder = Builder::::new(H0); + let mut builder = Builder::new(TestNetwork, H0); builder .add_sapling_spend( extsk.clone(), diff --git a/zcash_primitives/src/util.rs b/zcash_primitives/src/util.rs index 307e530aa..d28a28982 100644 --- a/zcash_primitives/src/util.rs +++ b/zcash_primitives/src/util.rs @@ -17,10 +17,11 @@ pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr { } pub fn generate_random_rseed( + params: &P, height: BlockHeight, rng: &mut R, ) -> Rseed { - if P::is_nu_active(NetworkUpgrade::Canopy, height) { + if params.is_nu_active(NetworkUpgrade::Canopy, height) { let mut buffer = [0u8; 32]; &rng.fill_bytes(&mut buffer); Rseed::AfterZip212(buffer) From d232133216489434d0af5924fa8a5fa7cd62591a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 17 Sep 2020 11:55:39 -0600 Subject: [PATCH 3/7] Add constants to support static resolution of network parameters. --- zcash_primitives/src/consensus.rs | 138 +++++++++----- zcash_primitives/src/note_encryption.rs | 197 +++++++++----------- zcash_primitives/src/transaction/builder.rs | 24 +-- 3 files changed, 195 insertions(+), 164 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 9c3e6ecd6..e0d6f6973 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -132,14 +132,76 @@ pub trait Parameters { fn b58_script_address_prefix(&self) -> [u8; 2]; fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool { - match self.activation_height(nu) { - Some(h) if h <= height => true, - _ => false, - } + self.activation_height(nu).map_or(false, |h| h <= height) } } /// Marker struct for the production network. +pub struct MainNetwork {} + +pub const MAIN_NETWORK: MainNetwork = MainNetwork {}; + +impl Parameters for MainNetwork { + fn activation_height(&self, nu: NetworkUpgrade) -> Option { + match nu { + NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)), + NetworkUpgrade::Sapling => Some(BlockHeight(419_200)), + NetworkUpgrade::Blossom => Some(BlockHeight(653_600)), + NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)), + NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)), + } + } + + fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY + } + + fn hrp_sapling_payment_address(&self) -> &str { + constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS + } + + fn b58_pubkey_address_prefix(&self) -> [u8; 2] { + constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX + } + + fn b58_script_address_prefix(&self) -> [u8; 2] { + constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX + } +} + +/// Marker struct for the test network. +pub struct TestNetwork {} + +pub const TEST_NETWORK: TestNetwork = TestNetwork {}; + +impl Parameters for TestNetwork { + fn activation_height(&self, nu: NetworkUpgrade) -> Option { + match nu { + NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)), + NetworkUpgrade::Sapling => Some(BlockHeight(280_000)), + NetworkUpgrade::Blossom => Some(BlockHeight(584_000)), + NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)), + NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)), + } + } + + fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY + } + + fn hrp_sapling_payment_address(&self) -> &str { + constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS + } + + fn b58_pubkey_address_prefix(&self) -> [u8; 2] { + constants::testnet::B58_PUBKEY_ADDRESS_PREFIX + } + + fn b58_script_address_prefix(&self) -> [u8; 2] { + constants::testnet::B58_SCRIPT_ADDRESS_PREFIX + } +} + #[derive(Clone, Copy, Debug)] pub enum Network { MainNetwork, @@ -149,52 +211,36 @@ pub enum Network { impl Parameters for Network { fn activation_height(&self, nu: NetworkUpgrade) -> Option { match self { - Network::MainNetwork => match nu { - NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)), - NetworkUpgrade::Sapling => Some(BlockHeight(419_200)), - NetworkUpgrade::Blossom => Some(BlockHeight(653_600)), - NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)), - NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)), - }, - Network::TestNetwork => match nu { - NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)), - NetworkUpgrade::Sapling => Some(BlockHeight(280_000)), - NetworkUpgrade::Blossom => Some(BlockHeight(584_000)), - NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)), - NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)), - }, + Network::MainNetwork => MAIN_NETWORK.activation_height(nu), + Network::TestNetwork => TEST_NETWORK.activation_height(nu), } } fn hrp_sapling_extended_full_viewing_key(&self) -> &str { match self { - Network::MainNetwork => constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - - Network::TestNetwork => constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + Network::MainNetwork => MAIN_NETWORK.hrp_sapling_extended_full_viewing_key(), + Network::TestNetwork => TEST_NETWORK.hrp_sapling_extended_full_viewing_key(), } } fn hrp_sapling_payment_address(&self) -> &str { match self { - Network::MainNetwork => constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, - - Network::TestNetwork => constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, + Network::MainNetwork => MAIN_NETWORK.hrp_sapling_payment_address(), + Network::TestNetwork => TEST_NETWORK.hrp_sapling_payment_address(), } } fn b58_pubkey_address_prefix(&self) -> [u8; 2] { match self { - Network::MainNetwork => constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX, - - Network::TestNetwork => constants::testnet::B58_PUBKEY_ADDRESS_PREFIX, + Network::MainNetwork => MAIN_NETWORK.b58_pubkey_address_prefix(), + Network::TestNetwork => TEST_NETWORK.b58_pubkey_address_prefix(), } } fn b58_script_address_prefix(&self) -> [u8; 2] { match self { - Network::MainNetwork => constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX, - - Network::TestNetwork => constants::testnet::B58_SCRIPT_ADDRESS_PREFIX, + Network::MainNetwork => MAIN_NETWORK.b58_script_address_prefix(), + Network::TestNetwork => TEST_NETWORK.b58_script_address_prefix(), } } } @@ -344,7 +390,9 @@ impl BranchId { mod tests { use std::convert::TryFrom; - use super::{BlockHeight, BranchId, Network, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER}; + use super::{ + BlockHeight, BranchId, NetworkUpgrade, Parameters, MAIN_NETWORK, UPGRADES_IN_ORDER, + }; #[test] fn nu_ordering() { @@ -352,12 +400,10 @@ mod tests { let nu_a = UPGRADES_IN_ORDER[i - 1]; let nu_b = UPGRADES_IN_ORDER[i]; match ( - Network::MainNetwork.activation_height(nu_a), - Network::MainNetwork.activation_height(nu_b), + MAIN_NETWORK.activation_height(nu_a), + MAIN_NETWORK.activation_height(nu_b), ) { - (Some(a), Some(b)) if a < b => (), - (Some(_), None) => (), - (None, None) => (), + (a, b) if a < b => (), _ => panic!( "{} should not be before {} in UPGRADES_IN_ORDER", nu_a, nu_b @@ -368,11 +414,9 @@ mod tests { #[test] fn nu_is_active() { - assert!(!Network::MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(0))); - assert!( - !Network::MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_499)) - ); - assert!(Network::MainNetwork.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_500))); + assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(0))); + assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_499))); + assert!(MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_500))); } #[test] @@ -384,27 +428,27 @@ mod tests { #[test] fn branch_id_for_height() { assert_eq!( - BranchId::for_height(&Network::MainNetwork, BlockHeight(0)), + BranchId::for_height(&MAIN_NETWORK, BlockHeight(0)), BranchId::Sprout, ); assert_eq!( - BranchId::for_height(&Network::MainNetwork, BlockHeight(419_199)), + BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_199)), BranchId::Overwinter, ); assert_eq!( - BranchId::for_height(&Network::MainNetwork, BlockHeight(419_200)), + BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_200)), BranchId::Sapling, ); assert_eq!( - BranchId::for_height(&Network::MainNetwork, BlockHeight(903_000)), + BranchId::for_height(&MAIN_NETWORK, BlockHeight(903_000)), BranchId::Heartwood, ); assert_eq!( - BranchId::for_height(&Network::MainNetwork, BlockHeight(1_046_400)), + BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_046_400)), BranchId::Canopy, ); assert_eq!( - BranchId::for_height(&Network::MainNetwork, BlockHeight(5_000_000)), + BranchId::for_height(&MAIN_NETWORK, BlockHeight(5_000_000)), BranchId::Canopy, ); } diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index e7a2c67e9..d67880215 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1,7 +1,7 @@ //! Implementation of in-band secret distribution for Zcash transactions. use crate::{ - consensus::{self, BlockHeight, NetworkUpgrade, ZIP212_GRACE_PERIOD}, + consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD}, primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; @@ -412,11 +412,9 @@ pub fn plaintext_version_is_valid( height: BlockHeight, leadbyte: u8, ) -> bool { - if params.is_nu_active(NetworkUpgrade::Canopy, height) { - let grace_period_end_height = params - .activation_height(NetworkUpgrade::Canopy) - .expect("Should have Canopy activation height") - + ZIP212_GRACE_PERIOD; + if params.is_nu_active(Canopy, height) { + let grace_period_end_height = + params.activation_height(Canopy).unwrap() + ZIP212_GRACE_PERIOD; if height < grace_period_end_height && leadbyte != 0x01 && leadbyte != 0x02 { // non-{0x01,0x02} received after Canopy activation and before grace period has elapsed @@ -662,11 +660,9 @@ mod tests { use crate::{ consensus::{ - BlockHeight, Network, - Network::TestNetwork, - NetworkUpgrade, + BlockHeight, NetworkUpgrade::{Canopy, Sapling}, - Parameters, ZIP212_GRACE_PERIOD, + Parameters, TEST_NETWORK, ZIP212_GRACE_PERIOD, }, // jubjub::{ // edwards, @@ -813,7 +809,7 @@ mod tests { random_enc_ciphertext_with(height, ivk, rng); assert!(try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -822,7 +818,7 @@ mod tests { ) .is_some()); assert!(try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -832,7 +828,7 @@ mod tests { .is_some()); let ovk_output_recovery = try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -843,7 +839,7 @@ mod tests { ); let ock_output_recovery = try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -884,7 +880,7 @@ mod tests { }; let cv = value_commitment.commitment().into(); - let rseed = generate_random_rseed(&Network::TestNetwork, height, &mut rng); + let rseed = generate_random_rseed(&TEST_NETWORK, height, &mut rng); let note = pa.create_note(value, rseed).unwrap(); let cmu = note.cmu(); @@ -986,8 +982,8 @@ mod tests { fn decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -995,7 +991,7 @@ mod tests { assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &jubjub::Fr::random(&mut rng), &epk, @@ -1011,8 +1007,8 @@ mod tests { fn decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1020,7 +1016,7 @@ mod tests { assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &jubjub::SubgroupPoint::random(&mut rng), @@ -1036,8 +1032,8 @@ mod tests { fn decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1045,7 +1041,7 @@ mod tests { assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1061,8 +1057,8 @@ mod tests { fn decryption_with_invalid_tag() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1072,7 +1068,7 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1087,7 +1083,7 @@ mod tests { #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; - let canopy_activation_height = TestNetwork.activation_height(Canopy).unwrap(); + let canopy_activation_height = TEST_NETWORK.activation_height(Canopy).unwrap(); let heights = [ canopy_activation_height - 1, canopy_activation_height, @@ -1110,7 +1106,7 @@ mod tests { ); assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1126,8 +1122,8 @@ mod tests { fn decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1145,7 +1141,7 @@ mod tests { ); assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1161,8 +1157,8 @@ mod tests { fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1181,7 +1177,7 @@ mod tests { assert_eq!( try_sapling_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1197,8 +1193,8 @@ mod tests { fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1206,7 +1202,7 @@ mod tests { assert_eq!( try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &jubjub::Fr::random(&mut rng), &epk, @@ -1222,8 +1218,8 @@ mod tests { fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1231,7 +1227,7 @@ mod tests { assert_eq!( try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &jubjub::SubgroupPoint::random(&mut rng), @@ -1247,8 +1243,8 @@ mod tests { fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1256,7 +1252,7 @@ mod tests { assert_eq!( try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1271,7 +1267,7 @@ mod tests { #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; - let canopy_activation_height = TestNetwork.activation_height(Canopy).unwrap(); + let canopy_activation_height = TEST_NETWORK.activation_height(Canopy).unwrap(); let heights = [ canopy_activation_height - 1, canopy_activation_height, @@ -1294,7 +1290,7 @@ mod tests { ); assert_eq!( try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1310,8 +1306,8 @@ mod tests { fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1329,7 +1325,7 @@ mod tests { ); assert_eq!( try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1345,8 +1341,8 @@ mod tests { fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1364,7 +1360,7 @@ mod tests { ); assert_eq!( try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1380,8 +1376,8 @@ mod tests { fn recovery_with_invalid_ovk() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1391,7 +1387,7 @@ mod tests { ovk.0[0] ^= 0xff; assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1409,8 +1405,8 @@ mod tests { fn recovery_with_invalid_ock() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1419,7 +1415,7 @@ mod tests { assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &OutgoingCipherKey([0u8; 32]), &cmu, @@ -1436,8 +1432,8 @@ mod tests { fn recovery_with_invalid_cv() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1446,7 +1442,7 @@ mod tests { assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &jubjub::ExtendedPoint::random(&mut rng), @@ -1464,8 +1460,8 @@ mod tests { fn recovery_with_invalid_cmu() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1474,7 +1470,7 @@ mod tests { assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1488,7 +1484,7 @@ mod tests { assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &bls12_381::Scalar::random(&mut rng), @@ -1505,8 +1501,8 @@ mod tests { fn recovery_with_invalid_epk() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1515,7 +1511,7 @@ mod tests { assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1529,7 +1525,7 @@ mod tests { assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1546,8 +1542,8 @@ mod tests { fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1557,7 +1553,7 @@ mod tests { enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1570,7 +1566,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1587,8 +1583,8 @@ mod tests { fn recovery_with_invalid_out_tag() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1598,7 +1594,7 @@ mod tests { out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1611,7 +1607,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1627,7 +1623,7 @@ mod tests { #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; - let canopy_activation_height = TestNetwork.activation_height(Canopy).unwrap(); + let canopy_activation_height = TEST_NETWORK.activation_height(Canopy).unwrap(); let heights = [ canopy_activation_height - 1, canopy_activation_height, @@ -1650,7 +1646,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1663,7 +1659,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1680,8 +1676,8 @@ mod tests { fn recovery_with_invalid_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1699,7 +1695,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1712,7 +1708,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1729,8 +1725,8 @@ mod tests { fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1748,7 +1744,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1761,7 +1757,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1778,8 +1774,8 @@ mod tests { fn recovery_with_invalid_pk_d() { let mut rng = OsRng; let heights = [ - TestNetwork.activation_height(Sapling).unwrap(), - TestNetwork.activation_height(Canopy).unwrap(), + TEST_NETWORK.activation_height(Sapling).unwrap(), + TEST_NETWORK.activation_height(Canopy).unwrap(), ]; for &height in heights.iter() { @@ -1789,7 +1785,7 @@ mod tests { assert_eq!( try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, @@ -1802,7 +1798,7 @@ mod tests { ); assert_eq!( try_sapling_output_recovery_with_ock( - &Network::TestNetwork, + &TEST_NETWORK, height, &ock, &cmu, @@ -1837,9 +1833,7 @@ mod tests { }; } - let height = TestNetwork - .activation_height(NetworkUpgrade::Sapling) - .expect("Should have Sapling activation height"); + let height = TEST_NETWORK.activation_height(Sapling).unwrap(); for tv in test_vectors { // @@ -1877,14 +1871,7 @@ mod tests { // (Tested first because it only requires immutable references.) // - match try_sapling_note_decryption( - &Network::TestNetwork, - height, - &ivk, - &epk, - &cmu, - &tv.c_enc, - ) { + match try_sapling_note_decryption(&TEST_NETWORK, height, &ivk, &epk, &cmu, &tv.c_enc) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1894,7 +1881,7 @@ mod tests { } match try_sapling_compact_note_decryption( - &Network::TestNetwork, + &TEST_NETWORK, height, &ivk, &epk, @@ -1909,7 +1896,7 @@ mod tests { } match try_sapling_output_recovery( - &Network::TestNetwork, + &TEST_NETWORK, height, &ovk, &cv, diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 64fc115f0..374c2af15 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -720,7 +720,7 @@ mod tests { use super::{Builder, Error}; use crate::{ - consensus::{self, Network::TestNetwork, H0}, + consensus::{self, Parameters, H0, TEST_NETWORK}, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, primitives::Rseed, @@ -737,7 +737,7 @@ mod tests { let ovk = extfvk.fvk.ovk; let to = extfvk.default_address().unwrap().1; - let mut builder = Builder::new(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); assert_eq!( builder.add_sapling_output(Some(ovk), to, Amount::from_i64(-1).unwrap(), None), Err(Error::InvalidAmount) @@ -746,19 +746,19 @@ mod tests { #[test] fn binding_sig_absent_if_no_shielded_spend_or_output() { - use crate::consensus::{NetworkUpgrade, Parameters}; + use crate::consensus::NetworkUpgrade; use crate::transaction::{ builder::{self, TransparentInputs}, TransactionData, }; - let sapling_activation_height = TestNetwork + let sapling_activation_height = TEST_NETWORK .activation_height(NetworkUpgrade::Sapling) .unwrap(); // Create a builder with 0 fee, so we can construct t outputs let mut builder = builder::Builder { - params: TestNetwork, + params: TEST_NETWORK, rng: OsRng, height: sapling_activation_height, mtx: TransactionData::new(), @@ -799,7 +799,7 @@ mod tests { tree.append(cmu1).unwrap(); let witness1 = IncrementalWitness::from_tree(&tree); - let mut builder = Builder::new(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); // Create a tx with a sapling spend. binding_sig should be present builder @@ -825,7 +825,7 @@ mod tests { #[test] fn fails_on_negative_transparent_output() { - let mut builder = Builder::new(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); assert_eq!( builder.add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -845,7 +845,7 @@ mod tests { // Fails with no inputs or outputs // 0.0001 t-ZEC fee { - let builder = Builder::new(TestNetwork, H0); + let builder = Builder::new(TEST_NETWORK, H0); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap())) @@ -859,7 +859,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(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); builder .add_sapling_output( ovk.clone(), @@ -877,7 +877,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(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); builder .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -901,7 +901,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(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); builder .add_sapling_spend( extsk.clone(), @@ -944,7 +944,7 @@ mod tests { // (Still fails because we are using a MockTxProver which doesn't correctly // compute bindingSig.) { - let mut builder = Builder::new(TestNetwork, H0); + let mut builder = Builder::new(TEST_NETWORK, H0); builder .add_sapling_spend( extsk.clone(), From 90d898016275662dc3c43387dd4010a67f3512a4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 18 Sep 2020 10:39:37 -0600 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: str4d --- zcash_client_backend/src/encoding.rs | 2 +- zcash_client_sqlite/src/address.rs | 2 +- zcash_client_sqlite/src/init.rs | 4 ++-- zcash_client_sqlite/src/query.rs | 2 +- zcash_client_sqlite/src/scan.rs | 2 +- zcash_client_sqlite/src/transact.rs | 2 +- zcash_primitives/src/note_encryption.rs | 5 ----- 7 files changed, 7 insertions(+), 12 deletions(-) diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 1548d6575..b3c421b3f 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -291,7 +291,7 @@ mod tests { use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use zcash_primitives::{ - constants::{self}, + constants, primitives::{Diversifier, PaymentAddress}, zip32::ExtendedSpendingKey, }; diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs index 8b7bdc39c..2abd5bd1a 100644 --- a/zcash_client_sqlite/src/address.rs +++ b/zcash_client_sqlite/src/address.rs @@ -5,7 +5,7 @@ use zcash_client_backend::encoding::{ encode_transparent_address, }; use zcash_primitives::{ - consensus::{self}, + consensus, legacy::TransparentAddress, primitives::PaymentAddress, }; diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs index 498ba4cd6..8a3cb838b 100644 --- a/zcash_client_sqlite/src/init.rs +++ b/zcash_client_sqlite/src/init.rs @@ -6,7 +6,7 @@ use zcash_client_backend::encoding::encode_extended_full_viewing_key; use zcash_primitives::{ block::BlockHash, - consensus::{self}, + consensus, zip32::ExtendedFullViewingKey, }; @@ -261,7 +261,7 @@ mod tests { use super::{init_accounts_table, init_blocks_table, init_data_database}; use crate::{ query::get_address, - tests::{self}, + tests, }; #[test] diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs index 80185da6e..9adb7a030 100644 --- a/zcash_client_sqlite/src/query.rs +++ b/zcash_client_sqlite/src/query.rs @@ -176,7 +176,7 @@ mod tests { use crate::{ error::ErrorKind, init::{init_accounts_table, init_data_database}, - tests::{self}, + tests, }; #[test] diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index fc108d892..146561405 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -409,7 +409,7 @@ pub fn decrypt_and_store_transaction, P: consensus::Parameters>( }) .optional()? .map(|last_height: u32| BlockHeight::from(last_height + 1)) - .and(params.activation_height(NetworkUpgrade::Sapling)) + .or_else(|| params.activation_height(NetworkUpgrade::Sapling)) .ok_or(Error(ErrorKind::SaplingNotActive))?, }; diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index 07ee510eb..a933b8236 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -376,7 +376,7 @@ mod tests { use zcash_primitives::{ block::BlockHash, - consensus::{self}, + consensus, note_encryption::try_sapling_output_recovery, prover::TxProver, transaction::{components::Amount, Transaction}, diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index d67880215..24dc747ef 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -664,11 +664,6 @@ mod tests { NetworkUpgrade::{Canopy, Sapling}, Parameters, TEST_NETWORK, ZIP212_GRACE_PERIOD, }, - // jubjub::{ - // edwards, - // fs::{Fs, FsRepr}, - // PrimeOrder, Unknown, - // }, keys::OutgoingViewingKey, primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, util::generate_random_rseed, From 80e1d35ea639eb22c1a69ff4c9c9d351a1d1d612 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 18 Sep 2020 10:40:30 -0600 Subject: [PATCH 5/7] Apply additional suggestions from code review. --- zcash_client_sqlite/src/address.rs | 4 ++-- zcash_client_sqlite/src/chain.rs | 3 +-- zcash_client_sqlite/src/lib.rs | 2 +- zcash_client_sqlite/src/scan.rs | 2 +- zcash_client_sqlite/src/transact.rs | 4 ++-- zcash_primitives/src/consensus.rs | 28 ++++++++++++++++++++++------ 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs index 2abd5bd1a..95cd97358 100644 --- a/zcash_client_sqlite/src/address.rs +++ b/zcash_client_sqlite/src/address.rs @@ -29,7 +29,7 @@ impl From for RecipientAddress { } impl RecipientAddress { - pub fn from_str(params: &P, s: &str) -> Option { + pub fn decode(params: &P, s: &str) -> Option { if let Ok(Some(pa)) = decode_payment_address(params.hrp_sapling_payment_address(), s) { Some(pa.into()) } else if let Ok(Some(addr)) = decode_transparent_address( @@ -43,7 +43,7 @@ impl RecipientAddress { } } - pub fn to_string(&self, params: &P) -> String { + pub fn encode(&self, params: &P) -> String { match self { RecipientAddress::Shielded(pa) => { encode_payment_address(params.hrp_sapling_payment_address(), pa) diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index a15f51675..9b86ef4fe 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -4,7 +4,6 @@ //! //! ``` //! use rusqlite::Connection; -//! use std::ops::Sub; //! use zcash_primitives::{ //! consensus::{BlockHeight, Network, Parameters} //! }; @@ -33,7 +32,7 @@ //! // This might be informed by some external chain reorg information, or //! // heuristics such as the platform, available bandwidth, size of recent //! // CompactBlocks, etc. -//! let rewind_height = upper_bound.sub(10); +//! let rewind_height = *upper_bound - 10; //! //! // b) Rewind scanned block information. //! rewind_to_height(network, &db_data, rewind_height); diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 90ac5bdf7..6d1551cf3 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -273,7 +273,7 @@ mod tests { .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") .unwrap() .execute(&[ - (cb.height as u32).to_sql().unwrap(), + u32::from(cb.height()).to_sql().unwrap(), cb_bytes.to_sql().unwrap(), ]) .unwrap(); diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs index 146561405..305fc01d8 100644 --- a/zcash_client_sqlite/src/scan.rs +++ b/zcash_client_sqlite/src/scan.rs @@ -479,7 +479,7 @@ pub fn decrypt_and_store_transaction, P: consensus::Parameters>( let value = output.note.value as i64; if output.outgoing { - let to_str = RecipientAddress::from(output.to).to_string(params); + let to_str = RecipientAddress::from(output.to).encode(params); // Try updating an existing sent note. if stmt_update_sent_note.execute(&[ diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs index a933b8236..e29966e54 100644 --- a/zcash_client_sqlite/src/transact.rs +++ b/zcash_client_sqlite/src/transact.rs @@ -123,7 +123,7 @@ struct SelectedNoteRow { /// Err(e) => (), /// } /// ``` -pub fn create_to_address, P: consensus::Parameters + Clone>( +pub fn create_to_address, P: consensus::Parameters>( db_data: DB, params: &P, consensus_branch_id: consensus::BranchId, @@ -334,7 +334,7 @@ pub fn create_to_address, P: consensus::Parameters + Clone>( // Save the sent note in the database. // TODO: Decide how to save transparent output information. - let to_str = to.to_string(params); + let to_str = to.encode(params); if let Some(memo) = memo { let mut stmt_insert_sent_note = data.prepare( "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index e0d6f6973..c94d459c2 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -7,6 +7,8 @@ use std::ops::{Add, Sub}; use crate::constants; +/// A wrapper type representing blockchain heights. Safe conversion from +/// various integer types, as well as addition and subtraction, are provided. #[repr(transparent)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BlockHeight(u32); @@ -120,26 +122,39 @@ impl Sub for BlockHeight { } /// Zcash consensus parameters. -pub trait Parameters { +pub trait Parameters: Clone { + /// Returns the activation height for a particular network upgrade, + /// if an activation height has been set. fn activation_height(&self, nu: NetworkUpgrade) -> Option; + /// Returns the human-readable prefix for Sapling extended full + /// viewing keys for the network to which this Parameters value applies. fn hrp_sapling_extended_full_viewing_key(&self) -> &str; + /// Returns the human-readable prefix for Sapling payment addresses + /// viewing keys for the network to which this Parameters value applies. fn hrp_sapling_payment_address(&self) -> &str; + /// Returns the human-readable prefix for transparent pay-to-public-key-hash + /// payment addresses for the network to which this Parameters value applies. fn b58_pubkey_address_prefix(&self) -> [u8; 2]; + /// Returns the human-readable prefix for transparent pay-to-script-hash + /// payment addresses for the network to which this Parameters value applies. fn b58_script_address_prefix(&self) -> [u8; 2]; + /// Determines whether the specified network upgrade is active as of the + /// provided block height on the network to which this Parameters value applies. fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool { self.activation_height(nu).map_or(false, |h| h <= height) } } /// Marker struct for the production network. -pub struct MainNetwork {} +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct MainNetwork; -pub const MAIN_NETWORK: MainNetwork = MainNetwork {}; +pub const MAIN_NETWORK: MainNetwork = MainNetwork; impl Parameters for MainNetwork { fn activation_height(&self, nu: NetworkUpgrade) -> Option { @@ -170,9 +185,10 @@ impl Parameters for MainNetwork { } /// Marker struct for the test network. -pub struct TestNetwork {} +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct TestNetwork; -pub const TEST_NETWORK: TestNetwork = TestNetwork {}; +pub const TEST_NETWORK: TestNetwork = TestNetwork; impl Parameters for TestNetwork { fn activation_height(&self, nu: NetworkUpgrade) -> Option { @@ -202,7 +218,7 @@ impl Parameters for TestNetwork { } } -#[derive(Clone, Copy, Debug)] +#[derive(PartialEq, Copy, Clone, Debug)] pub enum Network { MainNetwork, TestNetwork, From cfb6850d7c4b658a2bb437e46e615e2fbd31e739 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 18 Sep 2020 10:52:58 -0600 Subject: [PATCH 6/7] Rustfmt --- zcash_client_sqlite/src/address.rs | 6 +----- zcash_client_sqlite/src/init.rs | 11 ++--------- zcash_primitives/src/consensus.rs | 6 +++--- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs index 95cd97358..dd857568c 100644 --- a/zcash_client_sqlite/src/address.rs +++ b/zcash_client_sqlite/src/address.rs @@ -4,11 +4,7 @@ use zcash_client_backend::encoding::{ decode_payment_address, decode_transparent_address, encode_payment_address, encode_transparent_address, }; -use zcash_primitives::{ - consensus, - legacy::TransparentAddress, - primitives::PaymentAddress, -}; +use zcash_primitives::{consensus, legacy::TransparentAddress, primitives::PaymentAddress}; /// An address that funds can be sent to. pub enum RecipientAddress { diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs index 8a3cb838b..be35e4f2e 100644 --- a/zcash_client_sqlite/src/init.rs +++ b/zcash_client_sqlite/src/init.rs @@ -4,11 +4,7 @@ use rusqlite::{types::ToSql, Connection, NO_PARAMS}; use std::path::Path; use zcash_client_backend::encoding::encode_extended_full_viewing_key; -use zcash_primitives::{ - block::BlockHash, - consensus, - zip32::ExtendedFullViewingKey, -}; +use zcash_primitives::{block::BlockHash, consensus, zip32::ExtendedFullViewingKey}; use crate::{ address_from_extfvk, @@ -259,10 +255,7 @@ mod tests { }; use super::{init_accounts_table, init_blocks_table, init_data_database}; - use crate::{ - query::get_address, - tests, - }; + use crate::{query::get_address, tests}; #[test] fn init_accounts_table_only_works_once() { diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index c94d459c2..2352493f7 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -127,7 +127,7 @@ pub trait Parameters: Clone { /// if an activation height has been set. fn activation_height(&self, nu: NetworkUpgrade) -> Option; - /// Returns the human-readable prefix for Sapling extended full + /// Returns the human-readable prefix for Sapling extended full /// viewing keys for the network to which this Parameters value applies. fn hrp_sapling_extended_full_viewing_key(&self) -> &str; @@ -135,7 +135,7 @@ pub trait Parameters: Clone { /// viewing keys for the network to which this Parameters value applies. fn hrp_sapling_payment_address(&self) -> &str; - /// Returns the human-readable prefix for transparent pay-to-public-key-hash + /// Returns the human-readable prefix for transparent pay-to-public-key-hash /// payment addresses for the network to which this Parameters value applies. fn b58_pubkey_address_prefix(&self) -> [u8; 2]; @@ -143,7 +143,7 @@ pub trait Parameters: Clone { /// payment addresses for the network to which this Parameters value applies. fn b58_script_address_prefix(&self) -> [u8; 2]; - /// Determines whether the specified network upgrade is active as of the + /// Determines whether the specified network upgrade is active as of the /// provided block height on the network to which this Parameters value applies. fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool { self.activation_height(nu).map_or(false, |h| h <= height) From c7c39964ecdc1c848c95cc52675ec62f1d067e61 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 18 Sep 2020 10:55:29 -0600 Subject: [PATCH 7/7] Use block.height() method instead of .into() --- zcash_client_backend/src/welding_rig.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 48bbdfca9..4598cec59 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -96,6 +96,7 @@ pub fn scan_block( ) -> Vec { let mut wtxs: Vec = vec![]; let ivks: Vec<_> = extfvks.iter().map(|extfvk| extfvk.fvk.vk.ivk()).collect(); + let block_height = block.height(); for tx in block.vtx.into_iter() { let num_spends = tx.spends.len(); @@ -156,7 +157,7 @@ pub fn scan_block( if let Some(output) = scan_output( params, - block.height.into(), + block_height, to_scan, &ivks, &spent_from_accounts,