zcash_client_backend: Make wallet and account birthday heights available via the data access API

This commit is contained in:
Kris Nuttycombe 2023-08-16 11:15:10 -06:00
parent 459dc49b54
commit 0b0274cdc1
6 changed files with 69 additions and 2 deletions

View File

@ -29,7 +29,8 @@ and this library adheres to Rust's notion of
- `ScannedBlock`
- `ShieldedProtocol`
- `WalletCommitmentTrees`
- `WalletRead::{chain_height, block_metadata, block_fully_scanned, suggest_scan_ranges}`
- `WalletRead::{chain_height, block_metadata, block_fully_scanned, suggest_scan_ranges,
get_wallet_birthday, get_account_birthday}`
- `WalletWrite::{put_blocks, update_chain_tip}`
- `chain::CommitmentTreeRoot`
- `scanning` A new module containing types required for `suggest_scan_ranges`

View File

@ -119,6 +119,16 @@ pub trait WalletRead {
/// transaction is not in the main chain.
fn get_tx_height(&self, txid: TxId) -> Result<Option<BlockHeight>, Self::Error>;
/// Returns the birthday height for the wallet.
///
/// This returns earliest birthday height among accounts maintained by this wallet, or
/// `Ok(None)` if the wallet has no initialized accounts.
fn get_wallet_birthday(&self) -> Result<Option<BlockHeight>, Self::Error>;
/// Returns the birthday height for the given account, or an error if the account is not known
/// to the wallet.
fn get_account_birthday(&self, account: AccountId) -> Result<BlockHeight, Self::Error>;
/// Returns the most recently generated unified address for the specified account, if the
/// account identifier specified refers to a valid account for this wallet.
///
@ -801,6 +811,14 @@ pub mod testing {
Ok(None)
}
fn get_wallet_birthday(&self) -> Result<Option<BlockHeight>, Self::Error> {
Ok(None)
}
fn get_account_birthday(&self, _account: AccountId) -> Result<BlockHeight, Self::Error> {
Err(())
}
fn get_current_address(
&self,
_account: AccountId,

View File

@ -26,6 +26,7 @@ and this library adheres to Rust's notion of
wallet did not contain enough observed blocks to satisfy the `min_confirmations`
value specified; this situation is now treated as an error.
- `zcash_client_sqlite::error::SqliteClientError` has new error variants:
- `SqliteClientError::AccountUnknown`
- `SqliteClientError::BlockConflict`
- `SqliteClientError::CacheMiss`
- `SqliteClientError::ChainHeightUnknown`
@ -37,7 +38,7 @@ and this library adheres to Rust's notion of
### Removed
- The empty `wallet::transact` module has been removed.
- `zcash_client_sqlite::NoteId` has been replaced with `zcash_client_sqlite::ReceivedNoteId`
as the `SentNoteId` variant of is now unused following changes to
as the `SentNoteId` variant of is now unused following changes to
`zcash_client_backend::data_api::WalletRead`.
- `zcash_client_sqlite::wallet::init::{init_blocks_table, init_accounts_table}`
have been removed. `zcash_client_backend::data_api::WalletWrite::create_account`

View File

@ -66,6 +66,9 @@ pub enum SqliteClientError {
/// The space of allocatable diversifier indices has been exhausted for the given account.
DiversifierIndexOutOfRange,
/// The account for which information was requested does not belong to the wallet.
AccountUnknown(AccountId),
/// An error occurred deriving a spending key from a seed and an account
/// identifier.
KeyDerivationError(AccountId),
@ -132,6 +135,8 @@ impl fmt::Display for SqliteClientError {
SqliteClientError::BlockConflict(h) => write!(f, "A block hash conflict occurred at height {}; rewind required.", u32::from(*h)),
SqliteClientError::NonSequentialBlocks => write!(f, "`put_blocks` requires that the provided block range be sequential"),
SqliteClientError::DiversifierIndexOutOfRange => write!(f, "The space of available diversifier indices is exhausted"),
SqliteClientError::AccountUnknown(id) => write!(f, "Account {} does not belong to this wallet.", u32::from(*id)),
SqliteClientError::KeyDerivationError(acct_id) => write!(f, "Key derivation failed for account {:?}", acct_id),
SqliteClientError::AccountIdDiscontinuity => write!(f, "Wallet account identifiers must be sequential."),
SqliteClientError::AccountIdOutOfRange => write!(f, "Wallet account identifiers must be less than 0x7FFFFFFF."),

View File

@ -206,6 +206,14 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
wallet::get_tx_height(self.conn.borrow(), txid).map_err(SqliteClientError::from)
}
fn get_wallet_birthday(&self) -> Result<Option<BlockHeight>, Self::Error> {
wallet::wallet_birthday(self.conn.borrow()).map_err(SqliteClientError::from)
}
fn get_account_birthday(&self, account: AccountId) -> Result<BlockHeight, Self::Error> {
wallet::account_birthday(self.conn.borrow(), account).map_err(SqliteClientError::from)
}
fn get_current_address(
&self,
account: AccountId,

View File

@ -675,6 +675,40 @@ pub(crate) fn get_sent_memo(
.transpose()
}
/// Returns the minimum birthday height for accounts in the wallet.
///
/// TODO ORCHARD: we should consider whether we want to permit protocol-restricted accounts; if so,
/// we would then want this method to take a protocol identifier to be able to learn the wallet's
/// "Orchard birthday" which might be different from the overall wallet birthday.
pub(crate) fn wallet_birthday(
conn: &rusqlite::Connection,
) -> Result<Option<BlockHeight>, rusqlite::Error> {
conn.query_row(
"SELECT MIN(birthday_height) AS wallet_birthday FROM accounts",
[],
|row| {
row.get::<_, Option<u32>>(0)
.map(|opt| opt.map(BlockHeight::from))
},
)
}
pub(crate) fn account_birthday(
conn: &rusqlite::Connection,
account: AccountId,
) -> Result<BlockHeight, SqliteClientError> {
conn.query_row(
"SELECT birthday_height
FROM accounts
WHERE account = :account_id",
named_params![":account_id": u32::from(account)],
|row| row.get::<_, u32>(0).map(BlockHeight::from),
)
.optional()
.map_err(SqliteClientError::from)
.and_then(|opt| opt.ok_or(SqliteClientError::AccountUnknown(account)))
}
/// Returns the minimum and maximum heights for blocks stored in the wallet database.
pub(crate) fn block_height_extrema(
conn: &rusqlite::Connection,