Merge pull request #1274 from nuttycom/use_zip32_seed_fingerprint

zcash_keys: Remove HdSeedFingerprint as it duplicates `zip32::fingerprint::SeedFingerprint`'
This commit is contained in:
Kris Nuttycombe 2024-03-14 17:37:13 -06:00 committed by GitHub
commit c3d82b2cce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 40 additions and 75 deletions

4
Cargo.lock generated
View File

@ -3297,9 +3297,9 @@ dependencies = [
[[package]]
name = "zip32"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22"
checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e"
dependencies = [
"blake2b_simd",
"memuse",

View File

@ -114,7 +114,7 @@ rand_xorshift = "0.3"
# ZIP 32
aes = "0.8"
fpe = "0.6"
zip32 = "0.1"
zip32 = "0.1.1"
[profile.release]
lto = true

View File

@ -66,7 +66,7 @@ use std::{
use incrementalmerkletree::{frontier::Frontier, Retention};
use secrecy::SecretVec;
use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree};
use zcash_keys::keys::HdSeedFingerprint;
use zip32::fingerprint::SeedFingerprint;
use self::{
chain::{ChainState, CommitmentTreeRoot},
@ -320,7 +320,7 @@ impl AccountBalance {
pub enum AccountKind {
/// An account derived from a known seed.
Derived {
seed_fingerprint: HdSeedFingerprint,
seed_fingerprint: SeedFingerprint,
account_index: zip32::AccountId,
},
@ -698,11 +698,11 @@ pub trait WalletRead {
account_id: Self::AccountId,
) -> Result<Option<Self::Account>, Self::Error>;
/// Returns the account corresponding to a given [`HdSeedFingerprint`] and
/// Returns the account corresponding to a given [`SeedFingerprint`] and
/// [`zip32::AccountId`], if any.
fn get_derived_account(
&self,
seed: &HdSeedFingerprint,
seed: &SeedFingerprint,
account_id: zip32::AccountId,
) -> Result<Option<Self::Account>, Self::Error>;
@ -1586,7 +1586,7 @@ pub mod testing {
use secrecy::{ExposeSecret, SecretVec};
use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree};
use std::{collections::HashMap, convert::Infallible, num::NonZeroU32};
use zcash_keys::keys::HdSeedFingerprint;
use zip32::fingerprint::SeedFingerprint;
use zcash_primitives::{
block::BlockHash,
@ -1686,7 +1686,7 @@ pub mod testing {
fn get_derived_account(
&self,
_seed: &HdSeedFingerprint,
_seed: &SeedFingerprint,
_account_id: zip32::AccountId,
) -> Result<Option<Self::Account>, Self::Error> {
Ok(None)

View File

@ -45,7 +45,6 @@ use std::{
path::Path,
};
use subtle::ConditionallySelectable;
use zcash_keys::keys::HdSeedFingerprint;
use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
@ -53,6 +52,7 @@ use zcash_primitives::{
transaction::{components::amount::NonNegativeAmount, Transaction, TxId},
zip32::{self, DiversifierIndex, Scope},
};
use zip32::fingerprint::SeedFingerprint;
use zcash_client_backend::{
address::UnifiedAddress,
@ -304,7 +304,7 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
fn get_derived_account(
&self,
seed: &HdSeedFingerprint,
seed: &SeedFingerprint,
account_id: zip32::AccountId,
) -> Result<Option<Self::Account>, Self::Error> {
wallet::get_derived_account(self.conn.borrow(), &self.params, seed, account_id)
@ -321,7 +321,12 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
account_index,
} = account.kind()
{
let seed_fingerprint_match = HdSeedFingerprint::from_seed(seed) == seed_fingerprint;
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(
&self.params,
@ -502,7 +507,12 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
birthday: AccountBirthday,
) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> {
self.transactionally(|wdb| {
let seed_fingerprint = HdSeedFingerprint::from_seed(seed);
let seed_fingerprint =
SeedFingerprint::from_seed(seed.expose_secret()).ok_or_else(|| {
SqliteClientError::BadAccountData(
"Seed must be between 32 and 252 bytes in length.".to_owned(),
)
})?;
let account_index = wallet::max_zip32_account_index(wdb.conn.0, &seed_fingerprint)?
.map(|a| a.next().ok_or(SqliteClientError::AccountIdOutOfRange))
.transpose()?

View File

@ -67,6 +67,7 @@
use incrementalmerkletree::Retention;
use rusqlite::{self, named_params, params, OptionalExtension};
use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree};
use zip32::fingerprint::SeedFingerprint;
use std::collections::HashMap;
use std::convert::TryFrom;
@ -75,7 +76,7 @@ use std::num::NonZeroU32;
use std::ops::RangeInclusive;
use tracing::debug;
use zcash_address::unified::{Encoding, Ivk, Uivk};
use zcash_keys::keys::{AddressGenerationError, HdSeedFingerprint, UnifiedAddressRequest};
use zcash_keys::keys::{AddressGenerationError, UnifiedAddressRequest};
use zcash_client_backend::{
address::{Address, UnifiedAddress},
@ -145,7 +146,7 @@ fn parse_account_kind(
) -> Result<AccountKind, SqliteClientError> {
match (account_kind, hd_seed_fingerprint, hd_account_index) {
(0, Some(seed_fp), Some(account_index)) => Ok(AccountKind::Derived {
seed_fingerprint: HdSeedFingerprint::from_bytes(seed_fp),
seed_fingerprint: SeedFingerprint::from_bytes(seed_fp),
account_index: zip32::AccountId::try_from(account_index).map_err(|_| {
SqliteClientError::CorruptedData(
"ZIP-32 account ID from wallet DB is out of range.".to_string(),
@ -280,11 +281,11 @@ pub(crate) fn memo_repr(memo: Option<&MemoBytes>) -> Option<&[u8]> {
// Returns the highest used account index for a given seed.
pub(crate) fn max_zip32_account_index(
conn: &rusqlite::Connection,
seed_id: &HdSeedFingerprint,
seed_id: &SeedFingerprint,
) -> Result<Option<zip32::AccountId>, SqliteClientError> {
conn.query_row_and_then(
"SELECT MAX(hd_account_index) FROM accounts WHERE hd_seed_fingerprint = :hd_seed",
[seed_id.as_bytes()],
[seed_id.to_bytes()],
|row| {
let account_id: Option<u32> = row.get(0)?;
account_id
@ -366,7 +367,7 @@ pub(crate) fn add_account<P: consensus::Parameters>(
"#,
named_params![
":account_kind": account_kind_code(kind),
":hd_seed_fingerprint": hd_seed_fingerprint.as_ref().map(|fp| fp.as_bytes()),
":hd_seed_fingerprint": hd_seed_fingerprint.as_ref().map(|fp| fp.to_bytes()),
":hd_account_index": hd_account_index.map(u32::from),
":ufvk": viewing_key.ufvk().map(|ufvk| ufvk.encode(params)),
":uivk": viewing_key.uivk_str(params)?,
@ -765,12 +766,12 @@ pub(crate) fn get_account_for_ufvk<P: consensus::Parameters>(
}
}
/// Returns the account id corresponding to a given [`HdSeedFingerprint`]
/// Returns the account id corresponding to a given [`SeedFingerprint`]
/// and [`zip32::AccountId`], if any.
pub(crate) fn get_derived_account<P: consensus::Parameters>(
conn: &rusqlite::Connection,
params: &P,
seed: &HdSeedFingerprint,
seed: &SeedFingerprint,
account_index: zip32::AccountId,
) -> Result<Option<Account>, SqliteClientError> {
let mut stmt = conn.prepare(
@ -782,7 +783,7 @@ pub(crate) fn get_derived_account<P: consensus::Parameters>(
let mut accounts = stmt.query_and_then::<_, SqliteClientError, _, _>(
named_params![
":hd_seed_fingerprint": seed.as_bytes(),
":hd_seed_fingerprint": seed.to_bytes(),
":hd_account_index": u32::from(account_index),
],
|row| {

View File

@ -6,8 +6,9 @@ use schemer_rusqlite::RusqliteMigration;
use secrecy::{ExposeSecret, SecretVec};
use uuid::Uuid;
use zcash_client_backend::{data_api::AccountKind, keys::UnifiedSpendingKey};
use zcash_keys::keys::{HdSeedFingerprint, UnifiedFullViewingKey};
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_primitives::consensus;
use zip32::fingerprint::SeedFingerprint;
use super::{add_account_birthdays, receiving_key_scopes, v_transactions_note_uniqueness};
@ -45,7 +46,7 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
fn up(&self, transaction: &Transaction) -> Result<(), WalletMigrationError> {
let account_kind_derived = account_kind_code(AccountKind::Derived {
seed_fingerprint: HdSeedFingerprint::from_bytes([0; 32]),
seed_fingerprint: SeedFingerprint::from_bytes([0; 32]),
account_index: zip32::AccountId::ZERO,
});
let account_kind_imported = account_kind_code(AccountKind::Imported);
@ -83,7 +84,8 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
Ok(row.get::<_, u32>(0)? > 0)
})? {
if let Some(seed) = &self.seed.as_ref() {
let seed_id = HdSeedFingerprint::from_seed(seed);
let seed_id = SeedFingerprint::from_seed(seed.expose_secret())
.expect("Seed is between 32 and 252 bytes in length.");
let mut q = transaction.prepare("SELECT * FROM accounts")?;
let mut rows = q.query([])?;
while let Some(row) = rows.next()? {
@ -145,7 +147,7 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
named_params![
":account_id": account_id,
":account_kind": account_kind_derived,
":seed_id": seed_id.as_bytes(),
":seed_id": seed_id.to_bytes(),
":account_index": account_index,
":ufvk": ufvk,
":uivk": uivk,

View File

@ -7,7 +7,6 @@ and this library adheres to Rust's notion of
## [Unreleased]
### Added
- `zcash_keys::keys::HdSeedFingerprint`
- `zcash_keys::address::Address::has_receiver`
- `impl Display for zcash_keys::keys::AddressGenerationError`
- `impl std::error::Error for zcash_keys::keys::AddressGenerationError`

View File

@ -1,6 +1,6 @@
//! Helper functions for managing light client key material.
use blake2b_simd::Params as blake2bParams;
use secrecy::{ExposeSecret, SecretVec};
use std::{error, fmt};
use zcash_address::unified::{self, Container, Encoding, Typecode};
@ -73,53 +73,6 @@ pub mod sapling {
}
}
/// A [ZIP 32 seed fingerprint] of a seed used for an HD account.
///
/// For wallets that use [BIP 39] mnemonic phrases, this is the fingerprint of the binary
/// seed [produced from the mnemonic].
///
/// [ZIP 32 seed fingerprint]: https://zips.z.cash/zip-0032#seed-fingerprints
/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
/// [produced from the mnemonic]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#from-mnemonic-to-seed
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HdSeedFingerprint([u8; 32]);
impl HdSeedFingerprint {
/// Generates the fingerprint from a given seed.
///
/// Panics if the length of the seed is not between 32 and 252 bytes inclusive.
pub fn from_seed(seed: &SecretVec<u8>) -> Self {
let len = seed.expose_secret().len();
let len = match len {
32..=252 => [u8::try_from(len).unwrap()],
_ => panic!("ZIP 32 seeds MUST be at least 32 bytes and at most 252 bytes"),
};
const PERSONALIZATION: &[u8] = b"Zcash_HD_Seed_FP";
let hash = blake2bParams::new()
.hash_length(32)
.personal(PERSONALIZATION)
.to_state()
.update(&len)
.update(seed.expose_secret())
.finalize();
Self(
hash.as_bytes()
.try_into()
.expect("BLAKE2b-256 hash length is 32 bytes"),
)
}
/// Instantiates the fingerprint from a buffer containing a previously computed fingerprint.
pub fn from_bytes(hash: [u8; 32]) -> Self {
Self(hash)
}
/// Returns the bytes of the fingerprint.
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
#[cfg(feature = "transparent-inputs")]
fn to_transparent_child_index(j: DiversifierIndex) -> Option<NonHardenedChildIndex> {
let (low_4_bytes, rest) = j.as_bytes().split_at(4);