From 3dfd47814192925c4e57df50cfcdb8a0771e3889 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 5 Dec 2023 12:38:06 +0000 Subject: [PATCH] Migrate to published `zip32` crate outside this repository --- Cargo.lock | 5 +- Cargo.toml | 3 +- .../examples/diversify-address.rs | 9 +- zcash_client_backend/src/address.rs | 4 +- zcash_client_backend/src/data_api.rs | 2 +- zcash_client_backend/src/encoding.rs | 4 +- zcash_client_backend/src/keys.rs | 21 +- zcash_client_backend/src/scanning.rs | 17 +- zcash_client_sqlite/src/chain.rs | 24 +-- zcash_client_sqlite/src/lib.rs | 18 +- zcash_client_sqlite/src/wallet.rs | 57 +++-- zcash_client_sqlite/src/wallet/init.rs | 8 +- .../init/migrations/add_transaction_views.rs | 6 +- .../init/migrations/add_utxo_account.rs | 27 +-- .../wallet/init/migrations/addresses_table.rs | 4 +- .../migrations/received_notes_nullable_nf.rs | 5 +- .../migrations/sapling_memo_consistency.rs | 10 +- .../wallet/init/migrations/ufvk_support.rs | 4 +- .../init/migrations/v_transactions_net.rs | 14 +- .../v_transactions_note_uniqueness.rs | 5 +- zcash_client_sqlite/src/wallet/sapling.rs | 50 +++-- zcash_primitives/src/sapling/zip32.rs | 28 ++- zcash_primitives/src/transaction/builder.rs | 2 +- zip32/.gitignore | 3 - zip32/COPYRIGHT | 14 -- zip32/Cargo.toml | 22 -- zip32/LICENSE-APACHE | 201 ------------------ zip32/LICENSE-MIT | 23 -- zip32/README.md | 17 -- zip32/src/fingerprint.rs | 80 ------- zip32/src/lib.rs | 190 ----------------- 31 files changed, 173 insertions(+), 704 deletions(-) delete mode 100644 zip32/.gitignore delete mode 100644 zip32/COPYRIGHT delete mode 100644 zip32/Cargo.toml delete mode 100644 zip32/LICENSE-APACHE delete mode 100644 zip32/LICENSE-MIT delete mode 100644 zip32/README.md delete mode 100644 zip32/src/fingerprint.rs delete mode 100644 zip32/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0dbabbf1e..64b0d408a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3181,9 +3181,10 @@ dependencies = [ [[package]] name = "zip32" -version = "0.0.0" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22" dependencies = [ - "assert_matches", "blake2b_simd", "memuse", "subtle", diff --git a/Cargo.toml b/Cargo.toml index a70708d5e..555e8ca4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ members = [ "zcash_history", "zcash_primitives", "zcash_proofs", - "zip32", ] [workspace.package] @@ -34,7 +33,6 @@ zcash_encoding = { version = "0.2", path = "components/zcash_encoding" } zcash_note_encryption = "0.4" zcash_primitives = { version = "0.13", path = "zcash_primitives", default-features = false } zcash_proofs = { version = "0.13", path = "zcash_proofs", default-features = false } -zip32 = { version = "0.0", path = "zip32" } # Shielded protocols ff = "0.13" @@ -104,6 +102,7 @@ rand_xorshift = "0.3" # ZIP 32 aes = "0.8" fpe = "0.6" +zip32 = "0.1" [profile.release] lto = true diff --git a/zcash_client_backend/examples/diversify-address.rs b/zcash_client_backend/examples/diversify-address.rs index 30b8339b3..6dedfc2d3 100644 --- a/zcash_client_backend/examples/diversify-address.rs +++ b/zcash_client_backend/examples/diversify-address.rs @@ -18,16 +18,11 @@ fn parse_viewing_key(s: &str) -> Result<(ExtendedFullViewingKey, bool), &'static fn parse_diversifier_index(s: &str) -> Result { let i: u128 = s.parse().map_err(|_| "Diversifier index is not a number")?; - if i >= (1 << 88) { - return Err("Diversifier index too large"); - } - Ok(DiversifierIndex(i.to_le_bytes()[..11].try_into().unwrap())) + DiversifierIndex::try_from(i).map_err(|_| "Diversifier index too large") } fn encode_diversifier_index(di: &DiversifierIndex) -> u128 { - let mut bytes = [0; 16]; - bytes[..11].copy_from_slice(&di.0); - u128::from_le_bytes(bytes) + (*di).into() } #[derive(Debug, Options)] diff --git a/zcash_client_backend/src/address.rs b/zcash_client_backend/src/address.rs index 87e1ac0fe..d2386ddf6 100644 --- a/zcash_client_backend/src/address.rs +++ b/zcash_client_backend/src/address.rs @@ -253,7 +253,7 @@ impl RecipientAddress { #[cfg(test)] mod tests { use zcash_address::test_vectors; - use zcash_primitives::consensus::MAIN_NETWORK; + use zcash_primitives::{consensus::MAIN_NETWORK, zip32::AccountId}; use super::{RecipientAddress, UnifiedAddress}; use crate::keys::sapling; @@ -267,7 +267,7 @@ mod tests { }; let sapling = { - let extsk = sapling::spending_key(&[0; 32], 0, 0.into()); + let extsk = sapling::spending_key(&[0; 32], 0, AccountId::ZERO); let dfvk = extsk.to_diversifiable_full_viewing_key(); Some(dfvk.default_address().1) }; diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 65ce0f238..714a9724b 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -1193,7 +1193,7 @@ pub mod testing { seed: &SecretVec, _birthday: AccountBirthday, ) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> { - let account = AccountId::from(0); + let account = AccountId::ZERO; UnifiedSpendingKey::from_seed(&self.network, seed.expose_secret(), account) .map(|k| (account, k)) .map_err(|_| ()) diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 66c5d69cb..78859abbf 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -185,7 +185,7 @@ impl AddressCodec

for UnifiedAddress { /// keys::sapling, /// }; /// -/// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::from(0)); +/// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::ZERO); /// let encoded = encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, &extsk); /// ``` /// [`ExtendedSpendingKey`]: zcash_primitives::sapling::zip32::ExtendedSpendingKey @@ -218,7 +218,7 @@ pub fn decode_extended_spending_key( /// keys::sapling, /// }; /// -/// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::from(0)); +/// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::ZERO); /// let extfvk = extsk.to_extended_full_viewing_key(); /// let encoded = encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk); /// ``` diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index d1e1cc01c..af8775432 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -51,7 +51,7 @@ pub mod sapling { /// keys::sapling, /// }; /// - /// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::from(0)); + /// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::ZERO); /// ``` /// [`ExtendedSpendingKey`]: zcash_primitives::sapling::zip32::ExtendedSpendingKey pub fn spending_key(seed: &[u8], coin_type: u32, account: AccountId) -> ExtendedSpendingKey { @@ -72,7 +72,7 @@ pub mod sapling { #[cfg(feature = "transparent-inputs")] fn to_transparent_child_index(j: DiversifierIndex) -> Option { - let (low_4_bytes, rest) = j.0.split_at(4); + let (low_4_bytes, rest) = j.as_bytes().split_at(4); let transparent_j = u32::from_le_bytes(low_4_bytes.try_into().unwrap()); if transparent_j > (0x7FFFFFFF) || rest.iter().any(|b| b != &0) { None @@ -588,7 +588,11 @@ pub mod testing { prop::array::uniform32(prop::num::u8::ANY).prop_flat_map(move |seed| { prop::num::u32::ANY .prop_map(move |account| { - UnifiedSpendingKey::from_seed(¶ms, &seed, AccountId::from(account)) + UnifiedSpendingKey::from_seed( + ¶ms, + &seed, + AccountId::try_from(account & ((1 << 31) - 1)).unwrap(), + ) }) .prop_filter("seeds must generate valid USKs", |v| v.is_ok()) .prop_map(|v| v.unwrap()) @@ -632,14 +636,14 @@ mod tests { #[test] #[should_panic] fn spending_key_panics_on_short_seed() { - let _ = sapling::spending_key(&[0; 31][..], 0, AccountId::from(0)); + let _ = sapling::spending_key(&[0; 31][..], 0, AccountId::ZERO); } #[cfg(feature = "transparent-inputs")] #[test] fn pk_to_taddr() { let taddr = - legacy::keys::AccountPrivKey::from_seed(&MAIN_NETWORK, &seed(), AccountId::from(0)) + legacy::keys::AccountPrivKey::from_seed(&MAIN_NETWORK, &seed(), AccountId::ZERO) .unwrap() .to_account_pubkey() .derive_external_ivk() @@ -652,7 +656,7 @@ mod tests { #[test] fn ufvk_round_trip() { - let account = 0.into(); + let account = AccountId::ZERO; let orchard = { let sk = orchard::keys::SpendingKey::from_zip32_seed(&[0; 32], 0, 0).unwrap(); @@ -666,8 +670,7 @@ mod tests { #[cfg(feature = "transparent-inputs")] let transparent = { - let privkey = - AccountPrivKey::from_seed(&MAIN_NETWORK, &[0; 32], AccountId::from(0)).unwrap(); + let privkey = AccountPrivKey::from_seed(&MAIN_NETWORK, &[0; 32], account).unwrap(); Some(privkey.to_account_pubkey()) }; @@ -726,7 +729,7 @@ mod tests { let usk = UnifiedSpendingKey::from_seed( &MAIN_NETWORK, &tv.root_seed, - AccountId::from(tv.account), + AccountId::try_from(tv.account).unwrap(), ) .expect("seed produced a valid unified spending key"); diff --git a/zcash_client_backend/src/scanning.rs b/zcash_client_backend/src/scanning.rs index 380e41a19..44a3f07bf 100644 --- a/zcash_client_backend/src/scanning.rs +++ b/zcash_client_backend/src/scanning.rs @@ -464,10 +464,9 @@ pub(crate) fn scan_block_with_runner< let spend = nullifiers .iter() .map(|&(account, nf)| CtOption::new(account, nf.ct_eq(&spend_nf))) - .fold( - CtOption::new(AccountId::from(0), 0.into()), - |first, next| CtOption::conditional_select(&next, &first, first.is_some()), - ) + .fold(CtOption::new(AccountId::ZERO, 0.into()), |first, next| { + CtOption::conditional_select(&next, &first, first.is_some()) + }) .map(|account| WalletSaplingSpend::from_parts(index, spend_nf, account)); if spend.is_some().into() { @@ -804,7 +803,7 @@ mod tests { #[test] fn scan_block_with_my_tx() { fn go(scan_multithreaded: bool) { - let account = AccountId::from(0); + let account = AccountId::ZERO; let extsk = ExtendedSpendingKey::master(&[]); let dfvk = extsk.to_diversifiable_full_viewing_key(); @@ -889,7 +888,7 @@ mod tests { #[test] fn scan_block_with_txs_after_my_tx() { fn go(scan_multithreaded: bool) { - let account = AccountId::from(0); + let account = AccountId::ZERO; let extsk = ExtendedSpendingKey::master(&[]); let dfvk = extsk.to_diversifiable_full_viewing_key(); @@ -924,7 +923,7 @@ mod tests { let scanned_block = scan_block_with_runner( &Network::TestNetwork, cb, - &[(&AccountId::from(0), &dfvk)], + &[(&AccountId::ZERO, &dfvk)], &[], None, batch_runner.as_mut(), @@ -938,7 +937,7 @@ mod tests { assert_eq!(tx.sapling_spends.len(), 0); assert_eq!(tx.sapling_outputs.len(), 1); assert_eq!(tx.sapling_outputs[0].index(), 0); - assert_eq!(tx.sapling_outputs[0].account(), AccountId::from(0)); + assert_eq!(tx.sapling_outputs[0].account(), AccountId::ZERO); assert_eq!(tx.sapling_outputs[0].note().value().inner(), 5); assert_eq!( @@ -967,7 +966,7 @@ mod tests { let extsk = ExtendedSpendingKey::master(&[]); let dfvk = extsk.to_diversifiable_full_viewing_key(); let nf = Nullifier([7; 32]); - let account = AccountId::from(12); + let account = AccountId::try_from(12).unwrap(); let cb = fake_compact_block( 1u32.into(), diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index e49312a45..8a9d582b5 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -455,7 +455,7 @@ mod tests { // Account balance should reflect both received notes assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value + value2).unwrap() ); @@ -466,7 +466,7 @@ mod tests { // Account balance should be unaltered assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value + value2).unwrap() ); @@ -476,14 +476,14 @@ mod tests { .unwrap(); // Account balance should only contain the first received note - assert_eq!(st.get_total_balance(AccountId::from(0)), value); + assert_eq!(st.get_total_balance(AccountId::ZERO), value); // Scan the cache again st.scan_cached_blocks(h, 2); // Account balance should again reflect both received notes assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value + value2).unwrap() ); } @@ -502,7 +502,7 @@ mod tests { let value = NonNegativeAmount::const_from_u64(50000); let (h1, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); st.scan_cached_blocks(h1, 1); - assert_eq!(st.get_total_balance(AccountId::from(0)), value); + assert_eq!(st.get_total_balance(AccountId::ZERO), value); // Create blocks to reach SAPLING_ACTIVATION_HEIGHT + 2 let (h2, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); @@ -514,7 +514,7 @@ mod tests { // Now scan the block of height SAPLING_ACTIVATION_HEIGHT + 1 st.scan_cached_blocks(h2, 1); assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), NonNegativeAmount::const_from_u64(150_000) ); @@ -567,7 +567,7 @@ mod tests { assert_eq!(summary.received_sapling_note_count(), 1); // Account balance should reflect the received note - assert_eq!(st.get_total_balance(AccountId::from(0)), value); + assert_eq!(st.get_total_balance(AccountId::ZERO), value); // Create a second fake CompactBlock sending more value to the address let value2 = NonNegativeAmount::const_from_u64(7); @@ -581,7 +581,7 @@ mod tests { // Account balance should reflect both received notes assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value + value2).unwrap() ); } @@ -606,7 +606,7 @@ mod tests { st.scan_cached_blocks(received_height, 1); // Account balance should reflect the received note - assert_eq!(st.get_total_balance(AccountId::from(0)), value); + assert_eq!(st.get_total_balance(AccountId::ZERO), value); // Create a second fake CompactBlock spending value from the address let extsk2 = ExtendedSpendingKey::master(&[0]); @@ -619,7 +619,7 @@ mod tests { // Account balance should equal the change assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value - value2).unwrap() ); } @@ -652,7 +652,7 @@ mod tests { // Account balance should equal the change assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value - value2).unwrap() ); @@ -661,7 +661,7 @@ mod tests { // Account balance should be the same. assert_eq!( - st.get_total_balance(AccountId::from(0)), + st.get_total_balance(AccountId::ZERO), (value - value2).unwrap() ); } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 596db68fc..0b02b7a1f 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -384,12 +384,9 @@ impl WalletWrite for WalletDb ) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> { self.transactionally(|wdb| { let account = wallet::get_max_account_id(wdb.conn.0)? - .map(|a| AccountId::from(u32::from(a) + 1)) - .unwrap_or_else(|| AccountId::from(0)); - - if u32::from(account) >= 0x7FFFFFFF { - return Err(SqliteClientError::AccountIdOutOfRange); - } + .map(|a| a.next().ok_or(SqliteClientError::AccountIdOutOfRange)) + .transpose()? + .unwrap_or_else(|| AccountId::ZERO); let usk = UnifiedSpendingKey::from_seed(&wdb.params, seed.expose_secret(), account) .map_err(|_| SqliteClientError::KeyDerivationError(account))?; @@ -1130,7 +1127,7 @@ mod tests { .with_test_account(AccountBirthday::from_sapling_activation) .build(); - let account = AccountId::from(0); + let account = AccountId::ZERO; let current_addr = st.wallet().get_current_address(account).unwrap(); assert!(current_addr.is_some()); @@ -1155,7 +1152,10 @@ mod tests { let ufvk = usk.to_unified_full_viewing_key(); let (taddr, _) = usk.default_transparent_address(); - let receivers = st.wallet().get_transparent_receivers(0.into()).unwrap(); + let receivers = st + .wallet() + .get_transparent_receivers(AccountId::ZERO) + .unwrap(); // The receiver for the default UA should be in the set. assert!(receivers.contains_key(ufvk.default_address().0.transparent().unwrap())); @@ -1174,7 +1174,7 @@ mod tests { // Generate some fake CompactBlocks. let seed = [0u8; 32]; - let account = AccountId::from(0); + let account = AccountId::ZERO; let extsk = sapling::spending_key(&seed, st.wallet().params.coin_type(), account); let dfvk = extsk.to_diversifiable_full_viewing_key(); let (h1, meta1, _) = st.generate_next_block( diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 32f6ac999..fe390f3f2 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -152,11 +152,13 @@ pub(crate) fn get_max_account_id( conn: &rusqlite::Connection, ) -> Result, SqliteClientError> { // This returns the most recently generated address. - conn.query_row("SELECT MAX(account) FROM accounts", [], |row| { + conn.query_row_and_then("SELECT MAX(account) FROM accounts", [], |row| { let account_id: Option = row.get(0)?; - Ok(account_id.map(AccountId::from)) + account_id + .map(AccountId::try_from) + .transpose() + .map_err(|_| SqliteClientError::AccountIdOutOfRange) }) - .map_err(SqliteClientError::from) } pub(crate) fn add_account( @@ -283,7 +285,7 @@ pub(crate) fn get_current_address( addr_str, ))), }) - .map(|addr| (addr, DiversifierIndex(di_be))) + .map(|addr| (addr, DiversifierIndex::from(di_be))) }) .transpose() } @@ -295,7 +297,7 @@ pub(crate) fn insert_address( conn: &rusqlite::Connection, params: &P, account: AccountId, - mut diversifier_index: DiversifierIndex, + diversifier_index: DiversifierIndex, address: &UnifiedAddress, ) -> Result<(), rusqlite::Error> { let mut stmt = conn.prepare_cached( @@ -314,10 +316,11 @@ pub(crate) fn insert_address( )?; // the diversifier index is stored in big-endian order to allow sorting - diversifier_index.0.reverse(); + let mut di_be = *diversifier_index.as_bytes(); + di_be.reverse(); stmt.execute(named_params![ ":account": &u32::from(account), - ":diversifier_index_be": &&diversifier_index.0[..], + ":diversifier_index_be": &di_be[..], ":address": &address.encode(params), ":cached_transparent_receiver_address": &address.transparent().map(|r| r.encode(params)), ])?; @@ -363,7 +366,7 @@ pub(crate) fn get_transparent_receivers( if let Some(taddr) = ua.transparent() { ret.insert( *taddr, - AddressMetadata::new(account, DiversifierIndex(di_be)), + AddressMetadata::new(account, DiversifierIndex::from(di_be)), ); } } @@ -422,7 +425,7 @@ pub(crate) fn get_unified_full_viewing_keys( let rows = stmt_fetch_accounts.query_map([], |row| { let acct: u32 = row.get(0)?; - let account = AccountId::from(acct); + let account = AccountId::try_from(acct).map_err(|_| SqliteClientError::AccountIdOutOfRange); let ufvk_str: String = row.get(1)?; let ufvk = UnifiedFullViewingKey::decode(params, &ufvk_str) .map_err(SqliteClientError::CorruptedData); @@ -433,7 +436,7 @@ pub(crate) fn get_unified_full_viewing_keys( let mut res: HashMap = HashMap::new(); for row in rows { let (account_id, ufvkr) = row?; - res.insert(account_id, ufvkr?); + res.insert(account_id?, ufvkr?); } Ok(res) @@ -451,11 +454,12 @@ pub(crate) fn get_account_for_ufvk( [&ufvk.encode(params)], |row| { let acct: u32 = row.get(0)?; - Ok(AccountId::from(acct)) + Ok(AccountId::try_from(acct).map_err(|_| SqliteClientError::AccountIdOutOfRange)) }, ) .optional() - .map_err(SqliteClientError::from) + .map_err(SqliteClientError::from)? + .transpose() } pub(crate) trait ScanProgress { @@ -598,9 +602,10 @@ pub(crate) fn get_wallet_summary( let mut stmt_accounts = conn.prepare_cached("SELECT account FROM accounts")?; let mut account_balances = stmt_accounts .query([])? - .mapped(|row| { - row.get::<_, u32>(0) - .map(|a| (AccountId::from(a), AccountBalance::ZERO)) + .and_then(|row| { + AccountId::try_from(row.get::<_, u32>(0)?) + .map_err(|_| SqliteClientError::AccountIdOutOfRange) + .map(|a| (a, AccountBalance::ZERO)) }) .collect::, _>>()?; @@ -622,7 +627,8 @@ pub(crate) fn get_wallet_summary( let mut rows = stmt_select_notes.query(named_params![":summary_height": u32::from(summary_height)])?; while let Some(row) = rows.next()? { - let account = row.get::<_, u32>(0).map(AccountId::from)?; + let account = AccountId::try_from(row.get::<_, u32>(0)?) + .map_err(|_| SqliteClientError::AccountIdOutOfRange)?; let value_raw = row.get::<_, i64>(1)?; let value = NonNegativeAmount::from_nonnegative_i64(value_raw).map_err(|_| { @@ -699,7 +705,8 @@ pub(crate) fn get_wallet_summary( ])?; while let Some(row) = rows.next()? { - let account = AccountId::from(row.get::<_, u32>(0)?); + let account = AccountId::try_from(row.get::<_, u32>(0)?) + .map_err(|_| SqliteClientError::AccountIdOutOfRange)?; let raw_value = row.get(1)?; let value = NonNegativeAmount::from_nonnegative_i64(raw_value).map_err(|_| { SqliteClientError::CorruptedData(format!("Negative UTXO value {:?}", raw_value)) @@ -1601,18 +1608,23 @@ pub(crate) fn put_received_transparent_utxo( .query_row( "SELECT account FROM addresses WHERE cached_transparent_receiver_address = :address", named_params![":address": &address_str], - |row| row.get::<_, u32>(0).map(AccountId::from), + |row| row.get::<_, u32>(0), ) .optional()?; let utxoid = if let Some(account) = account_id { - put_legacy_transparent_utxo(conn, params, output, account)? + put_legacy_transparent_utxo( + conn, + params, + output, + AccountId::try_from(account).map_err(|_| SqliteClientError::AccountIdOutOfRange)?, + )? } else { // If the UTXO is received at the legacy transparent address, there may be no entry in the // addresses table that can be used to tie the address to a particular account. In this // case, we should look up the legacy address for account 0 and check whether it matches // the address for the received UTXO, and if so then insert/update it directly. - let account = AccountId::from(0u32); + let account = AccountId::ZERO; get_legacy_transparent_address(params, conn, account).and_then(|legacy_taddr| { if legacy_taddr .iter() @@ -2003,13 +2015,14 @@ mod tests { // The default address is set for the test account assert_matches!( - st.wallet().get_current_address(AccountId::from(0)), + st.wallet().get_current_address(AccountId::ZERO), Ok(Some(_)) ); // No default address is set for an un-initialized account assert_matches!( - st.wallet().get_current_address(AccountId::from(1)), + st.wallet() + .get_current_address(AccountId::try_from(1).unwrap()), Ok(None) ); } diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 1de985dbf..1de2e687d 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -719,7 +719,7 @@ mod tests { let mut db_data = WalletDb::for_path(data_file.path(), Network::TestNetwork).unwrap(); let seed = [0xab; 32]; - let account = AccountId::from(0); + let account = AccountId::ZERO; let secret_key = sapling::spending_key(&seed, db_data.params.coin_type(), account); let extfvk = secret_key.to_extended_full_viewing_key(); @@ -890,7 +890,7 @@ mod tests { let mut db_data = WalletDb::for_path(data_file.path(), Network::TestNetwork).unwrap(); let seed = [0xab; 32]; - let account = AccountId::from(0); + let account = AccountId::ZERO; let secret_key = sapling::spending_key(&seed, db_data.params.coin_type(), account); let extfvk = secret_key.to_extended_full_viewing_key(); @@ -1043,7 +1043,7 @@ mod tests { let mut db_data = WalletDb::for_path(data_file.path(), Network::TestNetwork).unwrap(); let seed = [0xab; 32]; - let account = AccountId::from(0); + let account = AccountId::ZERO; let secret_key = UnifiedSpendingKey::from_seed(&db_data.params, &seed, account).unwrap(); init_main( @@ -1076,7 +1076,7 @@ mod tests { let (account, _usk) = db_data .create_account(&Secret::new(seed.to_vec()), birthday) .unwrap(); - assert_eq!(account, AccountId::from(0u32)); + assert_eq!(account, AccountId::ZERO); for tv in &test_vectors::UNIFIED[..3] { if let Some(RecipientAddress::Unified(tvua)) = 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 5d838aae2..31d0b438b 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 @@ -308,8 +308,7 @@ mod tests { let data_file = NamedTempFile::new().unwrap(); let mut db_data = WalletDb::for_path(data_file.path(), network).unwrap(); init_wallet_db_internal(&mut db_data, None, &[addresses_table::MIGRATION_ID]).unwrap(); - let usk = - UnifiedSpendingKey::from_seed(&network, &[0u8; 32][..], AccountId::from(0)).unwrap(); + let usk = UnifiedSpendingKey::from_seed(&network, &[0u8; 32][..], AccountId::ZERO).unwrap(); let ufvk = usk.to_unified_full_viewing_key(); db_data @@ -436,8 +435,7 @@ mod tests { let mut tx_bytes = vec![]; tx.write(&mut tx_bytes).unwrap(); - let usk = - UnifiedSpendingKey::from_seed(&network, &[0u8; 32][..], AccountId::from(0)).unwrap(); + let usk = UnifiedSpendingKey::from_seed(&network, &[0u8; 32][..], AccountId::ZERO).unwrap(); let ufvk = usk.to_unified_full_viewing_key(); let (ua, _) = ufvk.default_address(); let taddr = ufvk diff --git a/zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs b/zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs index 304a4490a..f7686a0eb 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs @@ -59,18 +59,21 @@ impl RusqliteMigration for Migration

{ let mut rows = stmt_fetch_accounts.query([])?; while let Some(row) = rows.next()? { let account: u32 = row.get(0)?; - let taddrs = - get_transparent_receivers(transaction, &self._params, AccountId::from(account)) - .map_err(|e| match e { - SqliteClientError::DbError(e) => WalletMigrationError::DbError(e), - SqliteClientError::CorruptedData(s) => { - WalletMigrationError::CorruptedData(s) - } - other => WalletMigrationError::CorruptedData(format!( - "Unexpected error in migration: {}", - other - )), - })?; + let taddrs = get_transparent_receivers( + transaction, + &self._params, + AccountId::try_from(account).map_err(|_| { + WalletMigrationError::CorruptedData("Account ID is invalid".to_owned()) + })?, + ) + .map_err(|e| match e { + SqliteClientError::DbError(e) => WalletMigrationError::DbError(e), + SqliteClientError::CorruptedData(s) => WalletMigrationError::CorruptedData(s), + other => WalletMigrationError::CorruptedData(format!( + "Unexpected error in migration: {}", + other + )), + })?; for (taddr, _) in taddrs { stmt_update_utxo_account.execute(named_params![ diff --git a/zcash_client_sqlite/src/wallet/init/migrations/addresses_table.rs b/zcash_client_sqlite/src/wallet/init/migrations/addresses_table.rs index 9456a4fcc..8c4129657 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/addresses_table.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/addresses_table.rs @@ -61,7 +61,9 @@ impl RusqliteMigration for Migration

{ let mut rows = stmt_fetch_accounts.query([])?; while let Some(row) = rows.next()? { let account: u32 = row.get(0)?; - let account = AccountId::from(account); + let account = AccountId::try_from(account).map_err(|_| { + WalletMigrationError::CorruptedData("Account ID is invalid".to_owned()) + })?; let ufvk_str: String = row.get(1)?; let ufvk = UnifiedFullViewingKey::decode(&self.params, &ufvk_str) diff --git a/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs b/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs index ff7b14e97..fb54fa135 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs @@ -242,9 +242,8 @@ mod tests { init_wallet_db_internal(&mut db_data, None, &[v_transactions_net::MIGRATION_ID]).unwrap(); // Create an account in the wallet - let usk0 = - UnifiedSpendingKey::from_seed(&db_data.params, &[0u8; 32][..], AccountId::from(0)) - .unwrap(); + let usk0 = UnifiedSpendingKey::from_seed(&db_data.params, &[0u8; 32][..], AccountId::ZERO) + .unwrap(); let ufvk0 = usk0.to_unified_full_viewing_key(); db_data .conn diff --git a/zcash_client_sqlite/src/wallet/init/migrations/sapling_memo_consistency.rs b/zcash_client_sqlite/src/wallet/init/migrations/sapling_memo_consistency.rs index 0d8c17b72..07b4faa0a 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/sapling_memo_consistency.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/sapling_memo_consistency.rs @@ -71,10 +71,12 @@ impl RusqliteMigration for Migration

{ )) })?; - tx_sent_notes - .entry((id_tx, txid)) - .or_default() - .insert(AccountId::from(account), ufvk); + tx_sent_notes.entry((id_tx, txid)).or_default().insert( + AccountId::try_from(account).map_err(|_| { + WalletMigrationError::CorruptedData("Account ID is invalid".to_owned()) + })?, + ufvk, + ); } let mut stmt_update_sent_memo = transaction.prepare( diff --git a/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs b/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs index 889d8d0ae..25e229df3 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs @@ -73,7 +73,9 @@ impl RusqliteMigration for Migration

{ // migration is being used to initialize an empty database. if let Some(seed) = &self.seed { let account: u32 = row.get(0)?; - let account = AccountId::from(account); + let account = AccountId::try_from(account).map_err(|_| { + WalletMigrationError::CorruptedData("Account ID is invalid".to_owned()) + })?; let usk = UnifiedSpendingKey::from_seed(&self.params, seed.expose_secret(), account) .unwrap(); diff --git a/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs b/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs index c6ad76403..95d7c33d0 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs @@ -222,9 +222,8 @@ mod tests { .unwrap(); // Create two accounts in the wallet. - let usk0 = - UnifiedSpendingKey::from_seed(&db_data.params, &[0u8; 32][..], AccountId::from(0)) - .unwrap(); + let usk0 = UnifiedSpendingKey::from_seed(&db_data.params, &[0u8; 32][..], AccountId::ZERO) + .unwrap(); let ufvk0 = usk0.to_unified_full_viewing_key(); db_data .conn @@ -234,9 +233,12 @@ mod tests { ) .unwrap(); - let usk1 = - UnifiedSpendingKey::from_seed(&db_data.params, &[1u8; 32][..], AccountId::from(1)) - .unwrap(); + let usk1 = UnifiedSpendingKey::from_seed( + &db_data.params, + &[1u8; 32][..], + AccountId::try_from(1).unwrap(), + ) + .unwrap(); let ufvk1 = usk1.to_unified_full_viewing_key(); db_data .conn diff --git a/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_note_uniqueness.rs b/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_note_uniqueness.rs index 3636ac93f..04d00296d 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_note_uniqueness.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_note_uniqueness.rs @@ -178,9 +178,8 @@ mod tests { init_wallet_db_internal(&mut db_data, None, &[v_transactions_net::MIGRATION_ID]).unwrap(); // Create an account in the wallet - let usk0 = - UnifiedSpendingKey::from_seed(&db_data.params, &[0u8; 32][..], AccountId::from(0)) - .unwrap(); + let usk0 = UnifiedSpendingKey::from_seed(&db_data.params, &[0u8; 32][..], AccountId::ZERO) + .unwrap(); let ufvk0 = usk0.to_unified_full_viewing_key(); db_data .conn diff --git a/zcash_client_sqlite/src/wallet/sapling.rs b/zcash_client_sqlite/src/wallet/sapling.rs index 93b890721..c595a9685 100644 --- a/zcash_client_sqlite/src/wallet/sapling.rs +++ b/zcash_client_sqlite/src/wallet/sapling.rs @@ -293,13 +293,12 @@ pub(crate) fn get_sapling_nullifiers( WHERE block IS NULL AND nf IS NOT NULL", )?; - let nullifiers = stmt_fetch_nullifiers.query_map([], |row| { + let nullifiers = stmt_fetch_nullifiers.query_and_then([], |row| { let account: u32 = row.get(1)?; let nf_bytes: Vec = row.get(2)?; - Ok(( - AccountId::from(account), - sapling::Nullifier::from_slice(&nf_bytes).unwrap(), - )) + AccountId::try_from(account) + .map_err(|_| SqliteClientError::AccountIdOutOfRange) + .map(|a| (a, sapling::Nullifier::from_slice(&nf_bytes).unwrap())) })?; let res: Vec<_> = nullifiers.collect::>()?; @@ -316,13 +315,12 @@ pub(crate) fn get_all_sapling_nullifiers( FROM sapling_received_notes rn WHERE nf IS NOT NULL", )?; - let nullifiers = stmt_fetch_nullifiers.query_map([], |row| { + let nullifiers = stmt_fetch_nullifiers.query_and_then([], |row| { let account: u32 = row.get(1)?; let nf_bytes: Vec = row.get(2)?; - Ok(( - AccountId::from(account), - sapling::Nullifier::from_slice(&nf_bytes).unwrap(), - )) + AccountId::try_from(account) + .map_err(|_| SqliteClientError::AccountIdOutOfRange) + .map(|a| (a, sapling::Nullifier::from_slice(&nf_bytes).unwrap())) })?; let res: Vec<_> = nullifiers.collect::>()?; @@ -637,7 +635,7 @@ pub(crate) mod tests { let to = dfvk.default_address().1.into(); // Create a USK that doesn't exist in the wallet - let acct1 = AccountId::from(1); + let acct1 = AccountId::try_from(1).unwrap(); let usk1 = UnifiedSpendingKey::from_seed(&st.network(), &[1u8; 32], acct1).unwrap(); // Attempting to spend with a USK that is not in the wallet results in an error @@ -1203,11 +1201,11 @@ pub(crate) mod tests { st.scan_cached_blocks(h, 1); // Spendable balance matches total balance - assert_eq!(st.get_total_balance(AccountId::from(0)), value); - assert_eq!(st.get_spendable_balance(AccountId::from(0), 1), value); + assert_eq!(st.get_total_balance(AccountId::ZERO), value); + assert_eq!(st.get_spendable_balance(AccountId::ZERO, 1), value); assert_eq!( - st.get_total_balance(AccountId::from(1)), - NonNegativeAmount::ZERO + st.get_total_balance(AccountId::try_from(1).unwrap()), + NonNegativeAmount::ZERO, ); let amount_sent = NonNegativeAmount::from_u64(20000).unwrap(); @@ -1257,15 +1255,18 @@ pub(crate) mod tests { let pending_change = (amount_left - amount_legacy_change).unwrap(); // The "legacy change" is not counted by get_pending_change(). - assert_eq!(st.get_pending_change(AccountId::from(0), 1), pending_change); + assert_eq!(st.get_pending_change(AccountId::ZERO, 1), pending_change); // We spent the only note so we only have pending change. - assert_eq!(st.get_total_balance(AccountId::from(0)), pending_change); + assert_eq!(st.get_total_balance(AccountId::ZERO), pending_change); let (h, _) = st.generate_next_block_including(txid); st.scan_cached_blocks(h, 1); - assert_eq!(st.get_total_balance(AccountId::from(1)), amount_sent); - assert_eq!(st.get_total_balance(AccountId::from(0)), amount_left); + assert_eq!( + st.get_total_balance(AccountId::try_from(1).unwrap()), + amount_sent, + ); + assert_eq!(st.get_total_balance(AccountId::ZERO), amount_left); st.reset(); @@ -1293,8 +1294,11 @@ pub(crate) mod tests { st.scan_cached_blocks(st.sapling_activation_height(), 2); - assert_eq!(st.get_total_balance(AccountId::from(1)), amount_sent); - assert_eq!(st.get_total_balance(AccountId::from(0)), amount_left); + assert_eq!( + st.get_total_balance(AccountId::try_from(1).unwrap()), + amount_sent, + ); + assert_eq!(st.get_total_balance(AccountId::ZERO), amount_left); } #[test] @@ -1514,7 +1518,7 @@ pub(crate) mod tests { // Verify that the received note is not considered spendable let spendable = select_spendable_sapling_notes( &st.wallet().conn, - AccountId::from(0), + AccountId::ZERO, Amount::const_from_i64(300000), received_tx_height + 10, &[], @@ -1529,7 +1533,7 @@ pub(crate) mod tests { // Verify that the received note is now considered spendable let spendable = select_spendable_sapling_notes( &st.wallet().conn, - AccountId::from(0), + AccountId::ZERO, Amount::const_from_i64(300000), received_tx_height + 10, &[], diff --git a/zcash_primitives/src/sapling/zip32.rs b/zcash_primitives/src/sapling/zip32.rs index 507ca1ee6..66365fa57 100644 --- a/zcash_primitives/src/sapling/zip32.rs +++ b/zcash_primitives/src/sapling/zip32.rs @@ -179,7 +179,7 @@ impl DiversifierKey { fn try_diversifier_internal(ff: &FF1, j: DiversifierIndex) -> Option { // Generate d_j let enc = ff - .encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.0[..])) + .encrypt(&[], &BinaryNumeralString::from_bytes_le(j.as_bytes())) .unwrap(); let mut d_j = [0; 11]; d_j.copy_from_slice(&enc.to_bytes_le()); @@ -206,9 +206,7 @@ impl DiversifierKey { let dec = ff .decrypt(&[], &BinaryNumeralString::from_bytes_le(&d.0[..])) .unwrap(); - let mut j = DiversifierIndex::new(); - j.0.copy_from_slice(&dec.to_bytes_le()); - j + DiversifierIndex::from(<[u8; 11]>::try_from(&dec.to_bytes_le()[..]).unwrap()) } /// Returns the first index starting from j that generates a valid @@ -855,9 +853,9 @@ mod tests { fn diversifier() { let dk = DiversifierKey([0; 32]); let j_0 = DiversifierIndex::new(); - let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - let j_2 = DiversifierIndex([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - let j_3 = DiversifierIndex([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_2 = DiversifierIndex::from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_3 = DiversifierIndex::from([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // Computed using this Rust implementation let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140]; let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34]; @@ -884,12 +882,12 @@ mod tests { let di32: u32 = 0xa0b0c0d0; assert_eq!( DiversifierIndex::from(di32), - DiversifierIndex([0xd0, 0xc0, 0xb0, 0xa0, 0, 0, 0, 0, 0, 0, 0]) + DiversifierIndex::from([0xd0, 0xc0, 0xb0, 0xa0, 0, 0, 0, 0, 0, 0, 0]) ); let di64: u64 = 0x0102030405060708; assert_eq!( DiversifierIndex::from(di64), - DiversifierIndex([8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0]) + DiversifierIndex::from([8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0]) ); } @@ -897,9 +895,9 @@ mod tests { fn find_diversifier() { let dk = DiversifierKey([0; 32]); let j_0 = DiversifierIndex::new(); - let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - let j_2 = DiversifierIndex([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - let j_3 = DiversifierIndex([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_2 = DiversifierIndex::from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_3 = DiversifierIndex::from([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // Computed using this Rust implementation let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140]; let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34]; @@ -959,7 +957,7 @@ mod tests { [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19] ); - let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(xfvk_m.address(j_1), None); } @@ -968,7 +966,7 @@ mod tests { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed); let (j_m, addr_m) = xsk_m.default_address(); - assert_eq!(j_m.0, [0; 11]); + assert_eq!(j_m.as_bytes(), &[0; 11]); assert_eq!( addr_m.diversifier().0, // Computed using this Rust implementation @@ -1690,7 +1688,7 @@ mod tests { } // dmax - let dmax = DiversifierIndex([0xff; 11]); + let dmax = DiversifierIndex::from([0xff; 11]); match xfvk.dk.find_diversifier(dmax) { Some((l, d)) if l == dmax => assert_eq!(d.0, tv.dmax.unwrap()), Some((_, _)) => panic!(), diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 504ee2fde..ba5e23983 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -784,7 +784,7 @@ mod tests { orchard_saks: Vec::new(), }; - let tsk = AccountPrivKey::from_seed(&TEST_NETWORK, &[0u8; 32], AccountId::from(0)).unwrap(); + let tsk = AccountPrivKey::from_seed(&TEST_NETWORK, &[0u8; 32], AccountId::ZERO).unwrap(); let prev_coin = TxOut { value: NonNegativeAmount::const_from_u64(50000), script_pubkey: tsk diff --git a/zip32/.gitignore b/zip32/.gitignore deleted file mode 100644 index 693699042..000000000 --- a/zip32/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/zip32/COPYRIGHT b/zip32/COPYRIGHT deleted file mode 100644 index e58e86893..000000000 --- a/zip32/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "zip32" library are retained by their contributors. No -copyright assignment is required to contribute to the "zip32" library. - -The "zip32" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/zip32/Cargo.toml b/zip32/Cargo.toml deleted file mode 100644 index 9daaa10c2..000000000 --- a/zip32/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "zip32" -version = "0.0.0" -authors = [ - "Jack Grigg ", - "Kris Nuttycombe ", -] -description = "Library for implementing shielded hierarchical deterministic wallets" -documentation = "https://docs.rs/zip32/" -homepage = "https://github.com/zcash-hackworks/zip32" -repository = "https://github.com/zcash-hackworks/zip32" -license.workspace = true -edition.workspace = true -rust-version.workspace = true - -[dependencies] -blake2b_simd.workspace = true -memuse.workspace = true -subtle.workspace = true - -[dev-dependencies] -assert_matches.workspace = true diff --git a/zip32/LICENSE-APACHE b/zip32/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/zip32/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/zip32/LICENSE-MIT b/zip32/LICENSE-MIT deleted file mode 100644 index 31aa79387..000000000 --- a/zip32/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/zip32/README.md b/zip32/README.md deleted file mode 100644 index 898d56cdf..000000000 --- a/zip32/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# zip32 [![Crates.io](https://img.shields.io/crates/v/zip32.svg)](https://crates.io/crates/zip32) # - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/zip32/src/fingerprint.rs b/zip32/src/fingerprint.rs deleted file mode 100644 index 8c4cffb9a..000000000 --- a/zip32/src/fingerprint.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! Seed Fingerprints according to ZIP 32 -//! -//! Implements section `Seed Fingerprints` of Shielded Hierarchical Deterministic Wallets (ZIP 32) -//! -//! [Section Seed Fingerprints]: https://zips.z.cash/zip-0032#seed-fingerprints -use blake2b_simd::Params as Blake2bParams; - -pub const ZIP32_SEED_FP_PERSONALIZATION: &[u8; 16] = b"Zcash_HD_Seed_FP"; -pub struct SeedFingerprint([u8; 32]); - -impl SeedFingerprint { - /// Return the seed fingerprint of the wallet as defined in - /// or None - /// if the length of `seed_bytes` is less than 32 or - /// greater than 252. - pub fn from_seed(seed_bytes: &[u8]) -> Option { - let seed_len = seed_bytes.len(); - - if (32..=252).contains(&seed_len) { - let seed_len: u8 = seed_len.try_into().unwrap(); - Some(SeedFingerprint( - Blake2bParams::new() - .hash_length(32) - .personal(ZIP32_SEED_FP_PERSONALIZATION) - .to_state() - .update(&[seed_len]) - .update(seed_bytes) - .finalize() - .as_bytes() - .try_into() - .expect("hash length should be 32 bytes"), - )) - } else { - None - } - } - - /// Returns the fingerprint as a byte array. - pub fn to_bytes(&self) -> [u8; 32] { - self.0 - } -} - -#[test] -fn test_seed_fingerprint() { - struct TestVector { - root_seed: Vec, - fingerprint: Vec, - } - - let test_vectors = vec![TestVector { - root_seed: vec![ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, - 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, - ], - fingerprint: vec![ - 0xde, 0xff, 0x60, 0x4c, 0x24, 0x67, 0x10, 0xf7, 0x17, 0x6d, 0xea, 0xd0, 0x2a, 0xa7, - 0x46, 0xf2, 0xfd, 0x8d, 0x53, 0x89, 0xf7, 0x7, 0x25, 0x56, 0xdc, 0xb5, 0x55, 0xfd, - 0xbe, 0x5e, 0x3a, 0xe3, - ], - }]; - - for tv in test_vectors { - let fp = SeedFingerprint::from_seed(&tv.root_seed).expect("root_seed has valid length"); - assert_eq!(&fp.to_bytes(), &tv.fingerprint[..]); - } -} -#[test] -fn test_seed_fingerprint_is_none() { - let odd_seed = vec![ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, - ]; - - assert!( - SeedFingerprint::from_seed(&odd_seed).is_none(), - "fingerprint from short seed should be `None`" - ); -} diff --git a/zip32/src/lib.rs b/zip32/src/lib.rs deleted file mode 100644 index a130b6b32..000000000 --- a/zip32/src/lib.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Implementation of [ZIP 32] for hierarchical deterministic key management. -//! -//! [ZIP 32]: https://zips.z.cash/zip-0032 - -use memuse::{self, DynamicUsage}; -use subtle::{Choice, ConditionallySelectable}; - -pub mod fingerprint; - -/// A type-safe wrapper for account identifiers. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct AccountId(u32); - -memuse::impl_no_dynamic_usage!(AccountId); - -impl From for AccountId { - fn from(id: u32) -> Self { - Self(id) - } -} - -impl From for u32 { - fn from(id: AccountId) -> Self { - id.0 - } -} - -impl From for ChildIndex { - fn from(id: AccountId) -> Self { - // Account IDs are always hardened in derivation paths. - ChildIndex::hardened(id.0) - } -} - -impl ConditionallySelectable for AccountId { - fn conditional_select(a0: &Self, a1: &Self, c: Choice) -> Self { - AccountId(u32::conditional_select(&a0.0, &a1.0, c)) - } -} - -// ZIP 32 structures - -/// A child index for a derived key. -/// -/// Only hardened derivation is supported. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ChildIndex(u32); - -impl ChildIndex { - /// Parses the given ZIP 32 child index. - /// - /// Returns `None` if the hardened bit is not set. - pub fn from_index(i: u32) -> Option { - if i >= (1 << 31) { - Some(ChildIndex(i)) - } else { - None - } - } - - /// Constructs a hardened `ChildIndex` from the given value. - /// - /// # Panics - /// - /// Panics if `value >= (1 << 31)`. - pub const fn hardened(value: u32) -> Self { - assert!(value < (1 << 31)); - Self(value + (1 << 31)) - } - - /// Returns the index as a 32-bit integer, including the hardened bit. - pub fn index(&self) -> u32 { - self.0 - } -} - -/// A value that is needed, in addition to a spending key, in order to derive descendant -/// keys and addresses of that key. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ChainCode([u8; 32]); - -impl ChainCode { - /// Constructs a `ChainCode` from the given array. - pub fn new(c: [u8; 32]) -> Self { - Self(c) - } - - /// Returns the byte representation of the chain code, as required for - /// [ZIP 32](https://zips.z.cash/zip-0032) encoding. - pub fn as_bytes(&self) -> &[u8; 32] { - &self.0 - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct DiversifierIndex(pub [u8; 11]); - -impl Default for DiversifierIndex { - fn default() -> Self { - DiversifierIndex::new() - } -} - -impl From for DiversifierIndex { - fn from(i: u32) -> Self { - u64::from(i).into() - } -} - -impl From for DiversifierIndex { - fn from(i: u64) -> Self { - let mut result = DiversifierIndex([0; 11]); - result.0[..8].copy_from_slice(&i.to_le_bytes()); - result - } -} - -impl TryFrom for u32 { - type Error = std::num::TryFromIntError; - - fn try_from(di: DiversifierIndex) -> Result { - let mut u128_bytes = [0u8; 16]; - u128_bytes[0..11].copy_from_slice(&di.0[..]); - u128::from_le_bytes(u128_bytes).try_into() - } -} - -impl DiversifierIndex { - pub fn new() -> Self { - DiversifierIndex([0; 11]) - } - - pub fn increment(&mut self) -> Result<(), ()> { - for k in 0..11 { - self.0[k] = self.0[k].wrapping_add(1); - if self.0[k] != 0 { - // No overflow - return Ok(()); - } - } - // Overflow - Err(()) - } -} - -/// The scope of a viewing key or address. -/// -/// A "scope" narrows the visibility or usage to a level below "full". -/// -/// Consistent usage of `Scope` enables the user to provide consistent views over a wallet -/// to other people. For example, a user can give an external [SaplingIvk] to a merchant -/// terminal, enabling it to only detect "real" transactions from customers and not -/// internal transactions from the wallet. -/// -/// [SaplingIvk]: crate::sapling::SaplingIvk -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum Scope { - /// A scope used for wallet-external operations, namely deriving addresses to give to - /// other users in order to receive funds. - External, - /// A scope used for wallet-internal operations, such as creating change notes, - /// auto-shielding, and note management. - Internal, -} - -memuse::impl_no_dynamic_usage!(Scope); - -#[cfg(test)] -mod tests { - use super::DiversifierIndex; - use assert_matches::assert_matches; - - #[test] - fn diversifier_index_to_u32() { - let two = DiversifierIndex([ - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - assert_eq!(u32::try_from(two), Ok(2)); - - let max_u32 = DiversifierIndex([ - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - assert_eq!(u32::try_from(max_u32), Ok(u32::MAX)); - - let too_big = DiversifierIndex([ - 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - assert_matches!(u32::try_from(too_big), Err(_)); - } -}