zcash_client_backend: Remove account from `UnifiedFullViewingKey`

The account number is not stored in the ZIP 316 UFVK encoding, and in
general won't necessarily be known (e.g. if a UFVK is being imported
into a wallet).

`zcash_client_sqlite::wallet::init::init_accounts_table` reverts to its
previous behaviour of requiring the provided `&[UnifiedFullViewingKey]`
to be indexed by account number.
This commit is contained in:
Jack Grigg 2022-06-14 01:56:46 +00:00
parent 1ce289e568
commit d8b860207d
5 changed files with 24 additions and 62 deletions

View File

@ -107,7 +107,6 @@ impl UnifiedSpendingKey {
pub fn to_unified_full_viewing_key(&self) -> UnifiedFullViewingKey { pub fn to_unified_full_viewing_key(&self) -> UnifiedFullViewingKey {
UnifiedFullViewingKey { UnifiedFullViewingKey {
account: self.account,
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent: Some(self.transparent.to_account_pubkey()), transparent: Some(self.transparent.to_account_pubkey()),
sapling: Some(sapling::ExtendedFullViewingKey::from(&self.sapling)), sapling: Some(sapling::ExtendedFullViewingKey::from(&self.sapling)),
@ -137,7 +136,6 @@ impl UnifiedSpendingKey {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[doc(hidden)] #[doc(hidden)]
pub struct UnifiedFullViewingKey { pub struct UnifiedFullViewingKey {
account: AccountId,
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent: Option<legacy::AccountPubKey>, transparent: Option<legacy::AccountPubKey>,
// TODO: This type is invalid for a UFVK; create a `sapling::DiversifiableFullViewingKey` // TODO: This type is invalid for a UFVK; create a `sapling::DiversifiableFullViewingKey`
@ -149,7 +147,6 @@ pub struct UnifiedFullViewingKey {
impl UnifiedFullViewingKey { impl UnifiedFullViewingKey {
/// Construct a new unified full viewing key, if the required components are present. /// Construct a new unified full viewing key, if the required components are present.
pub fn new( pub fn new(
account: AccountId,
#[cfg(feature = "transparent-inputs")] transparent: Option<legacy::AccountPubKey>, #[cfg(feature = "transparent-inputs")] transparent: Option<legacy::AccountPubKey>,
sapling: Option<sapling::ExtendedFullViewingKey>, sapling: Option<sapling::ExtendedFullViewingKey>,
) -> Option<UnifiedFullViewingKey> { ) -> Option<UnifiedFullViewingKey> {
@ -157,7 +154,6 @@ impl UnifiedFullViewingKey {
None None
} else { } else {
Some(UnifiedFullViewingKey { Some(UnifiedFullViewingKey {
account,
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent, transparent,
sapling, sapling,
@ -167,11 +163,7 @@ impl UnifiedFullViewingKey {
/// Attempts to decode the given string as an encoding of a `UnifiedFullViewingKey` /// Attempts to decode the given string as an encoding of a `UnifiedFullViewingKey`
/// for the given network. /// for the given network.
pub fn decode<P: consensus::Parameters>( pub fn decode<P: consensus::Parameters>(params: &P, encoding: &str) -> Result<Self, String> {
params: &P,
encoding: &str,
account: AccountId,
) -> Result<Self, String> {
encoding encoding
.strip_prefix("DONOTUSEUFVK") .strip_prefix("DONOTUSEUFVK")
.and_then(|data| hex::decode(data).ok()) .and_then(|data| hex::decode(data).ok())
@ -199,7 +191,6 @@ impl UnifiedFullViewingKey {
}; };
Some(Self { Some(Self {
account,
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent, transparent,
sapling, sapling,
@ -227,12 +218,6 @@ impl UnifiedFullViewingKey {
format!("DONOTUSEUFVK{}", hex::encode(&ufvk)) format!("DONOTUSEUFVK{}", hex::encode(&ufvk))
} }
/// Returns the ZIP32 account identifier to which all component
/// keys are related.
pub fn account(&self) -> AccountId {
self.account
}
/// Returns the transparent component of the unified key at the /// Returns the transparent component of the unified key at the
/// BIP44 path `m/44'/<coin_type>'/<account>'`. /// BIP44 path `m/44'/<coin_type>'/<account>'`.
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
@ -370,7 +355,6 @@ mod tests {
let transparent = { None }; let transparent = { None };
let ufvk = UnifiedFullViewingKey::new( let ufvk = UnifiedFullViewingKey::new(
account,
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent, transparent,
sapling, sapling,
@ -378,8 +362,7 @@ mod tests {
.unwrap(); .unwrap();
let encoding = ufvk.encode(&MAIN_NETWORK); let encoding = ufvk.encode(&MAIN_NETWORK);
let decoded = UnifiedFullViewingKey::decode(&MAIN_NETWORK, &encoding, account).unwrap(); let decoded = UnifiedFullViewingKey::decode(&MAIN_NETWORK, &encoding).unwrap();
assert_eq!(decoded.account, ufvk.account);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
assert_eq!( assert_eq!(
decoded.transparent.map(|t| t.serialize()), decoded.transparent.map(|t| t.serialize()),

View File

@ -802,7 +802,6 @@ mod tests {
let taddr = None; let taddr = None;
let ufvk = UnifiedFullViewingKey::new( let ufvk = UnifiedFullViewingKey::new(
account,
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
tkey, tkey,
Some(extfvk.clone()), Some(extfvk.clone()),

View File

@ -208,7 +208,7 @@ pub(crate) fn get_unified_full_viewing_keys<P: consensus::Parameters>(
let acct: u32 = row.get(0)?; let acct: u32 = row.get(0)?;
let account = AccountId::from(acct); let account = AccountId::from(acct);
let ufvk_str: String = row.get(1)?; let ufvk_str: String = row.get(1)?;
let ufvk = UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str, account) let ufvk = UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str)
.map_err(SqliteClientError::CorruptedData); .map_err(SqliteClientError::CorruptedData);
Ok((account, ufvk)) Ok((account, ufvk))
@ -240,7 +240,7 @@ pub fn is_valid_account_extfvk<P: consensus::Parameters>(
.prepare("SELECT ufvk FROM accounts WHERE account = ?")? .prepare("SELECT ufvk FROM accounts WHERE account = ?")?
.query_row(&[u32::from(account).to_sql()?], |row| { .query_row(&[u32::from(account).to_sql()?], |row| {
row.get(0).map(|ufvk_str: String| { row.get(0).map(|ufvk_str: String| {
UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str, account) UnifiedFullViewingKey::decode(&wdb.params, &ufvk_str)
.map_err(SqliteClientError::CorruptedData) .map_err(SqliteClientError::CorruptedData)
}) })
}) })

View File

@ -180,7 +180,7 @@ pub fn init_wallet_db<P>(wdb: &WalletDb<P>) -> Result<(), rusqlite::Error> {
/// let account = AccountId::from(0); /// let account = AccountId::from(0);
/// let extsk = sapling::spending_key(&seed, Network::TestNetwork.coin_type(), account); /// let extsk = sapling::spending_key(&seed, Network::TestNetwork.coin_type(), account);
/// let extfvk = ExtendedFullViewingKey::from(&extsk); /// let extfvk = ExtendedFullViewingKey::from(&extsk);
/// let ufvk = UnifiedFullViewingKey::new(account, None, Some(extfvk)).unwrap(); /// let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk)).unwrap();
/// init_accounts_table(&db_data, &[ufvk]).unwrap(); /// init_accounts_table(&db_data, &[ufvk]).unwrap();
/// # } /// # }
/// ``` /// ```
@ -199,7 +199,7 @@ pub fn init_accounts_table<P: consensus::Parameters>(
// Insert accounts atomically // Insert accounts atomically
wdb.conn.execute("BEGIN IMMEDIATE", NO_PARAMS)?; wdb.conn.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
for key in keys.iter() { for (account, key) in (0u32..).zip(keys) {
let ufvk_str: String = key.encode(&wdb.params); let ufvk_str: String = key.encode(&wdb.params);
let address_str: String = key.default_address().0.encode(&wdb.params); let address_str: String = key.default_address().0.encode(&wdb.params);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
@ -214,12 +214,7 @@ pub fn init_accounts_table<P: consensus::Parameters>(
wdb.conn.execute( wdb.conn.execute(
"INSERT INTO accounts (account, ufvk, address, transparent_address) "INSERT INTO accounts (account, ufvk, address, transparent_address)
VALUES (?, ?, ?, ?)", VALUES (?, ?, ?, ?)",
params![ params![account, ufvk_str, address_str, taddress_str],
u32::from(key.account()),
ufvk_str,
address_str,
taddress_str,
],
)?; )?;
} }
wdb.conn.execute("COMMIT", NO_PARAMS)?; wdb.conn.execute("COMMIT", NO_PARAMS)?;
@ -328,7 +323,6 @@ mod tests {
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new( let ufvk = UnifiedFullViewingKey::new(
account,
Some( Some(
transparent::AccountPrivKey::from_seed(&network(), &seed, account) transparent::AccountPrivKey::from_seed(&network(), &seed, account)
.unwrap() .unwrap()
@ -339,7 +333,7 @@ mod tests {
.unwrap(); .unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(account, Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk)).unwrap();
init_accounts_table(&db_data, &[ufvk.clone()]).unwrap(); init_accounts_table(&db_data, &[ufvk.clone()]).unwrap();

View File

@ -219,24 +219,14 @@ mod tests {
transparent::AccountPrivKey::from_seed(&network(), &[1u8; 32], AccountId::from(1)) transparent::AccountPrivKey::from_seed(&network(), &[1u8; 32], AccountId::from(1))
.unwrap(); .unwrap();
[ [
UnifiedFullViewingKey::new( UnifiedFullViewingKey::new(Some(tsk0.to_account_pubkey()), Some(extfvk0)).unwrap(),
AccountId::from(0), UnifiedFullViewingKey::new(Some(tsk1.to_account_pubkey()), Some(extfvk1)).unwrap(),
Some(tsk0.to_account_pubkey()),
Some(extfvk0),
)
.unwrap(),
UnifiedFullViewingKey::new(
AccountId::from(1),
Some(tsk1.to_account_pubkey()),
Some(extfvk1),
)
.unwrap(),
] ]
}; };
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvks = [ let ufvks = [
UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk0)).unwrap(), UnifiedFullViewingKey::new(Some(extfvk0)).unwrap(),
UnifiedFullViewingKey::new(AccountId::from(1), Some(extfvk1)).unwrap(), UnifiedFullViewingKey::new(Some(extfvk1)).unwrap(),
]; ];
init_accounts_table(&db_data, &ufvks).unwrap(); init_accounts_table(&db_data, &ufvks).unwrap();
@ -288,9 +278,9 @@ mod tests {
let extfvk = ExtendedFullViewingKey::from(&extsk); let extfvk = ExtendedFullViewingKey::from(&extsk);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), None, Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk)).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk)).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
let to = extsk.default_address().1.into(); let to = extsk.default_address().1.into();
@ -331,9 +321,9 @@ mod tests {
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let extfvk = ExtendedFullViewingKey::from(&extsk);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), None, Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk)).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk)).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
let to = extsk.default_address().1.into(); let to = extsk.default_address().1.into();
@ -379,10 +369,9 @@ mod tests {
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let extfvk = ExtendedFullViewingKey::from(&extsk);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap();
UnifiedFullViewingKey::new(AccountId::from(0), None, Some(extfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -523,10 +512,9 @@ mod tests {
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let extfvk = ExtendedFullViewingKey::from(&extsk);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap();
UnifiedFullViewingKey::new(AccountId::from(0), None, Some(extfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -653,10 +641,9 @@ mod tests {
let extsk = sapling::spending_key(&[0u8; 32], network.coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network.coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let extfvk = ExtendedFullViewingKey::from(&extsk);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap();
UnifiedFullViewingKey::new(AccountId::from(0), None, Some(extfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -764,10 +751,9 @@ mod tests {
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let extfvk = ExtendedFullViewingKey::from(&extsk);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap();
UnifiedFullViewingKey::new(AccountId::from(0), None, Some(extfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(AccountId::from(0), Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note