From c67b17dc962112a37f6b3ee9f096b6d744a994f9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 18 Mar 2024 21:13:39 +0000 Subject: [PATCH] zcash_client_sqlite: Extract `seed_matches_derived_account` helper --- zcash_client_sqlite/src/lib.rs | 33 +++--------------------- zcash_client_sqlite/src/wallet.rs | 42 ++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 469299c17..fb9ca3777 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -321,38 +321,13 @@ impl, P: consensus::Parameters> WalletRead for W account_index, } = account.source() { - let seed_fingerprint_match = - SeedFingerprint::from_seed(seed.expose_secret()).ok_or_else(|| { - SqliteClientError::BadAccountData( - "Seed must be between 32 and 252 bytes in length.".to_owned(), - ) - })? == seed_fingerprint; - - let usk = UnifiedSpendingKey::from_seed( + wallet::seed_matches_derived_account( &self.params, - &seed.expose_secret()[..], + seed, + &seed_fingerprint, account_index, + &account.uivk(), ) - .map_err(|_| SqliteClientError::KeyDerivationError(account_index))?; - - // Keys are not comparable with `Eq`, but addresses are, so we derive what should - // be equivalent addresses for each key and use those to check for key equality. - let ufvk_match = UnifiedAddressRequest::all().map_or( - Ok::<_, Self::Error>(false), - |ua_request| { - Ok(usk - .to_unified_full_viewing_key() - .default_address(ua_request)? - == account.default_address(ua_request)?) - }, - )?; - - if seed_fingerprint_match != ufvk_match { - // If these mismatch, it suggests database corruption. - return Err(SqliteClientError::CorruptedData(format!("Seed fingerprint match: {seed_fingerprint_match}, ufvk match: {ufvk_match}"))); - } - - Ok(seed_fingerprint_match && ufvk_match) } else { Err(SqliteClientError::UnknownZip32Derivation) } diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index d1a9615f9..413b0c9ec 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -66,6 +66,7 @@ use incrementalmerkletree::Retention; use rusqlite::{self, named_params, params, OptionalExtension}; +use secrecy::{ExposeSecret, SecretVec}; use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree}; use zip32::fingerprint::SeedFingerprint; @@ -75,7 +76,9 @@ use std::io::{self, Cursor}; use std::num::NonZeroU32; use std::ops::RangeInclusive; use tracing::debug; -use zcash_keys::keys::{AddressGenerationError, UnifiedAddressRequest, UnifiedIncomingViewingKey}; +use zcash_keys::keys::{ + AddressGenerationError, UnifiedAddressRequest, UnifiedIncomingViewingKey, UnifiedSpendingKey, +}; use zcash_client_backend::{ address::{Address, UnifiedAddress}, @@ -245,6 +248,43 @@ impl ViewingKey { } } +pub(crate) fn seed_matches_derived_account( + params: &P, + seed: &SecretVec, + seed_fingerprint: &SeedFingerprint, + account_index: zip32::AccountId, + uivk: &UnifiedIncomingViewingKey, +) -> Result { + let seed_fingerprint_match = + &SeedFingerprint::from_seed(seed.expose_secret()).ok_or_else(|| { + SqliteClientError::BadAccountData( + "Seed must be between 32 and 252 bytes in length.".to_owned(), + ) + })? == seed_fingerprint; + + let usk = UnifiedSpendingKey::from_seed(params, &seed.expose_secret()[..], account_index) + .map_err(|_| SqliteClientError::KeyDerivationError(account_index))?; + + // Keys are not comparable with `Eq`, but addresses are, so we derive what should + // be equivalent addresses for each key and use those to check for key equality. + let uivk_match = + UnifiedAddressRequest::all().map_or(Ok::<_, SqliteClientError>(false), |ua_request| { + Ok(usk + .to_unified_full_viewing_key() + .default_address(ua_request)? + == uivk.default_address(ua_request)?) + })?; + + if seed_fingerprint_match != uivk_match { + // If these mismatch, it suggests database corruption. + Err(SqliteClientError::CorruptedData(format!( + "Seed fingerprint match: {seed_fingerprint_match}, uivk match: {uivk_match}" + ))) + } else { + Ok(seed_fingerprint_match && uivk_match) + } +} + pub(crate) fn pool_code(pool_type: PoolType) -> i64 { // These constants are *incidentally* shared with the typecodes // for unified addresses, but this is exclusively an internal