From 7934e834d3f828e588d014a9809ef49b633c97d4 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 2 Sep 2022 17:26:59 -0600 Subject: [PATCH] Add test for fee calculation in add_transaction_views migration. --- .../init/migrations/add_transaction_views.rs | 133 +++++++++++++++--- zcash_primitives/CHANGELOG.md | 6 +- zcash_primitives/src/transaction/mod.rs | 12 +- 3 files changed, 126 insertions(+), 25 deletions(-) diff --git a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs index d061ca200..03bcee7b6 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs @@ -14,19 +14,17 @@ use zcash_primitives::{ use super::super::{WalletMigration2, WalletMigrationError}; -pub(super) struct Migration

{ - pub(super) params: P, +pub(crate) fn migration_id() -> Uuid { + Uuid::parse_str("282fad2e-8372-4ca0-8bed-71821320909f").unwrap() } -impl

Migration

{ - fn id() -> Uuid { - Uuid::parse_str("282fad2e-8372-4ca0-8bed-71821320909f").unwrap() - } +pub(crate) struct Migration

{ + pub(super) params: P, } impl

schemer::Migration for Migration

{ fn id(&self) -> Uuid { - Migration::

::id() + migration_id() } fn dependencies(&self) -> HashSet { @@ -97,7 +95,7 @@ impl RusqliteMigration for Migration

{ SUM( CASE WHEN sent_notes.memo IS NULL THEN 0 - WHEN SUBSTR(sent_notes.memo, 0, 2) = X'F6' THEN 0 + WHEN sent_notes.memo = X'F600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' THEN 0 ELSE 1 END ) AS memo_count, @@ -118,7 +116,7 @@ impl RusqliteMigration for Migration

{ SUM( CASE WHEN received_notes.memo IS NULL THEN 0 - WHEN SUBSTR(received_notes.memo, 0, 2) = X'F6' THEN 0 + WHEN received_notes.memo = X'F600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' THEN 0 ELSE 1 END ) AS memo_count, @@ -154,7 +152,7 @@ impl RusqliteMigration for Migration

{ received_notes.is_change AS is_change, CASE WHEN received_notes.memo IS NULL THEN 0 - WHEN SUBSTR(received_notes.memo, 0, 2) = X'F6' THEN 0 + WHEN received_notes.memo = X'F600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' THEN 0 ELSE 1 END AS memo_present FROM transactions @@ -171,7 +169,7 @@ impl RusqliteMigration for Migration

{ false AS is_change, CASE WHEN sent_notes.memo IS NULL THEN 0 - WHEN SUBSTR(sent_notes.memo, 0, 2) = X'F6' THEN 0 + WHEN sent_notes.memo = X'F600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' THEN 0 ELSE 1 END AS memo_present FROM transactions @@ -192,15 +190,29 @@ impl RusqliteMigration for Migration

{ #[cfg(test)] mod tests { use rusqlite::{self, NO_PARAMS}; - use tempfile::NamedTempFile; - use crate::{ - tests::{self}, - wallet::init::init_wallet_db, - WalletDb, + #[cfg(feature = "transparent-inputs")] + use { + crate::wallet::init::{init_wallet_db_internal, WalletMigration2}, + rusqlite::params, + zcash_client_backend::{encoding::AddressCodec, keys::UnifiedSpendingKey}, + zcash_primitives::{ + consensus::{BlockHeight, BranchId, Network}, + legacy::{keys::IncomingViewingKey, Script}, + transaction::{ + components::{ + transparent::{self, Authorized, OutPoint}, + Amount, TxIn, TxOut, + }, + TransactionData, TxVersion, + }, + zip32::AccountId, + }, }; + use crate::{tests, wallet::init::init_wallet_db, WalletDb}; + #[test] fn transaction_views() { let data_file = NamedTempFile::new().unwrap(); @@ -217,7 +229,7 @@ mod tests { INSERT INTO sent_notes (tx, output_pool, output_index, from_account, address, value, memo) VALUES (0, 2, 1, 0, '', 3, X'61'); INSERT INTO sent_notes (tx, output_pool, output_index, from_account, address, value, memo) - VALUES (0, 2, 2, 0, '', 0, X'f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); + VALUES (0, 2, 2, 0, '', 0, X'F600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change, memo) VALUES (0, 0, 0, '', 5, '', 'a', false, X'62'); @@ -276,4 +288,91 @@ mod tests { } assert_eq!(row_count, 1); } + + #[test] + #[cfg(feature = "transparent-inputs")] + fn migrate_from_wm2() { + let data_file = NamedTempFile::new().unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + init_wallet_db_internal(&mut db_data, None, Some(WalletMigration2::::id())) + .unwrap(); + + // create a UTXO to spend + let tx = TransactionData::from_parts( + TxVersion::Sapling, + BranchId::Canopy, + 0, + BlockHeight::from(3), + Some(transparent::Bundle { + vin: vec![TxIn { + prevout: OutPoint::new([1u8; 32], 1), + script_sig: Script(vec![]), + sequence: 0, + }], + vout: vec![TxOut { + value: Amount::from_i64(1100000000).unwrap(), + script_pubkey: Script(vec![]), + }], + authorization: Authorized, + }), + None, + None, + None, + ) + .freeze() + .unwrap(); + + let mut tx_bytes = vec![]; + tx.write(&mut tx_bytes).unwrap(); + + let usk = + UnifiedSpendingKey::from_seed(&tests::network(), &[0u8; 32][..], AccountId::from(0)) + .unwrap(); + let ufvk = usk.to_unified_full_viewing_key(); + let (ua, _) = ufvk.default_address(); + let taddr = ufvk + .transparent() + .and_then(|k| { + k.derive_external_ivk() + .ok() + .map(|k| k.derive_address(0).unwrap()) + }) + .map(|a| a.encode(&tests::network())); + + db_data.conn.execute( + "INSERT INTO accounts (account, ufvk, address, transparent_address) VALUES (0, ?, ?, ?)", + params![ufvk.encode(&tests::network()), ua.encode(&tests::network()), &taddr] + ).unwrap(); + db_data + .conn + .execute_batch( + "INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, '');", + ) + .unwrap(); + db_data.conn.execute( + "INSERT INTO utxos (address, prevout_txid, prevout_idx, script, value_zat, height) + VALUES (?, X'0101010101010101010101010101010101010101010101010101010101010101', 1, X'', 1400000000, 1)", + &[taddr] + ).unwrap(); + db_data + .conn + .execute( + "INSERT INTO transactions (block, id_tx, txid, raw) VALUES (0, 0, '', ?)", + params![tx_bytes], + ) + .unwrap(); + + init_wallet_db(&mut db_data, None).unwrap(); + + let fee = db_data + .conn + .query_row( + "SELECT fee FROM transactions WHERE id_tx = 0", + NO_PARAMS, + |row| Ok(Amount::from_i64(row.get(0)?).unwrap()), + ) + .unwrap(); + + assert_eq!(fee, Amount::from_i64(300000000).unwrap()); + } } diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 82f5ee27d..43bf96ae7 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -28,7 +28,11 @@ and this library adheres to Rust's notion of - `Builder::new_with_rng_and_fee` - `zcash_primitives::transaction::TransactionData::fee_paid` - `zcash_primitives::transaction::components::amount::BalanceError` -- `zcash_primitives::transaction::components::sprout::Bundle::value_balance` +- Added in `zcash_primitives::transaction::components::sprout` + - `Bundle::value_balance` + - `JSDescription::net_value` +- Added in `zcash_primitives::transaction::components::transparent` + - `Bundle::value_balance` ### Changed - `zcash_primitives::sapling::ViewingKey` now stores `nk` as a diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 33ec32d0f..fa0600f1b 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -396,15 +396,13 @@ impl TransactionData { self.tze_bundle.as_ref() } - /// Returns the total fees paid by the transaction, given a function that can - /// be used to retrieve the output values of previous transactions' outputs - /// that are being spent in this transaction. - pub fn fee_paid Result>( - &self, - get_prevout: F, - ) -> Result + /// Returns the total fees paid by the transaction, given a function that can be used to + /// retrieve the value of previous transactions' transparent outputs that are being spent in + /// this transaction. + pub fn fee_paid(&self, get_prevout: F) -> Result where E: From, + F: FnMut(&OutPoint) -> Result, { let value_balances = [ self.transparent_bundle