diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index 0515ff3e9..0cfe00b7c 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -364,7 +364,10 @@ mod tests { #[cfg(feature = "transparent-inputs")] use { crate::encoding::AddressCodec, - zcash_primitives::{legacy, legacy::keys::IncomingViewingKey}, + zcash_primitives::legacy::{ + self, + keys::{AccountPrivKey, IncomingViewingKey}, + }, }; #[cfg(feature = "transparent-inputs")] @@ -409,7 +412,11 @@ mod tests { }; #[cfg(feature = "transparent-inputs")] - let transparent = { None }; + let transparent = { + let privkey = + AccountPrivKey::from_seed(&MAIN_NETWORK, &[0; 32], AccountId::from(0)).unwrap(); + Some(privkey.to_account_pubkey()) + }; let ufvk = UnifiedFullViewingKey::new( #[cfg(feature = "transparent-inputs")] @@ -419,12 +426,24 @@ mod tests { ) .unwrap(); - let encoding = ufvk.encode(&MAIN_NETWORK); - let decoded = UnifiedFullViewingKey::decode(&MAIN_NETWORK, &encoding).unwrap(); + let encoded = ufvk.encode(&MAIN_NETWORK); + + // test encoded form against known values + let encoded_with_t = "uview1tg6rpjgju2s2j37gkgjq79qrh5lvzr6e0ed3n4sf4hu5qd35vmsh7avl80xa6mx7ryqce9hztwaqwrdthetpy4pc0kce25x453hwcmax02p80pg5savlg865sft9reat07c5vlactr6l2pxtlqtqunt2j9gmvr8spcuzf07af80h5qmut38h0gvcfa9k4rwujacwwca9vu8jev7wq6c725huv8qjmhss3hdj2vh8cfxhpqcm2qzc34msyrfxk5u6dqttt4vv2mr0aajreww5yufpk0gn4xkfm888467k7v6fmw7syqq6cceu078yw8xja502jxr0jgum43lhvpzmf7eu5dmnn6cr6f7p43yw8znzgxg598mllewnx076hljlvynhzwn5es94yrv65tdg3utuz2u3sras0wfcq4adxwdvlk387d22g3q98t5z74quw2fa4wed32escx8dwh4mw35t4jwf35xyfxnu83mk5s4kw2glkgsshmxk"; + let _encoded_no_t = "uview12z384wdq76ceewlsu0esk7d97qnd23v2qnvhujxtcf2lsq8g4hwzpx44fwxssnm5tg8skyh4tnc8gydwxefnnm0hd0a6c6etmj0pp9jqkdsllkr70u8gpf7ndsfqcjlqn6dec3faumzqlqcmtjf8vp92h7kj38ph2786zx30hq2wru8ae3excdwc8w0z3t9fuw7mt7xy5sn6s4e45kwm0cjp70wytnensgdnev286t3vew3yuwt2hcz865y037k30e428dvgne37xvyeal2vu8yjnznphf9t2rw3gdp0hk5zwq00ws8f3l3j5n3qkqgsyzrwx4qzmgq0xwwk4vz2r6vtsykgz089jncvycmem3535zjwvvtvjw8v98y0d5ydwte575gjm7a7k"; + #[cfg(feature = "transparent-inputs")] + assert_eq!(encoded, encoded_with_t); + #[cfg(not(feature = "transparent-inputs"))] + assert_eq!(encoded, _encoded_no_t); + + let decoded = UnifiedFullViewingKey::decode(&MAIN_NETWORK, &encoded).unwrap(); + let reencoded = decoded.encode(&MAIN_NETWORK); + assert_eq!(encoded, reencoded); + #[cfg(feature = "transparent-inputs")] assert_eq!( decoded.transparent.map(|t| t.serialize()), - ufvk.transparent.map(|t| t.serialize()), + ufvk.transparent.as_ref().map(|t| t.serialize()), ); assert_eq!( decoded.sapling.map(|s| s.to_bytes()), @@ -434,5 +453,14 @@ mod tests { decoded.orchard.map(|o| o.to_bytes()), ufvk.orchard.map(|o| o.to_bytes()), ); + + let decoded_with_t = UnifiedFullViewingKey::decode(&MAIN_NETWORK, encoded_with_t).unwrap(); + #[cfg(feature = "transparent-inputs")] + assert_eq!( + decoded_with_t.transparent.map(|t| t.serialize()), + ufvk.transparent.as_ref().map(|t| t.serialize()), + ); + #[cfg(not(feature = "transparent-inputs"))] + assert_eq!(decoded_with_t.unknown.len(), 1); } } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index f9f1fe7b4..d3fc28555 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -728,6 +728,7 @@ mod tests { use protobuf::Message; use rand_core::{OsRng, RngCore}; use rusqlite::params; + use std::collections::HashMap; use zcash_client_backend::{ keys::{sapling, UnifiedFullViewingKey}, @@ -809,7 +810,8 @@ mod tests { ) .unwrap(); - init_accounts_table(db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account, ufvk)]); + init_accounts_table(db_data, &ufvks).unwrap(); (dfvk, taddr) } diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 4a6cf8075..76b26ac5c 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -1,10 +1,12 @@ //! Functions for initializing the various databases. use rusqlite::{params, types::ToSql, NO_PARAMS}; +use std::collections::HashMap; use zcash_primitives::{ block::BlockHash, consensus::{self, BlockHeight}, + zip32::AccountId, }; use zcash_client_backend::keys::UnifiedFullViewingKey; @@ -154,6 +156,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { /// # #[cfg(feature = "transparent-inputs")] /// # { /// use tempfile::NamedTempFile; +/// use std::collections::HashMap; /// /// use zcash_primitives::{ /// consensus::{Network, Parameters}, @@ -181,7 +184,8 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { /// let extsk = sapling::spending_key(&seed, Network::TestNetwork.coin_type(), account); /// let dfvk = ExtendedFullViewingKey::from(&extsk).into(); /// let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk), None).unwrap(); -/// init_accounts_table(&db_data, &[ufvk]).unwrap(); +/// let ufvks = HashMap::from([(account, ufvk)]); +/// init_accounts_table(&db_data, &ufvks).unwrap(); /// # } /// ``` /// @@ -190,7 +194,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { /// [`create_spend_to_address`]: zcash_client_backend::data_api::wallet::create_spend_to_address pub fn init_accounts_table( wdb: &WalletDb

, - keys: &[UnifiedFullViewingKey], + keys: &HashMap, ) -> Result<(), SqliteClientError> { let mut empty_check = wdb.conn.prepare("SELECT * FROM accounts LIMIT 1")?; if empty_check.exists(NO_PARAMS)? { @@ -199,7 +203,7 @@ pub fn init_accounts_table( // Insert accounts atomically wdb.conn.execute("BEGIN IMMEDIATE", NO_PARAMS)?; - for (account, key) in (0u32..).zip(keys) { + for (account, key) in keys.iter() { let ufvk_str: String = key.encode(&wdb.params); let address_str: String = key.default_address().0.encode(&wdb.params); #[cfg(feature = "transparent-inputs")] @@ -214,7 +218,7 @@ pub fn init_accounts_table( wdb.conn.execute( "INSERT INTO accounts (account, ufvk, address, transparent_address) VALUES (?, ?, ?, ?)", - params![account, ufvk_str, address_str, taddress_str], + params![::from(*account), ufvk_str, address_str, taddress_str], )?; } wdb.conn.execute("COMMIT", NO_PARAMS)?; @@ -283,6 +287,7 @@ pub fn init_blocks_table

( #[cfg(test)] #[allow(deprecated)] mod tests { + use std::collections::HashMap; use tempfile::NamedTempFile; use zcash_client_backend::keys::{sapling, UnifiedFullViewingKey, UnifiedSpendingKey}; @@ -312,8 +317,8 @@ mod tests { init_wallet_db(&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, &HashMap::new()).unwrap(); + init_accounts_table(&db_data, &HashMap::new()).unwrap(); let seed = [0u8; 32]; let account = AccountId::from(0); @@ -336,12 +341,13 @@ mod tests { #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk), None).unwrap(); + let ufvks = HashMap::from([(account, ufvk)]); - init_accounts_table(&db_data, &[ufvk.clone()]).unwrap(); + init_accounts_table(&db_data, &ufvks).unwrap(); // Subsequent calls should return an error - init_accounts_table(&db_data, &[]).unwrap_err(); - init_accounts_table(&db_data, &[ufvk]).unwrap_err(); + init_accounts_table(&db_data, &HashMap::new()).unwrap_err(); + init_accounts_table(&db_data, &ufvks).unwrap_err(); } #[test] @@ -384,7 +390,8 @@ mod tests { let usk = UnifiedSpendingKey::from_seed(&tests::network(), &seed, account_id).unwrap(); let ufvk = usk.to_unified_full_viewing_key(); let expected_address = ufvk.sapling().unwrap().default_address().1; - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); // The account's address should be in the data DB let pa = get_address(&db_data, AccountId::from(0)).unwrap(); diff --git a/zcash_client_sqlite/src/wallet/transact.rs b/zcash_client_sqlite/src/wallet/transact.rs index 8b703dfc6..d20b74edb 100644 --- a/zcash_client_sqlite/src/wallet/transact.rs +++ b/zcash_client_sqlite/src/wallet/transact.rs @@ -157,6 +157,7 @@ pub fn select_spendable_sapling_notes

( #[allow(deprecated)] mod tests { use rusqlite::Connection; + use std::collections::HashMap; use tempfile::NamedTempFile; use zcash_proofs::prover::LocalTxProver; @@ -207,32 +208,45 @@ mod tests { let db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); init_wallet_db(&db_data).unwrap(); + let acct0 = AccountId::from(0); + let acct1 = AccountId::from(1); + // Add two accounts to the wallet - let extsk0 = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); - let extsk1 = sapling::spending_key(&[1u8; 32], network().coin_type(), AccountId::from(1)); + let extsk0 = sapling::spending_key(&[0u8; 32], network().coin_type(), acct0); + let extsk1 = sapling::spending_key(&[1u8; 32], network().coin_type(), acct1); let dfvk0 = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk0)); let dfvk1 = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk1)); #[cfg(feature = "transparent-inputs")] let ufvks = { let tsk0 = - transparent::AccountPrivKey::from_seed(&network(), &[0u8; 32], AccountId::from(0)) - .unwrap(); + transparent::AccountPrivKey::from_seed(&network(), &[0u8; 32], acct0).unwrap(); let tsk1 = - transparent::AccountPrivKey::from_seed(&network(), &[1u8; 32], AccountId::from(1)) - .unwrap(); - [ - UnifiedFullViewingKey::new(Some(tsk0.to_account_pubkey()), Some(dfvk0), None) - .unwrap(), - UnifiedFullViewingKey::new(Some(tsk1.to_account_pubkey()), Some(dfvk1), None) - .unwrap(), - ] + transparent::AccountPrivKey::from_seed(&network(), &[1u8; 32], acct1).unwrap(); + HashMap::from([ + ( + acct0, + UnifiedFullViewingKey::new(Some(tsk0.to_account_pubkey()), Some(dfvk0), None) + .unwrap(), + ), + ( + acct1, + UnifiedFullViewingKey::new(Some(tsk1.to_account_pubkey()), Some(dfvk1), None) + .unwrap(), + ), + ]) }; #[cfg(not(feature = "transparent-inputs"))] - let ufvks = [ - UnifiedFullViewingKey::new(Some(dfvk0), None).unwrap(), - UnifiedFullViewingKey::new(Some(dfvk1), None).unwrap(), - ]; + let ufvks = HashMap::from([ + ( + acct0, + UnifiedFullViewingKey::new(Some(dfvk0), None).unwrap(), + ), + ( + acct1, + UnifiedFullViewingKey::new(Some(dfvk1), None).unwrap(), + ), + ]); init_accounts_table(&db_data, &ufvks).unwrap(); let to = extsk0.default_address().1.into(); @@ -279,14 +293,16 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); + let account_id = AccountId::from(0); + let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), account_id); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk)); #[cfg(feature = "transparent-inputs")] let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk), None).unwrap(); #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk), None).unwrap(); - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); let to = extsk.default_address().1.into(); // We cannot do anything if we aren't synchronised @@ -323,13 +339,15 @@ mod tests { .unwrap(); // Add an account to the wallet - let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); + let account_id = AccountId::from(0); + let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), account_id); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk)); #[cfg(feature = "transparent-inputs")] let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk), None).unwrap(); #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk), None).unwrap(); - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); let to = extsk.default_address().1.into(); // Account balance should be zero @@ -371,13 +389,15 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); + let account_id = AccountId::from(0); + let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), account_id); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk)); #[cfg(feature = "transparent-inputs")] let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone()), None).unwrap(); #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone()), None).unwrap(); - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); @@ -504,13 +524,15 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); + let account_id = AccountId::from(0); + let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), account_id); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk)); #[cfg(feature = "transparent-inputs")] let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone()), None).unwrap(); #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone()), None).unwrap(); - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); @@ -633,13 +655,15 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = sapling::spending_key(&[0u8; 32], network.coin_type(), AccountId::from(0)); + let account_id = AccountId::from(0); + let extsk = sapling::spending_key(&[0u8; 32], network.coin_type(), account_id); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk)); #[cfg(feature = "transparent-inputs")] let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone()), None).unwrap(); #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone()), None).unwrap(); - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); @@ -743,13 +767,15 @@ mod tests { init_wallet_db(&db_data).unwrap(); // Add an account to the wallet - let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); + let account_id = AccountId::from(0); + let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), account_id); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk)); #[cfg(feature = "transparent-inputs")] let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone()), None).unwrap(); #[cfg(not(feature = "transparent-inputs"))] let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone()), None).unwrap(); - init_accounts_table(&db_data, &[ufvk]).unwrap(); + let ufvks = HashMap::from([(account_id, ufvk)]); + init_accounts_table(&db_data, &ufvks).unwrap(); // Add funds to the wallet in a single note let value = Amount::from_u64(51000).unwrap();