2021-01-12 13:59:42 -08:00
|
|
|
//! Interfaces for wallet data persistence & low-level wallet utilities.
|
|
|
|
|
2020-08-26 14:47:47 -07:00
|
|
|
use std::cmp;
|
2021-01-11 17:13:40 -08:00
|
|
|
use std::collections::HashMap;
|
2020-09-11 15:17:43 -07:00
|
|
|
use std::fmt::Debug;
|
2020-08-26 14:47:47 -07:00
|
|
|
|
2022-09-08 11:48:06 -07:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
2022-09-14 12:38:00 -07:00
|
|
|
use secrecy::SecretVec;
|
2020-07-22 19:44:06 -07:00
|
|
|
use zcash_primitives::{
|
|
|
|
block::BlockHash,
|
2021-01-12 20:10:34 -08:00
|
|
|
consensus::BlockHeight,
|
2020-10-29 09:48:26 -07:00
|
|
|
memo::{Memo, MemoBytes},
|
2020-08-18 12:48:59 -07:00
|
|
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
2022-08-31 08:09:25 -07:00
|
|
|
sapling::{Node, Nullifier},
|
2022-01-20 13:33:29 -08:00
|
|
|
transaction::{components::Amount, Transaction, TxId},
|
2022-01-24 17:02:25 -08:00
|
|
|
zip32::{AccountId, ExtendedFullViewingKey},
|
2020-07-22 19:44:06 -07:00
|
|
|
};
|
|
|
|
|
2020-08-20 10:41:43 -07:00
|
|
|
use crate::{
|
2022-08-31 08:09:25 -07:00
|
|
|
address::{RecipientAddress, UnifiedAddress},
|
2020-08-25 14:02:44 -07:00
|
|
|
decrypt::DecryptedOutput,
|
2022-09-13 15:43:04 -07:00
|
|
|
keys::{UnifiedFullViewingKey, UnifiedSpendingKey},
|
2020-08-20 10:41:43 -07:00
|
|
|
proto::compact_formats::CompactBlock,
|
2022-01-24 17:02:25 -08:00
|
|
|
wallet::{SpendableNote, WalletTx},
|
2020-08-20 10:41:43 -07:00
|
|
|
};
|
2020-07-22 19:44:06 -07:00
|
|
|
|
2021-02-12 13:08:31 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2022-01-20 13:33:29 -08:00
|
|
|
use {
|
|
|
|
crate::wallet::WalletTransparentOutput,
|
|
|
|
zcash_primitives::{legacy::TransparentAddress, transaction::components::OutPoint},
|
|
|
|
};
|
2021-02-12 13:08:31 -08:00
|
|
|
|
2020-07-22 19:44:06 -07:00
|
|
|
pub mod chain;
|
|
|
|
pub mod error;
|
2020-08-25 14:02:44 -07:00
|
|
|
pub mod wallet;
|
2020-07-22 19:44:06 -07:00
|
|
|
|
2021-01-15 10:20:29 -08:00
|
|
|
/// Read-only operations required for light wallet functions.
|
2021-01-08 11:49:10 -08:00
|
|
|
///
|
2021-09-03 16:32:40 -07:00
|
|
|
/// This trait defines the read-only portion of the storage interface atop which
|
|
|
|
/// higher-level wallet operations are implemented. It serves to allow wallet functions to
|
|
|
|
/// be abstracted away from any particular data storage substrate.
|
2020-10-19 14:20:34 -07:00
|
|
|
pub trait WalletRead {
|
2021-01-08 11:49:10 -08:00
|
|
|
/// The type of errors produced by a wallet backend.
|
2020-07-22 19:44:06 -07:00
|
|
|
type Error;
|
2021-01-08 11:49:10 -08:00
|
|
|
|
2021-01-11 17:13:40 -08:00
|
|
|
/// Backend-specific note identifier.
|
2021-01-08 11:49:10 -08:00
|
|
|
///
|
|
|
|
/// For example, this might be a database identifier type
|
|
|
|
/// or a UUID.
|
2021-01-11 17:13:40 -08:00
|
|
|
type NoteRef: Copy + Debug;
|
2021-01-08 11:49:10 -08:00
|
|
|
|
2021-01-11 17:13:40 -08:00
|
|
|
/// Backend-specific transaction identifier.
|
2021-01-08 11:49:10 -08:00
|
|
|
///
|
|
|
|
/// For example, this might be a database identifier type
|
|
|
|
/// or a TxId if the backend is able to support that type
|
|
|
|
/// directly.
|
2020-09-11 15:17:43 -07:00
|
|
|
type TxRef: Copy + Debug;
|
2020-07-22 19:44:06 -07:00
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the minimum and maximum block heights for stored blocks.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// This will return `Ok(None)` if no block data is present in the database.
|
2020-07-22 19:44:06 -07:00
|
|
|
fn block_height_extrema(&self) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error>;
|
|
|
|
|
2021-01-15 10:20:29 -08:00
|
|
|
/// Returns the default target height (for the block in which a new
|
|
|
|
/// transaction would be mined) and anchor height (to use for a new
|
|
|
|
/// transaction), given the range of block heights that the backend
|
|
|
|
/// knows about.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// This will return `Ok(None)` if no block data is present in the database.
|
2020-08-26 14:47:47 -07:00
|
|
|
fn get_target_and_anchor_heights(
|
|
|
|
&self,
|
2021-08-27 12:45:36 -07:00
|
|
|
min_confirmations: u32,
|
2020-08-26 14:47:47 -07:00
|
|
|
) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error> {
|
|
|
|
self.block_height_extrema().map(|heights| {
|
|
|
|
heights.map(|(min_height, max_height)| {
|
|
|
|
let target_height = max_height + 1;
|
|
|
|
|
2022-01-27 07:54:27 -08:00
|
|
|
// Select an anchor min_confirmations back from the target block,
|
2020-08-26 14:47:47 -07:00
|
|
|
// unless that would be before the earliest block we have.
|
|
|
|
let anchor_height = BlockHeight::from(cmp::max(
|
2021-08-27 12:45:36 -07:00
|
|
|
u32::from(target_height).saturating_sub(min_confirmations),
|
2020-08-26 14:47:47 -07:00
|
|
|
u32::from(min_height),
|
|
|
|
));
|
|
|
|
|
|
|
|
(target_height, anchor_height)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-19 11:41:12 -08:00
|
|
|
/// Returns the block hash for the block at the given height, if the
|
|
|
|
/// associated block data is available. Returns `Ok(None)` if the hash
|
|
|
|
/// is not found in the database.
|
2020-07-22 19:44:06 -07:00
|
|
|
fn get_block_hash(&self, block_height: BlockHeight) -> Result<Option<BlockHash>, Self::Error>;
|
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the block hash for the block at the maximum height known
|
|
|
|
/// in stored data.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// This will return `Ok(None)` if no block data is present in the database.
|
2020-10-19 14:16:06 -07:00
|
|
|
fn get_max_height_hash(&self) -> Result<Option<(BlockHeight, BlockHash)>, Self::Error> {
|
2020-10-19 14:20:34 -07:00
|
|
|
self.block_height_extrema()
|
|
|
|
.and_then(|extrema_opt| {
|
|
|
|
extrema_opt
|
|
|
|
.map(|(_, max_height)| {
|
|
|
|
self.get_block_hash(max_height)
|
|
|
|
.map(|hash_opt| hash_opt.map(move |hash| (max_height, hash)))
|
|
|
|
})
|
|
|
|
.transpose()
|
|
|
|
})
|
|
|
|
.map(|oo| oo.flatten())
|
2020-10-19 14:16:06 -07:00
|
|
|
}
|
|
|
|
|
2021-01-15 10:20:29 -08:00
|
|
|
/// Returns the block height in which the specified transaction was mined,
|
2021-01-19 11:41:12 -08:00
|
|
|
/// or `Ok(None)` if the transaction is not mined in the main chain.
|
2020-08-25 14:02:44 -07:00
|
|
|
fn get_tx_height(&self, txid: TxId) -> Result<Option<BlockHeight>, Self::Error>;
|
|
|
|
|
2022-09-12 11:42:12 -07:00
|
|
|
/// Returns the most recently generated unified address for the specified account, if the
|
|
|
|
/// account identifier specified refers to a valid account for this wallet.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// This will return `Ok(None)` if the account identifier does not correspond
|
|
|
|
/// to a known account.
|
2022-09-12 11:42:12 -07:00
|
|
|
fn get_current_address(
|
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
) -> Result<Option<UnifiedAddress>, Self::Error>;
|
2020-08-05 16:01:22 -07:00
|
|
|
|
2022-06-13 17:57:20 -07:00
|
|
|
/// Returns all unified full viewing keys known to this wallet.
|
|
|
|
fn get_unified_full_viewing_keys(
|
2020-08-25 14:02:44 -07:00
|
|
|
&self,
|
2022-06-13 17:57:20 -07:00
|
|
|
) -> Result<HashMap<AccountId, UnifiedFullViewingKey>, Self::Error>;
|
2020-08-25 14:02:44 -07:00
|
|
|
|
2022-10-03 13:44:04 -07:00
|
|
|
/// Returns all unified full viewing keys known to this wallet.
|
|
|
|
fn get_account_for_ufvk(
|
|
|
|
&self,
|
|
|
|
ufvk: &UnifiedFullViewingKey,
|
|
|
|
) -> Result<Option<AccountId>, Self::Error>;
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
/// Checks whether the specified extended full viewing key is
|
2021-01-11 17:13:40 -08:00
|
|
|
/// associated with the account.
|
2021-01-12 17:24:18 -08:00
|
|
|
fn is_valid_account_extfvk(
|
2020-08-26 14:47:47 -07:00
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
extfvk: &ExtendedFullViewingKey,
|
|
|
|
) -> Result<bool, Self::Error>;
|
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the wallet balance for an account as of the specified block
|
2021-01-15 10:20:29 -08:00
|
|
|
/// height.
|
2021-01-11 17:13:40 -08:00
|
|
|
///
|
2021-01-08 11:49:10 -08:00
|
|
|
/// This may be used to obtain a balance that ignores notes that have been
|
|
|
|
/// received so recently that they are not yet deemed spendable.
|
2021-01-15 11:00:14 -08:00
|
|
|
fn get_balance_at(
|
2020-08-26 14:47:47 -07:00
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
anchor_height: BlockHeight,
|
|
|
|
) -> Result<Amount, Self::Error>;
|
2020-08-06 13:11:25 -07:00
|
|
|
|
2020-10-29 09:48:26 -07:00
|
|
|
/// Returns the memo for a note.
|
2021-09-03 16:32:40 -07:00
|
|
|
///
|
|
|
|
/// Implementations of this method must return an error if the note identifier
|
|
|
|
/// does not appear in the backing data store.
|
2020-10-29 09:48:26 -07:00
|
|
|
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Memo, Self::Error>;
|
2020-08-06 13:11:25 -07:00
|
|
|
|
2021-04-13 10:02:35 -07:00
|
|
|
/// Returns a transaction.
|
|
|
|
fn get_transaction(&self, id_tx: Self::TxRef) -> Result<Transaction, Self::Error>;
|
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the note commitment tree at the specified block height.
|
2020-08-06 17:13:24 -07:00
|
|
|
fn get_commitment_tree(
|
|
|
|
&self,
|
|
|
|
block_height: BlockHeight,
|
|
|
|
) -> Result<Option<CommitmentTree<Node>>, Self::Error>;
|
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the incremental witnesses as of the specified block height.
|
2021-02-03 11:46:47 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2020-08-18 12:48:59 -07:00
|
|
|
fn get_witnesses(
|
|
|
|
&self,
|
|
|
|
block_height: BlockHeight,
|
2020-08-20 10:41:43 -07:00
|
|
|
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error>;
|
2020-08-18 12:48:59 -07:00
|
|
|
|
2022-02-02 11:53:59 -08:00
|
|
|
/// Returns the nullifiers for notes that the wallet is tracking, along with their
|
|
|
|
/// associated account IDs, that are either unspent or have not yet been confirmed as
|
|
|
|
/// spent (in that the spending transaction has not yet been included in a block).
|
2021-01-15 09:18:43 -08:00
|
|
|
fn get_nullifiers(&self) -> Result<Vec<(AccountId, Nullifier)>, Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
|
2022-02-02 11:53:59 -08:00
|
|
|
/// Returns all nullifiers for notes that the wallet is tracking
|
2022-02-02 09:29:19 -08:00
|
|
|
/// (including those for notes that have been previously spent),
|
|
|
|
/// along with the account identifiers with which they are associated.
|
2021-04-14 10:20:56 -07:00
|
|
|
fn get_all_nullifiers(&self) -> Result<Vec<(AccountId, Nullifier)>, Self::Error>;
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
/// Return all unspent Sapling notes.
|
|
|
|
fn get_spendable_sapling_notes(
|
2021-01-12 19:33:53 -08:00
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error>;
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
/// Returns a list of spendable Sapling notes sufficient to cover the specified
|
2021-01-12 20:10:34 -08:00
|
|
|
/// target value, if possible.
|
2021-09-03 16:32:40 -07:00
|
|
|
fn select_spendable_sapling_notes(
|
2020-08-26 14:47:47 -07:00
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
target_value: Amount,
|
|
|
|
anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error>;
|
2022-01-31 15:22:20 -08:00
|
|
|
}
|
2020-12-22 06:10:13 -08:00
|
|
|
|
2022-01-31 15:22:20 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
pub trait WalletReadTransparent: WalletRead {
|
2022-09-08 11:48:06 -07:00
|
|
|
/// Returns the set of all transparent receivers associated with the given account.
|
|
|
|
///
|
|
|
|
/// The set contains all transparent receivers that are known to have been derived
|
|
|
|
/// under this account. Wallets should scan the chain for UTXOs sent to these
|
|
|
|
/// receivers.
|
|
|
|
fn get_transparent_receivers(
|
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
) -> Result<HashSet<TransparentAddress>, Self::Error>;
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
/// Returns a list of unspent transparent UTXOs that appear in the chain at heights up to and
|
|
|
|
/// including `max_height`.
|
|
|
|
fn get_unspent_transparent_outputs(
|
2020-12-22 06:10:13 -08:00
|
|
|
&self,
|
|
|
|
address: &TransparentAddress,
|
2021-09-03 16:32:40 -07:00
|
|
|
max_height: BlockHeight,
|
2020-12-22 06:10:13 -08:00
|
|
|
) -> Result<Vec<WalletTransparentOutput>, Self::Error>;
|
2020-07-22 19:44:06 -07:00
|
|
|
}
|
|
|
|
|
2021-03-09 17:10:44 -08:00
|
|
|
/// The subset of information that is relevant to this wallet that has been
|
|
|
|
/// decrypted and extracted from a [CompactBlock].
|
|
|
|
pub struct PrunedBlock<'a> {
|
|
|
|
pub block_height: BlockHeight,
|
|
|
|
pub block_hash: BlockHash,
|
|
|
|
pub block_time: u32,
|
|
|
|
pub commitment_tree: &'a CommitmentTree<Node>,
|
2021-03-09 19:55:44 -08:00
|
|
|
pub transactions: &'a Vec<WalletTx<Nullifier>>,
|
2021-03-09 17:10:44 -08:00
|
|
|
}
|
|
|
|
|
2021-03-25 17:26:57 -07:00
|
|
|
/// A transaction that was detected during scanning of the blockchain,
|
|
|
|
/// including its decrypted Sapling outputs.
|
|
|
|
///
|
|
|
|
/// The purpose of this struct is to permit atomic updates of the
|
|
|
|
/// wallet database when transactions are successfully decrypted.
|
2021-03-29 13:35:18 -07:00
|
|
|
pub struct DecryptedTransaction<'a> {
|
2021-03-09 17:10:44 -08:00
|
|
|
pub tx: &'a Transaction,
|
2021-03-29 13:35:18 -07:00
|
|
|
pub sapling_outputs: &'a Vec<DecryptedOutput>,
|
2021-03-09 17:10:44 -08:00
|
|
|
}
|
|
|
|
|
2021-03-25 17:26:57 -07:00
|
|
|
/// A transaction that was constructed and sent by the wallet.
|
|
|
|
///
|
|
|
|
/// The purpose of this struct is to permit atomic updates of the
|
|
|
|
/// wallet database when transactions are created and submitted
|
|
|
|
/// to the network.
|
2021-03-09 17:10:44 -08:00
|
|
|
pub struct SentTransaction<'a> {
|
|
|
|
pub tx: &'a Transaction,
|
|
|
|
pub created: time::OffsetDateTime,
|
2021-04-09 13:40:35 -07:00
|
|
|
pub account: AccountId,
|
|
|
|
pub outputs: Vec<SentTransactionOutput<'a>>,
|
2022-09-01 20:03:39 -07:00
|
|
|
pub fee_amount: Amount,
|
2022-01-20 13:33:29 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2021-04-09 13:40:35 -07:00
|
|
|
pub utxos_spent: Vec<OutPoint>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SentTransactionOutput<'a> {
|
2021-03-23 22:54:15 -07:00
|
|
|
/// The index within the transaction that contains the recipient output.
|
|
|
|
///
|
|
|
|
/// - If `recipient_address` is a Sapling address, this is an index into the Sapling
|
|
|
|
/// outputs of the transaction.
|
|
|
|
/// - If `recipient_address` is a transparent address, this is an index into the
|
|
|
|
/// transparent outputs of the transaction.
|
2021-03-09 17:10:44 -08:00
|
|
|
pub output_index: usize,
|
|
|
|
pub recipient_address: &'a RecipientAddress,
|
|
|
|
pub value: Amount,
|
2020-10-29 09:48:26 -07:00
|
|
|
pub memo: Option<MemoBytes>,
|
2021-03-09 17:10:44 -08:00
|
|
|
}
|
2021-03-09 12:15:27 -08:00
|
|
|
|
2021-01-12 20:12:28 -08:00
|
|
|
/// This trait encapsulates the write capabilities required to update stored
|
2021-01-08 17:08:02 -08:00
|
|
|
/// wallet data.
|
2021-01-08 11:49:10 -08:00
|
|
|
pub trait WalletWrite: WalletRead {
|
2022-09-14 11:20:39 -07:00
|
|
|
/// Tells the wallet to track the next available account-level spend authority, given
|
|
|
|
/// the current set of [ZIP 316] account identifiers known to the wallet database.
|
|
|
|
///
|
|
|
|
/// Returns the account identifier for the newly-created wallet database entry, along
|
|
|
|
/// with the associated [`UnifiedSpendingKey`].
|
|
|
|
///
|
|
|
|
/// If `seed` was imported from a backup and this method is being used to restore a
|
|
|
|
/// previous wallet state, you should use this method to add all of the desired
|
|
|
|
/// accounts before scanning the chain from the seed's birthday height.
|
|
|
|
///
|
|
|
|
/// By convention, wallets should only allow a new account to be generated after funds
|
|
|
|
/// have been received by the currently-available account (in order to enable
|
|
|
|
/// automated account recovery).
|
|
|
|
///
|
|
|
|
/// [ZIP 316]: https://zips.z.cash/zip-0316
|
2022-09-13 15:43:04 -07:00
|
|
|
fn create_account(
|
|
|
|
&mut self,
|
2022-09-14 11:20:39 -07:00
|
|
|
seed: &SecretVec<u8>,
|
2022-09-13 15:43:04 -07:00
|
|
|
) -> Result<(AccountId, UnifiedSpendingKey), Self::Error>;
|
|
|
|
|
2022-09-12 11:42:12 -07:00
|
|
|
/// Generates and persists the next available diversified address, given the current
|
|
|
|
/// addresses known to the wallet.
|
|
|
|
///
|
|
|
|
/// Returns `Ok(None)` if the account identifier does not correspond to a known
|
|
|
|
/// account.
|
|
|
|
fn get_next_available_address(
|
|
|
|
&mut self,
|
|
|
|
account: AccountId,
|
|
|
|
) -> Result<Option<UnifiedAddress>, Self::Error>;
|
|
|
|
|
2022-02-01 10:05:51 -08:00
|
|
|
/// Updates the state of the wallet database by persisting the provided
|
|
|
|
/// block information, along with the updated witness data that was
|
|
|
|
/// produced when scanning the block for transactions pertaining to
|
|
|
|
/// this wallet.
|
2021-03-09 19:55:44 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2021-03-11 12:01:22 -08:00
|
|
|
fn advance_by_block(
|
2020-08-20 10:41:43 -07:00
|
|
|
&mut self,
|
2021-03-09 17:10:44 -08:00
|
|
|
block: &PrunedBlock,
|
|
|
|
updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
|
|
|
|
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error>;
|
|
|
|
|
2022-02-01 10:05:51 -08:00
|
|
|
/// Caches a decrypted transaction in the persistent wallet store.
|
2021-03-29 13:35:18 -07:00
|
|
|
fn store_decrypted_tx(
|
2021-03-09 17:10:44 -08:00
|
|
|
&mut self,
|
2021-03-29 13:35:18 -07:00
|
|
|
received_tx: &DecryptedTransaction,
|
2021-03-09 17:10:44 -08:00
|
|
|
) -> Result<Self::TxRef, Self::Error>;
|
|
|
|
|
2022-09-12 11:42:12 -07:00
|
|
|
/// Saves information about a transaction that was constructed and sent by the wallet to the
|
|
|
|
/// persistent wallet store.
|
2021-03-09 17:10:44 -08:00
|
|
|
fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result<Self::TxRef, Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
|
2022-08-30 15:54:58 -07:00
|
|
|
/// Removes the specified unmined transaction from the persistent wallet store, if it
|
|
|
|
/// exists.
|
2022-08-29 14:32:18 -07:00
|
|
|
///
|
2022-08-30 15:54:58 -07:00
|
|
|
/// Returns an error if the specified transaction has been mined. To remove a mined
|
|
|
|
/// transaction, first use [`WalletWrite::rewind_to_height`] to unmine it.
|
2022-08-29 14:32:18 -07:00
|
|
|
#[cfg(feature = "unstable")]
|
2022-08-30 15:54:58 -07:00
|
|
|
fn remove_unmined_tx(&mut self, txid: &TxId) -> Result<(), Self::Error>;
|
2022-08-29 14:32:18 -07:00
|
|
|
|
2021-01-15 10:20:29 -08:00
|
|
|
/// Rewinds the wallet database to the specified height.
|
|
|
|
///
|
2021-01-08 17:08:02 -08:00
|
|
|
/// This method assumes that the state of the underlying data store is
|
|
|
|
/// consistent up to a particular block height. Since it is possible that
|
|
|
|
/// a chain reorg might invalidate some stored state, this method must be
|
|
|
|
/// implemented in order to allow users of this API to "reset" the data store
|
|
|
|
/// to correctly represent chainstate as of a specified block height.
|
2021-01-15 11:00:14 -08:00
|
|
|
///
|
|
|
|
/// After calling this method, the block at the given height will be the
|
|
|
|
/// most recent block and all other operations will treat this block
|
|
|
|
/// as the chain tip for balance determination purposes.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// There may be restrictions on how far it is possible to rewind.
|
2021-01-12 20:10:34 -08:00
|
|
|
fn rewind_to_height(&mut self, block_height: BlockHeight) -> Result<(), Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
}
|
|
|
|
|
2022-01-31 15:22:20 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
pub trait WalletWriteTransparent: WalletWrite + WalletReadTransparent {
|
|
|
|
type UtxoRef;
|
|
|
|
|
|
|
|
fn put_received_transparent_utxo(
|
|
|
|
&mut self,
|
|
|
|
output: &WalletTransparentOutput,
|
|
|
|
) -> Result<Self::UtxoRef, Self::Error>;
|
|
|
|
}
|
|
|
|
|
2021-01-12 12:55:22 -08:00
|
|
|
/// This trait provides sequential access to raw blockchain data via a callback-oriented
|
|
|
|
/// API.
|
2020-10-19 14:20:34 -07:00
|
|
|
pub trait BlockSource {
|
2020-07-22 19:44:06 -07:00
|
|
|
type Error;
|
|
|
|
|
2021-01-12 12:55:22 -08:00
|
|
|
/// Scan the specified `limit` number of blocks from the blockchain, starting at
|
|
|
|
/// `from_height`, applying the provided callback to each block.
|
2020-10-19 15:52:48 -07:00
|
|
|
fn with_blocks<F>(
|
2020-08-18 15:33:34 -07:00
|
|
|
&self,
|
|
|
|
from_height: BlockHeight,
|
|
|
|
limit: Option<u32>,
|
|
|
|
with_row: F,
|
|
|
|
) -> Result<(), Self::Error>
|
|
|
|
where
|
2020-10-19 15:52:48 -07:00
|
|
|
F: FnMut(CompactBlock) -> Result<(), Self::Error>;
|
2020-07-22 19:44:06 -07:00
|
|
|
}
|
2020-08-25 14:02:44 -07:00
|
|
|
|
2021-01-12 11:55:24 -08:00
|
|
|
#[cfg(feature = "test-dependencies")]
|
|
|
|
pub mod testing {
|
2022-09-13 15:43:04 -07:00
|
|
|
use secrecy::{ExposeSecret, SecretVec};
|
2021-01-12 11:55:24 -08:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2022-09-08 11:48:06 -07:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
2021-01-12 11:55:24 -08:00
|
|
|
use zcash_primitives::{
|
|
|
|
block::BlockHash,
|
2022-09-13 15:43:04 -07:00
|
|
|
consensus::{BlockHeight, Network},
|
2020-12-22 06:10:13 -08:00
|
|
|
legacy::TransparentAddress,
|
2020-10-29 09:48:26 -07:00
|
|
|
memo::Memo,
|
2021-01-12 11:55:24 -08:00
|
|
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
2022-08-31 08:09:25 -07:00
|
|
|
sapling::{Node, Nullifier},
|
2021-04-13 10:02:35 -07:00
|
|
|
transaction::{components::Amount, Transaction, TxId},
|
2022-01-24 17:02:25 -08:00
|
|
|
zip32::{AccountId, ExtendedFullViewingKey},
|
2021-01-12 11:55:24 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
2022-08-31 08:09:25 -07:00
|
|
|
address::UnifiedAddress,
|
2022-09-13 15:43:04 -07:00
|
|
|
keys::{UnifiedFullViewingKey, UnifiedSpendingKey},
|
2021-01-12 11:55:24 -08:00
|
|
|
proto::compact_formats::CompactBlock,
|
2022-01-24 17:02:25 -08:00
|
|
|
wallet::{SpendableNote, WalletTransparentOutput},
|
2021-01-12 11:55:24 -08:00
|
|
|
};
|
|
|
|
|
2021-03-09 17:10:44 -08:00
|
|
|
use super::{
|
2021-03-29 13:35:18 -07:00
|
|
|
error::Error, BlockSource, DecryptedTransaction, PrunedBlock, SentTransaction, WalletRead,
|
2021-03-09 17:10:44 -08:00
|
|
|
WalletWrite,
|
|
|
|
};
|
2021-01-12 11:55:24 -08:00
|
|
|
|
2022-01-31 15:22:20 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
use super::WalletReadTransparent;
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
pub struct MockBlockSource {}
|
2021-01-12 11:55:24 -08:00
|
|
|
|
|
|
|
impl BlockSource for MockBlockSource {
|
2021-01-13 14:20:11 -08:00
|
|
|
type Error = Error<u32>;
|
2021-01-12 11:55:24 -08:00
|
|
|
|
|
|
|
fn with_blocks<F>(
|
|
|
|
&self,
|
|
|
|
_from_height: BlockHeight,
|
|
|
|
_limit: Option<u32>,
|
|
|
|
_with_row: F,
|
|
|
|
) -> Result<(), Self::Error>
|
|
|
|
where
|
2021-01-12 20:10:34 -08:00
|
|
|
F: FnMut(CompactBlock) -> Result<(), Self::Error>,
|
|
|
|
{
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 15:43:04 -07:00
|
|
|
pub struct MockWalletDb {
|
|
|
|
pub network: Network,
|
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
|
2021-03-26 22:17:54 -07:00
|
|
|
impl WalletRead for MockWalletDb {
|
2021-01-13 14:20:11 -08:00
|
|
|
type Error = Error<u32>;
|
2021-01-12 11:55:24 -08:00
|
|
|
type NoteRef = u32;
|
|
|
|
type TxRef = TxId;
|
|
|
|
|
|
|
|
fn block_height_extrema(&self) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
fn get_block_hash(
|
|
|
|
&self,
|
|
|
|
_block_height: BlockHeight,
|
|
|
|
) -> Result<Option<BlockHash>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_tx_height(&self, _txid: TxId) -> Result<Option<BlockHeight>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2022-09-12 11:42:12 -07:00
|
|
|
fn get_current_address(
|
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
) -> Result<Option<UnifiedAddress>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:57:20 -07:00
|
|
|
fn get_unified_full_viewing_keys(
|
2021-01-12 20:10:34 -08:00
|
|
|
&self,
|
2022-06-13 17:57:20 -07:00
|
|
|
) -> Result<HashMap<AccountId, UnifiedFullViewingKey>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(HashMap::new())
|
|
|
|
}
|
|
|
|
|
2022-10-03 13:44:04 -07:00
|
|
|
/// Returns all unified full viewing keys known to this wallet.
|
|
|
|
fn get_account_for_ufvk(
|
|
|
|
&self,
|
|
|
|
_ufvk: &UnifiedFullViewingKey,
|
|
|
|
) -> Result<Option<AccountId>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2021-01-12 17:24:18 -08:00
|
|
|
fn is_valid_account_extfvk(
|
2021-01-12 11:55:24 -08:00
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_extfvk: &ExtendedFullViewingKey,
|
|
|
|
) -> Result<bool, Self::Error> {
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
|
2021-01-15 11:00:14 -08:00
|
|
|
fn get_balance_at(
|
2021-01-12 11:55:24 -08:00
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_anchor_height: BlockHeight,
|
|
|
|
) -> Result<Amount, Self::Error> {
|
|
|
|
Ok(Amount::zero())
|
|
|
|
}
|
|
|
|
|
2020-10-29 09:48:26 -07:00
|
|
|
fn get_memo(&self, _id_note: Self::NoteRef) -> Result<Memo, Self::Error> {
|
|
|
|
Ok(Memo::Empty)
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2021-04-13 10:02:35 -07:00
|
|
|
fn get_transaction(&self, _id_tx: Self::TxRef) -> Result<Transaction, Self::Error> {
|
|
|
|
Err(Error::ScanRequired) // wrong error but we'll fix it later.
|
|
|
|
}
|
|
|
|
|
2021-01-12 11:55:24 -08:00
|
|
|
fn get_commitment_tree(
|
|
|
|
&self,
|
|
|
|
_block_height: BlockHeight,
|
|
|
|
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2021-02-03 11:46:47 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2021-01-12 11:55:24 -08:00
|
|
|
fn get_witnesses(
|
|
|
|
&self,
|
|
|
|
_block_height: BlockHeight,
|
|
|
|
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
|
|
|
|
2021-01-15 09:18:43 -08:00
|
|
|
fn get_nullifiers(&self) -> Result<Vec<(AccountId, Nullifier)>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
|
|
|
|
2021-05-14 14:09:36 -07:00
|
|
|
fn get_all_nullifiers(&self) -> Result<Vec<(AccountId, Nullifier)>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
fn get_spendable_sapling_notes(
|
2021-01-12 20:51:08 -08:00
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
fn select_spendable_sapling_notes(
|
2021-01-12 11:55:24 -08:00
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_target_value: Amount,
|
|
|
|
_anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
2022-01-31 15:22:20 -08:00
|
|
|
}
|
2020-12-22 06:10:13 -08:00
|
|
|
|
2022-01-31 15:22:20 -08:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
impl WalletReadTransparent for MockWalletDb {
|
2022-09-08 11:48:06 -07:00
|
|
|
fn get_transparent_receivers(
|
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
) -> Result<HashSet<TransparentAddress>, Self::Error> {
|
|
|
|
Ok(HashSet::new())
|
|
|
|
}
|
|
|
|
|
2021-09-03 16:32:40 -07:00
|
|
|
fn get_unspent_transparent_outputs(
|
2020-12-22 06:10:13 -08:00
|
|
|
&self,
|
|
|
|
_address: &TransparentAddress,
|
|
|
|
_anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<WalletTransparentOutput>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2021-03-26 22:17:54 -07:00
|
|
|
impl WalletWrite for MockWalletDb {
|
2022-09-13 15:43:04 -07:00
|
|
|
fn create_account(
|
|
|
|
&mut self,
|
2022-09-14 11:20:39 -07:00
|
|
|
seed: &SecretVec<u8>,
|
2022-09-13 15:43:04 -07:00
|
|
|
) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> {
|
|
|
|
let account = AccountId::from(0);
|
|
|
|
UnifiedSpendingKey::from_seed(&self.network, seed.expose_secret(), account)
|
|
|
|
.map(|k| (account, k))
|
|
|
|
.map_err(|_| Error::KeyDerivationError(account))
|
|
|
|
}
|
|
|
|
|
2022-09-12 11:42:12 -07:00
|
|
|
fn get_next_available_address(
|
|
|
|
&mut self,
|
|
|
|
_account: AccountId,
|
|
|
|
) -> Result<Option<UnifiedAddress>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2021-03-09 19:55:44 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2021-03-11 12:01:22 -08:00
|
|
|
fn advance_by_block(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
2021-03-09 17:10:44 -08:00
|
|
|
_block: &PrunedBlock,
|
|
|
|
_updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
|
|
|
|
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
|
|
|
Ok(vec![])
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2021-03-29 13:35:18 -07:00
|
|
|
fn store_decrypted_tx(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
2021-03-29 13:35:18 -07:00
|
|
|
_received_tx: &DecryptedTransaction,
|
2021-01-12 11:55:24 -08:00
|
|
|
) -> Result<Self::TxRef, Self::Error> {
|
2021-05-11 09:54:39 -07:00
|
|
|
Ok(TxId::from_bytes([0u8; 32]))
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2021-03-09 17:10:44 -08:00
|
|
|
fn store_sent_tx(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
2021-03-09 17:10:44 -08:00
|
|
|
_sent_tx: &SentTransaction,
|
2021-01-12 11:55:24 -08:00
|
|
|
) -> Result<Self::TxRef, Self::Error> {
|
2021-05-11 09:54:39 -07:00
|
|
|
Ok(TxId::from_bytes([0u8; 32]))
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2022-08-29 14:32:18 -07:00
|
|
|
#[cfg(feature = "unstable")]
|
2022-08-30 15:54:58 -07:00
|
|
|
fn remove_unmined_tx(&mut self, _txid: &TxId) -> Result<(), Self::Error> {
|
2022-08-29 14:32:18 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-09 17:10:44 -08:00
|
|
|
fn rewind_to_height(&mut self, _block_height: BlockHeight) -> Result<(), Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|