zcash_client_backend: Add a `purpose` modifier for imported accounts.
This moves the tracking of whether or not a spending key is expected to be available for an imported account into the `AccountSource::Imported` variant.
This commit is contained in:
parent
52abb1f057
commit
ac7cbf9a41
|
@ -30,6 +30,7 @@ funds to those addresses. See [ZIP 320](https://zips.z.cash/zip-0320) for detail
|
||||||
- `DecryptedTransaction::mined_height`
|
- `DecryptedTransaction::mined_height`
|
||||||
- `TransactionDataRequest`
|
- `TransactionDataRequest`
|
||||||
- `TransactionStatus`
|
- `TransactionStatus`
|
||||||
|
- `AccountType`
|
||||||
- `zcash_client_backend::fees`:
|
- `zcash_client_backend::fees`:
|
||||||
- `EphemeralBalance`
|
- `EphemeralBalance`
|
||||||
- `ChangeValue::shielded, is_ephemeral`
|
- `ChangeValue::shielded, is_ephemeral`
|
||||||
|
@ -88,6 +89,9 @@ funds to those addresses. See [ZIP 320](https://zips.z.cash/zip-0320) for detail
|
||||||
references to slices, with a corresponding change to `SentTransaction::new`.
|
references to slices, with a corresponding change to `SentTransaction::new`.
|
||||||
- `SentTransaction` takes an additional `target_height` argument, which is used
|
- `SentTransaction` takes an additional `target_height` argument, which is used
|
||||||
to record the target height used in transaction generation.
|
to record the target height used in transaction generation.
|
||||||
|
- `AccountSource::Imported` is now a struct variant with a `purpose` field.
|
||||||
|
- The `Account` trait now defines a new `purpose` method with a default
|
||||||
|
implementation (which need not be overridden.)
|
||||||
- `zcash_client_backend::data_api::fees`
|
- `zcash_client_backend::data_api::fees`
|
||||||
- When the "transparent-inputs" feature is enabled, `ChangeValue` can also
|
- When the "transparent-inputs" feature is enabled, `ChangeValue` can also
|
||||||
represent an ephemeral transparent output in a proposal. Accordingly, the
|
represent an ephemeral transparent output in a proposal. Accordingly, the
|
||||||
|
|
|
@ -321,6 +321,17 @@ impl AccountBalance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An enumeration used to control what information is tracked by the wallet for
|
||||||
|
/// notes received by a given account.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum AccountPurpose {
|
||||||
|
/// For spending accounts, the wallet will track information needed to spend
|
||||||
|
/// received notes.
|
||||||
|
Spending,
|
||||||
|
/// For view-only accounts, the wallet will not track spend information.
|
||||||
|
ViewOnly,
|
||||||
|
}
|
||||||
|
|
||||||
/// The kinds of accounts supported by `zcash_client_backend`.
|
/// The kinds of accounts supported by `zcash_client_backend`.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum AccountSource {
|
pub enum AccountSource {
|
||||||
|
@ -331,7 +342,7 @@ pub enum AccountSource {
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An account imported from a viewing key.
|
/// An account imported from a viewing key.
|
||||||
Imported,
|
Imported { purpose: AccountPurpose },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of capabilities that a client account must provide.
|
/// A set of capabilities that a client account must provide.
|
||||||
|
@ -343,6 +354,14 @@ pub trait Account<AccountId: Copy> {
|
||||||
/// if applicable.
|
/// if applicable.
|
||||||
fn source(&self) -> AccountSource;
|
fn source(&self) -> AccountSource;
|
||||||
|
|
||||||
|
/// Returns whether the account is a spending account or a view-only account.
|
||||||
|
fn purpose(&self) -> AccountPurpose {
|
||||||
|
match self.source() {
|
||||||
|
AccountSource::Derived { .. } => AccountPurpose::Spending,
|
||||||
|
AccountSource::Imported { purpose } => purpose,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the UFVK that the wallet backend has stored for the account, if any.
|
/// Returns the UFVK that the wallet backend has stored for the account, if any.
|
||||||
///
|
///
|
||||||
/// Accounts for which this returns `None` cannot be used in wallet contexts, because
|
/// Accounts for which this returns `None` cannot be used in wallet contexts, because
|
||||||
|
@ -364,7 +383,9 @@ impl<A: Copy> Account<A> for (A, UnifiedFullViewingKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self) -> AccountSource {
|
fn source(&self) -> AccountSource {
|
||||||
AccountSource::Imported
|
AccountSource::Imported {
|
||||||
|
purpose: AccountPurpose::ViewOnly,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
|
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
|
||||||
|
@ -383,7 +404,9 @@ impl<A: Copy> Account<A> for (A, UnifiedIncomingViewingKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self) -> AccountSource {
|
fn source(&self) -> AccountSource {
|
||||||
AccountSource::Imported
|
AccountSource::Imported {
|
||||||
|
purpose: AccountPurpose::ViewOnly,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
|
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
|
||||||
|
@ -1816,7 +1839,7 @@ pub trait WalletWrite: WalletRead {
|
||||||
&mut self,
|
&mut self,
|
||||||
unified_key: &UnifiedFullViewingKey,
|
unified_key: &UnifiedFullViewingKey,
|
||||||
birthday: &AccountBirthday,
|
birthday: &AccountBirthday,
|
||||||
spending_key_available: bool,
|
purpose: AccountPurpose,
|
||||||
) -> Result<Self::Account, Self::Error>;
|
) -> Result<Self::Account, Self::Error>;
|
||||||
|
|
||||||
/// Generates and persists the next available diversified address, given the current
|
/// Generates and persists the next available diversified address, given the current
|
||||||
|
@ -2027,10 +2050,10 @@ pub mod testing {
|
||||||
use super::{
|
use super::{
|
||||||
chain::{ChainState, CommitmentTreeRoot},
|
chain::{ChainState, CommitmentTreeRoot},
|
||||||
scanning::ScanRange,
|
scanning::ScanRange,
|
||||||
AccountBirthday, BlockMetadata, DecryptedTransaction, InputSource, NullifierQuery,
|
AccountBirthday, AccountPurpose, BlockMetadata, DecryptedTransaction, InputSource,
|
||||||
ScannedBlock, SeedRelevance, SentTransaction, SpendableNotes, TransactionDataRequest,
|
NullifierQuery, ScannedBlock, SeedRelevance, SentTransaction, SpendableNotes,
|
||||||
TransactionStatus, WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite,
|
TransactionDataRequest, TransactionStatus, WalletCommitmentTrees, WalletRead,
|
||||||
SAPLING_SHARD_HEIGHT,
|
WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
|
@ -2319,7 +2342,7 @@ pub mod testing {
|
||||||
&mut self,
|
&mut self,
|
||||||
_unified_key: &UnifiedFullViewingKey,
|
_unified_key: &UnifiedFullViewingKey,
|
||||||
_birthday: &AccountBirthday,
|
_birthday: &AccountBirthday,
|
||||||
_spending_key_available: bool,
|
_purpose: AccountPurpose,
|
||||||
) -> Result<Self::Account, Self::Error> {
|
) -> Result<Self::Account, Self::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,10 @@ use zcash_client_backend::{
|
||||||
self,
|
self,
|
||||||
chain::{BlockSource, ChainState, CommitmentTreeRoot},
|
chain::{BlockSource, ChainState, CommitmentTreeRoot},
|
||||||
scanning::{ScanPriority, ScanRange},
|
scanning::{ScanPriority, ScanRange},
|
||||||
Account, AccountBirthday, AccountSource, BlockMetadata, DecryptedTransaction, InputSource,
|
Account, AccountBirthday, AccountPurpose, AccountSource, BlockMetadata,
|
||||||
NullifierQuery, ScannedBlock, SeedRelevance, SentTransaction, SpendableNotes,
|
DecryptedTransaction, InputSource, NullifierQuery, ScannedBlock, SeedRelevance,
|
||||||
TransactionDataRequest, WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite,
|
SentTransaction, SpendableNotes, TransactionDataRequest, WalletCommitmentTrees, WalletRead,
|
||||||
SAPLING_SHARD_HEIGHT,
|
WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
|
||||||
},
|
},
|
||||||
keys::{
|
keys::{
|
||||||
AddressGenerationError, UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey,
|
AddressGenerationError, UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey,
|
||||||
|
@ -630,7 +630,6 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
.map_err(|_| SqliteClientError::KeyDerivationError(account_index))?;
|
.map_err(|_| SqliteClientError::KeyDerivationError(account_index))?;
|
||||||
let ufvk = usk.to_unified_full_viewing_key();
|
let ufvk = usk.to_unified_full_viewing_key();
|
||||||
|
|
||||||
let spending_key_available = true;
|
|
||||||
let account = wallet::add_account(
|
let account = wallet::add_account(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
&wdb.params,
|
||||||
|
@ -640,7 +639,6 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
},
|
},
|
||||||
wallet::ViewingKey::Full(Box::new(ufvk)),
|
wallet::ViewingKey::Full(Box::new(ufvk)),
|
||||||
birthday,
|
birthday,
|
||||||
spending_key_available,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((account.id(), usk))
|
Ok((account.id(), usk))
|
||||||
|
@ -666,7 +664,6 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
.map_err(|_| SqliteClientError::KeyDerivationError(account_index))?;
|
.map_err(|_| SqliteClientError::KeyDerivationError(account_index))?;
|
||||||
let ufvk = usk.to_unified_full_viewing_key();
|
let ufvk = usk.to_unified_full_viewing_key();
|
||||||
|
|
||||||
let spending_key_available = true;
|
|
||||||
let account = wallet::add_account(
|
let account = wallet::add_account(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
&wdb.params,
|
||||||
|
@ -676,7 +673,6 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
},
|
},
|
||||||
wallet::ViewingKey::Full(Box::new(ufvk)),
|
wallet::ViewingKey::Full(Box::new(ufvk)),
|
||||||
birthday,
|
birthday,
|
||||||
spending_key_available,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((account, usk))
|
Ok((account, usk))
|
||||||
|
@ -687,16 +683,15 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
|
||||||
&mut self,
|
&mut self,
|
||||||
ufvk: &UnifiedFullViewingKey,
|
ufvk: &UnifiedFullViewingKey,
|
||||||
birthday: &AccountBirthday,
|
birthday: &AccountBirthday,
|
||||||
spending_key_available: bool,
|
purpose: AccountPurpose,
|
||||||
) -> Result<Self::Account, Self::Error> {
|
) -> Result<Self::Account, Self::Error> {
|
||||||
self.transactionally(|wdb| {
|
self.transactionally(|wdb| {
|
||||||
wallet::add_account(
|
wallet::add_account(
|
||||||
wdb.conn.0,
|
wdb.conn.0,
|
||||||
&wdb.params,
|
&wdb.params,
|
||||||
AccountSource::Imported,
|
AccountSource::Imported { purpose },
|
||||||
wallet::ViewingKey::Full(Box::new(ufvk.to_owned())),
|
wallet::ViewingKey::Full(Box::new(ufvk.to_owned())),
|
||||||
birthday,
|
birthday,
|
||||||
spending_key_available,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2029,7 +2024,8 @@ extern crate assert_matches;
|
||||||
mod tests {
|
mod tests {
|
||||||
use secrecy::{Secret, SecretVec};
|
use secrecy::{Secret, SecretVec};
|
||||||
use zcash_client_backend::data_api::{
|
use zcash_client_backend::data_api::{
|
||||||
chain::ChainState, Account, AccountBirthday, AccountSource, WalletRead, WalletWrite,
|
chain::ChainState, Account, AccountBirthday, AccountPurpose, AccountSource, WalletRead,
|
||||||
|
WalletWrite,
|
||||||
};
|
};
|
||||||
use zcash_keys::keys::UnifiedSpendingKey;
|
use zcash_keys::keys::UnifiedSpendingKey;
|
||||||
use zcash_primitives::block::BlockHash;
|
use zcash_primitives::block::BlockHash;
|
||||||
|
@ -2177,14 +2173,19 @@ mod tests {
|
||||||
|
|
||||||
let account = st
|
let account = st
|
||||||
.wallet_mut()
|
.wallet_mut()
|
||||||
.import_account_ufvk(&ufvk, &birthday, true)
|
.import_account_ufvk(&ufvk, &birthday, AccountPurpose::Spending)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ufvk.encode(&st.wallet().params),
|
ufvk.encode(&st.wallet().params),
|
||||||
account.ufvk().unwrap().encode(&st.wallet().params)
|
account.ufvk().unwrap().encode(&st.wallet().params)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(account.source(), AccountSource::Imported);
|
assert_matches!(
|
||||||
|
account.source(),
|
||||||
|
AccountSource::Imported {
|
||||||
|
purpose: AccountPurpose::Spending
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2202,7 +2203,7 @@ mod tests {
|
||||||
let ufvk = seed_based_account.ufvk().unwrap();
|
let ufvk = seed_based_account.ufvk().unwrap();
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
st.wallet_mut().import_account_ufvk(ufvk, &birthday, true),
|
st.wallet_mut().import_account_ufvk(ufvk, &birthday, AccountPurpose::Spending),
|
||||||
Err(SqliteClientError::AccountCollision(id)) if id == seed_based.0);
|
Err(SqliteClientError::AccountCollision(id)) if id == seed_based.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ use incrementalmerkletree::{Marking, Retention};
|
||||||
use rusqlite::{self, named_params, params, OptionalExtension};
|
use rusqlite::{self, named_params, params, OptionalExtension};
|
||||||
use secrecy::{ExposeSecret, SecretVec};
|
use secrecy::{ExposeSecret, SecretVec};
|
||||||
use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree};
|
use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree};
|
||||||
use zcash_client_backend::data_api::{TransactionDataRequest, TransactionStatus};
|
use zcash_client_backend::data_api::{AccountPurpose, TransactionDataRequest, TransactionStatus};
|
||||||
use zip32::fingerprint::SeedFingerprint;
|
use zip32::fingerprint::SeedFingerprint;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -146,6 +146,7 @@ fn parse_account_source(
|
||||||
account_kind: u32,
|
account_kind: u32,
|
||||||
hd_seed_fingerprint: Option<[u8; 32]>,
|
hd_seed_fingerprint: Option<[u8; 32]>,
|
||||||
hd_account_index: Option<u32>,
|
hd_account_index: Option<u32>,
|
||||||
|
spending_key_available: bool,
|
||||||
) -> Result<AccountSource, SqliteClientError> {
|
) -> Result<AccountSource, SqliteClientError> {
|
||||||
match (account_kind, hd_seed_fingerprint, hd_account_index) {
|
match (account_kind, hd_seed_fingerprint, hd_account_index) {
|
||||||
(0, Some(seed_fp), Some(account_index)) => Ok(AccountSource::Derived {
|
(0, Some(seed_fp), Some(account_index)) => Ok(AccountSource::Derived {
|
||||||
|
@ -156,7 +157,13 @@ fn parse_account_source(
|
||||||
)
|
)
|
||||||
})?,
|
})?,
|
||||||
}),
|
}),
|
||||||
(1, None, None) => Ok(AccountSource::Imported),
|
(1, None, None) => Ok(AccountSource::Imported {
|
||||||
|
purpose: if spending_key_available {
|
||||||
|
AccountPurpose::Spending
|
||||||
|
} else {
|
||||||
|
AccountPurpose::ViewOnly
|
||||||
|
},
|
||||||
|
}),
|
||||||
(0, None, None) | (1, Some(_), Some(_)) => Err(SqliteClientError::CorruptedData(
|
(0, None, None) | (1, Some(_), Some(_)) => Err(SqliteClientError::CorruptedData(
|
||||||
"Wallet DB account_kind constraint violated".to_string(),
|
"Wallet DB account_kind constraint violated".to_string(),
|
||||||
)),
|
)),
|
||||||
|
@ -169,7 +176,7 @@ fn parse_account_source(
|
||||||
fn account_kind_code(value: AccountSource) -> u32 {
|
fn account_kind_code(value: AccountSource) -> u32 {
|
||||||
match value {
|
match value {
|
||||||
AccountSource::Derived { .. } => 0,
|
AccountSource::Derived { .. } => 0,
|
||||||
AccountSource::Imported => 1,
|
AccountSource::Imported { .. } => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,14 +356,13 @@ pub(crate) fn add_account<P: consensus::Parameters>(
|
||||||
kind: AccountSource,
|
kind: AccountSource,
|
||||||
viewing_key: ViewingKey,
|
viewing_key: ViewingKey,
|
||||||
birthday: &AccountBirthday,
|
birthday: &AccountBirthday,
|
||||||
spending_key_available: bool,
|
|
||||||
) -> Result<Account, SqliteClientError> {
|
) -> Result<Account, SqliteClientError> {
|
||||||
let (hd_seed_fingerprint, hd_account_index) = match kind {
|
let (hd_seed_fingerprint, hd_account_index, spending_key_available) = match kind {
|
||||||
AccountSource::Derived {
|
AccountSource::Derived {
|
||||||
seed_fingerprint,
|
seed_fingerprint,
|
||||||
account_index,
|
account_index,
|
||||||
} => (Some(seed_fingerprint), Some(account_index)),
|
} => (Some(seed_fingerprint), Some(account_index), true),
|
||||||
AccountSource::Imported => (None, None),
|
AccountSource::Imported { purpose } => (None, None, purpose == AccountPurpose::Spending),
|
||||||
};
|
};
|
||||||
|
|
||||||
let orchard_item = viewing_key
|
let orchard_item = viewing_key
|
||||||
|
@ -676,7 +682,7 @@ pub(crate) fn get_account_for_ufvk<P: consensus::Parameters>(
|
||||||
let transparent_item: Option<Vec<u8>> = None;
|
let transparent_item: Option<Vec<u8>> = None;
|
||||||
|
|
||||||
let mut stmt = conn.prepare(
|
let mut stmt = conn.prepare(
|
||||||
"SELECT id, account_kind, hd_seed_fingerprint, hd_account_index, ufvk
|
"SELECT id, account_kind, hd_seed_fingerprint, hd_account_index, ufvk, has_spend_key
|
||||||
FROM accounts
|
FROM accounts
|
||||||
WHERE orchard_fvk_item_cache = :orchard_fvk_item_cache
|
WHERE orchard_fvk_item_cache = :orchard_fvk_item_cache
|
||||||
OR sapling_fvk_item_cache = :sapling_fvk_item_cache
|
OR sapling_fvk_item_cache = :sapling_fvk_item_cache
|
||||||
|
@ -691,12 +697,17 @@ pub(crate) fn get_account_for_ufvk<P: consensus::Parameters>(
|
||||||
":p2pkh_fvk_item_cache": transparent_item,
|
":p2pkh_fvk_item_cache": transparent_item,
|
||||||
],
|
],
|
||||||
|row| {
|
|row| {
|
||||||
let account_id = row.get::<_, u32>(0).map(AccountId)?;
|
let account_id = row.get::<_, u32>("id").map(AccountId)?;
|
||||||
let kind = parse_account_source(row.get(1)?, row.get(2)?, row.get(3)?)?;
|
let kind = parse_account_source(
|
||||||
|
row.get("account_kind")?,
|
||||||
|
row.get("hd_seed_fingerprint")?,
|
||||||
|
row.get("hd_account_index")?,
|
||||||
|
row.get("has_spend_key")?,
|
||||||
|
)?;
|
||||||
|
|
||||||
// We looked up the account by FVK components, so the UFVK column must be
|
// We looked up the account by FVK components, so the UFVK column must be
|
||||||
// non-null.
|
// non-null.
|
||||||
let ufvk_str: String = row.get(4)?;
|
let ufvk_str: String = row.get("ufvk")?;
|
||||||
let viewing_key = ViewingKey::Full(Box::new(
|
let viewing_key = ViewingKey::Full(Box::new(
|
||||||
UnifiedFullViewingKey::decode(params, &ufvk_str).map_err(|e| {
|
UnifiedFullViewingKey::decode(params, &ufvk_str).map_err(|e| {
|
||||||
SqliteClientError::CorruptedData(format!(
|
SqliteClientError::CorruptedData(format!(
|
||||||
|
@ -1501,7 +1512,7 @@ pub(crate) fn get_account<P: Parameters>(
|
||||||
) -> Result<Option<Account>, SqliteClientError> {
|
) -> Result<Option<Account>, SqliteClientError> {
|
||||||
let mut sql = conn.prepare_cached(
|
let mut sql = conn.prepare_cached(
|
||||||
r#"
|
r#"
|
||||||
SELECT account_kind, hd_seed_fingerprint, hd_account_index, ufvk, uivk
|
SELECT account_kind, hd_seed_fingerprint, hd_account_index, ufvk, uivk, has_spend_key
|
||||||
FROM accounts
|
FROM accounts
|
||||||
WHERE id = :account_id
|
WHERE id = :account_id
|
||||||
"#,
|
"#,
|
||||||
|
@ -1515,6 +1526,7 @@ pub(crate) fn get_account<P: Parameters>(
|
||||||
row.get("account_kind")?,
|
row.get("account_kind")?,
|
||||||
row.get("hd_seed_fingerprint")?,
|
row.get("hd_seed_fingerprint")?,
|
||||||
row.get("hd_account_index")?,
|
row.get("hd_account_index")?,
|
||||||
|
row.get("has_spend_key")?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let ufvk_str: Option<String> = row.get("ufvk")?;
|
let ufvk_str: Option<String> = row.get("ufvk")?;
|
||||||
|
|
|
@ -5,7 +5,10 @@ use rusqlite::{named_params, OptionalExtension, Transaction};
|
||||||
use schemer_rusqlite::RusqliteMigration;
|
use schemer_rusqlite::RusqliteMigration;
|
||||||
use secrecy::{ExposeSecret, SecretVec};
|
use secrecy::{ExposeSecret, SecretVec};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use zcash_client_backend::{data_api::AccountSource, keys::UnifiedSpendingKey};
|
use zcash_client_backend::{
|
||||||
|
data_api::{AccountPurpose, AccountSource},
|
||||||
|
keys::UnifiedSpendingKey,
|
||||||
|
};
|
||||||
use zcash_keys::keys::UnifiedFullViewingKey;
|
use zcash_keys::keys::UnifiedFullViewingKey;
|
||||||
use zcash_primitives::consensus;
|
use zcash_primitives::consensus;
|
||||||
use zip32::fingerprint::SeedFingerprint;
|
use zip32::fingerprint::SeedFingerprint;
|
||||||
|
@ -53,7 +56,11 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
|
||||||
seed_fingerprint: SeedFingerprint::from_bytes([0; 32]),
|
seed_fingerprint: SeedFingerprint::from_bytes([0; 32]),
|
||||||
account_index: zip32::AccountId::ZERO,
|
account_index: zip32::AccountId::ZERO,
|
||||||
});
|
});
|
||||||
let account_kind_imported = account_kind_code(AccountSource::Imported);
|
let account_kind_imported = account_kind_code(AccountSource::Imported {
|
||||||
|
// the purpose here is irrelevant; we just use it to get the correct code
|
||||||
|
// for the account kind
|
||||||
|
purpose: AccountPurpose::ViewOnly,
|
||||||
|
});
|
||||||
transaction.execute_batch(&format!(
|
transaction.execute_batch(&format!(
|
||||||
r#"
|
r#"
|
||||||
CREATE TABLE accounts_new (
|
CREATE TABLE accounts_new (
|
||||||
|
|
|
@ -4,7 +4,8 @@ use std::ops::Range;
|
||||||
|
|
||||||
use rusqlite::{named_params, OptionalExtension};
|
use rusqlite::{named_params, OptionalExtension};
|
||||||
|
|
||||||
use zcash_client_backend::{data_api::Account, wallet::TransparentAddressMetadata};
|
use zcash_client_backend::wallet::TransparentAddressMetadata;
|
||||||
|
use zcash_keys::keys::UnifiedFullViewingKey;
|
||||||
use zcash_keys::{encoding::AddressCodec, keys::AddressGenerationError};
|
use zcash_keys::{encoding::AddressCodec, keys::AddressGenerationError};
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
legacy::{
|
legacy::{
|
||||||
|
@ -15,11 +16,8 @@ use zcash_primitives::{
|
||||||
};
|
};
|
||||||
use zcash_protocol::consensus;
|
use zcash_protocol::consensus;
|
||||||
|
|
||||||
use crate::TxRef;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SqliteClientError,
|
error::SqliteClientError, wallet::GAP_LIMIT, AccountId, SqlTransaction, TxRef, WalletDb,
|
||||||
wallet::{get_account, GAP_LIMIT},
|
|
||||||
AccountId, SqlTransaction, WalletDb,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns `TransparentAddressMetadata` in the ephemeral scope for the
|
// Returns `TransparentAddressMetadata` in the ephemeral scope for the
|
||||||
|
@ -118,12 +116,29 @@ pub(crate) fn get_ephemeral_ivk<P: consensus::Parameters>(
|
||||||
params: &P,
|
params: &P,
|
||||||
account_id: AccountId,
|
account_id: AccountId,
|
||||||
) -> Result<EphemeralIvk, SqliteClientError> {
|
) -> Result<EphemeralIvk, SqliteClientError> {
|
||||||
Ok(get_account(conn, params, account_id)?
|
let ufvk = conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT ufvk FROM accounts WHERE id = :account_id",
|
||||||
|
named_params![":account_id": account_id.0],
|
||||||
|
|row| {
|
||||||
|
let ufvk_str: Option<String> = row.get("ufvk")?;
|
||||||
|
Ok(ufvk_str.map(|s| {
|
||||||
|
UnifiedFullViewingKey::decode(params, &s[..])
|
||||||
|
.map_err(SqliteClientError::BadAccountData)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.optional()?
|
||||||
.ok_or(SqliteClientError::AccountUnknown)?
|
.ok_or(SqliteClientError::AccountUnknown)?
|
||||||
.ufvk()
|
.transpose()?;
|
||||||
|
|
||||||
|
let eivk = ufvk
|
||||||
|
.as_ref()
|
||||||
.and_then(|ufvk| ufvk.transparent())
|
.and_then(|ufvk| ufvk.transparent())
|
||||||
.ok_or(SqliteClientError::UnknownZip32Derivation)?
|
.ok_or(SqliteClientError::UnknownZip32Derivation)?
|
||||||
.derive_ephemeral_ivk()?)
|
.derive_ephemeral_ivk()?;
|
||||||
|
|
||||||
|
Ok(eivk)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a vector of ephemeral transparent addresses associated with the given
|
/// Returns a vector of ephemeral transparent addresses associated with the given
|
||||||
|
|
Loading…
Reference in New Issue