2021-01-12 13:59:42 -08:00
|
|
|
//! Interfaces for wallet data persistence & low-level wallet utilities.
|
|
|
|
|
2020-09-11 15:17:43 -07:00
|
|
|
use std::fmt::Debug;
|
2023-08-16 10:15:10 -07:00
|
|
|
use std::io;
|
2023-06-30 11:37:41 -07:00
|
|
|
use std::num::NonZeroU32;
|
2023-08-16 10:15:10 -07:00
|
|
|
use std::{collections::HashMap, num::TryFromIntError};
|
2020-08-26 14:47:47 -07:00
|
|
|
|
2023-08-16 10:15:10 -07:00
|
|
|
use incrementalmerkletree::{frontier::Frontier, Retention};
|
2022-09-14 12:38:00 -07:00
|
|
|
use secrecy::SecretVec;
|
2023-07-25 13:57:49 -07:00
|
|
|
use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree};
|
2020-07-22 19:44:06 -07:00
|
|
|
use zcash_primitives::{
|
|
|
|
block::BlockHash,
|
2021-01-12 20:10:34 -08:00
|
|
|
consensus::BlockHeight,
|
2022-10-11 08:07:11 -07:00
|
|
|
legacy::TransparentAddress,
|
2020-10-29 09:48:26 -07:00
|
|
|
memo::{Memo, MemoBytes},
|
2023-08-16 10:15:10 -07:00
|
|
|
sapling::{self, Node, NOTE_COMMITMENT_TREE_DEPTH},
|
2022-11-09 07:13:34 -08:00
|
|
|
transaction::{
|
|
|
|
components::{amount::Amount, OutPoint},
|
|
|
|
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-10-25 09:54:12 -07:00
|
|
|
address::{AddressMetadata, UnifiedAddress},
|
2020-08-25 14:02:44 -07:00
|
|
|
decrypt::DecryptedOutput,
|
2022-09-13 15:43:04 -07:00
|
|
|
keys::{UnifiedFullViewingKey, UnifiedSpendingKey},
|
2023-08-16 10:15:10 -07:00
|
|
|
proto::service::TreeState,
|
2023-04-03 12:53:43 -07:00
|
|
|
wallet::{ReceivedSaplingNote, WalletTransparentOutput, WalletTx},
|
2020-08-20 10:41:43 -07:00
|
|
|
};
|
2020-07-22 19:44:06 -07:00
|
|
|
|
2023-06-19 18:05:35 -07:00
|
|
|
use self::chain::CommitmentTreeRoot;
|
2023-07-06 07:37:28 -07:00
|
|
|
use self::scanning::ScanRange;
|
2023-06-19 18:05:35 -07:00
|
|
|
|
2020-07-22 19:44:06 -07:00
|
|
|
pub mod chain;
|
|
|
|
pub mod error;
|
2023-07-06 07:37:28 -07:00
|
|
|
pub mod scanning;
|
2020-08-25 14:02:44 -07:00
|
|
|
pub mod wallet;
|
2020-07-22 19:44:06 -07:00
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
pub const SAPLING_SHARD_HEIGHT: u8 = sapling::NOTE_COMMITMENT_TREE_DEPTH / 2;
|
|
|
|
|
2023-08-02 12:45:49 -07:00
|
|
|
/// An enumeration of constraints that can be applied when querying for nullifiers for notes
|
|
|
|
/// belonging to the wallet.
|
2023-04-03 12:53:43 -07:00
|
|
|
pub enum NullifierQuery {
|
|
|
|
Unspent,
|
|
|
|
All,
|
|
|
|
}
|
|
|
|
|
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.
|
2022-10-17 10:35:14 -07:00
|
|
|
type NoteRef: Copy + Debug + Eq + Ord;
|
2021-01-08 11:49:10 -08:00
|
|
|
|
2023-08-11 15:41:38 -07:00
|
|
|
/// Returns the height of the chain as known to the wallet as of the most recent call to
|
|
|
|
/// [`WalletWrite::update_chain_tip`].
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
2023-08-11 15:41:38 -07:00
|
|
|
/// This will return `Ok(None)` if the height of the current consensus chain tip is unknown.
|
|
|
|
fn chain_height(&self) -> Result<Option<BlockHeight>, Self::Error>;
|
2020-07-22 19:44:06 -07:00
|
|
|
|
2023-07-01 17:16:23 -07:00
|
|
|
/// Returns the available block metadata for the block at the specified height, if any.
|
|
|
|
fn block_metadata(&self, height: BlockHeight) -> Result<Option<BlockMetadata>, Self::Error>;
|
|
|
|
|
|
|
|
/// Returns the metadata for the block at the height to which the wallet has been fully
|
|
|
|
/// scanned.
|
2023-04-03 12:53:43 -07:00
|
|
|
///
|
|
|
|
/// This is the height for which the wallet has fully trial-decrypted this and all preceding
|
|
|
|
/// blocks above the wallet's birthday height. Along with this height, this method returns
|
|
|
|
/// metadata describing the state of the wallet's note commitment trees as of the end of that
|
|
|
|
/// block.
|
2023-07-01 17:16:23 -07:00
|
|
|
fn block_fully_scanned(&self) -> Result<Option<BlockMetadata>, Self::Error>;
|
2023-04-03 12:53:43 -07:00
|
|
|
|
|
|
|
/// Returns a vector of suggested scan ranges based upon the current wallet state.
|
|
|
|
///
|
|
|
|
/// This method should only be used in cases where the [`CompactBlock`] data that will be made
|
|
|
|
/// available to `scan_cached_blocks` for the requested block ranges includes note commitment
|
|
|
|
/// tree size information for each block; or else the scan is likely to fail if notes belonging
|
|
|
|
/// to the wallet are detected.
|
|
|
|
///
|
2023-07-13 10:25:56 -07:00
|
|
|
/// The returned range(s) may include block heights beyond the current chain tip.
|
|
|
|
///
|
2023-04-03 12:53:43 -07:00
|
|
|
/// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock
|
2023-07-06 07:37:28 -07:00
|
|
|
fn suggest_scan_ranges(&self) -> Result<Vec<ScanRange>, Self::Error>;
|
2023-04-03 12:53:43 -07:00
|
|
|
|
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,
|
2023-06-30 11:37:41 -07:00
|
|
|
min_confirmations: NonZeroU32,
|
2023-08-11 15:41:38 -07:00
|
|
|
) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error>;
|
2020-08-26 14:47:47 -07:00
|
|
|
|
2023-04-26 15:27:56 -07:00
|
|
|
/// Returns the minimum block height corresponding to an unspent note in the wallet.
|
|
|
|
fn get_min_unspent_height(&self) -> Result<Option<BlockHeight>, Self::Error>;
|
|
|
|
|
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>;
|
|
|
|
|
2023-08-11 15:41:38 -07:00
|
|
|
/// Returns the block height and hash for the block at the maximum scanned block height.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
2023-08-11 15:41:38 -07:00
|
|
|
/// This will return `Ok(None)` if no blocks have been scanned.
|
|
|
|
fn get_max_height_hash(&self) -> Result<Option<(BlockHeight, BlockHash)>, Self::Error>;
|
2020-10-19 14:16:06 -07:00
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
/// Returns the block height in which the specified transaction was mined, or `Ok(None)` if the
|
|
|
|
/// transaction is not in the main chain.
|
2020-08-25 14:02:44 -07:00
|
|
|
fn get_tx_height(&self, txid: TxId) -> Result<Option<BlockHeight>, Self::Error>;
|
|
|
|
|
2023-08-16 10:15:10 -07:00
|
|
|
/// 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>;
|
|
|
|
|
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
|
|
|
///
|
2023-04-03 12:53:43 -07: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
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
/// Returns the account id corresponding to a given [`UnifiedFullViewingKey`], if any.
|
2022-10-03 13:44:04 -07:00
|
|
|
fn get_account_for_ufvk(
|
|
|
|
&self,
|
|
|
|
ufvk: &UnifiedFullViewingKey,
|
|
|
|
) -> Result<Option<AccountId>, Self::Error>;
|
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
/// Checks whether the specified extended full viewing key is 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>;
|
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
/// Returns the wallet balance for an account as of the specified block height.
|
2021-01-11 17:13:40 -08:00
|
|
|
///
|
2023-04-03 12:53:43 -07: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
|
|
|
///
|
2023-08-02 12:45:49 -07:00
|
|
|
/// Returns `Ok(None)` if the note is known to the wallet but memo data has not yet been
|
|
|
|
/// populated for that note, or if the note identifier does not correspond to a note
|
|
|
|
/// that is known to the wallet.
|
|
|
|
fn get_memo(&self, note_id: NoteId) -> Result<Option<Memo>, Self::Error>;
|
2020-08-06 13:11:25 -07:00
|
|
|
|
2021-04-13 10:02:35 -07:00
|
|
|
/// Returns a transaction.
|
2023-08-02 12:45:49 -07:00
|
|
|
fn get_transaction(&self, txid: TxId) -> Result<Transaction, Self::Error>;
|
2021-04-13 10:02:35 -07:00
|
|
|
|
2023-04-03 12:53:43 -07: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 a
|
|
|
|
/// spending transaction known to the wallet has not yet been included in a block).
|
|
|
|
fn get_sapling_nullifiers(
|
|
|
|
&self,
|
|
|
|
query: NullifierQuery,
|
|
|
|
) -> Result<Vec<(AccountId, sapling::Nullifier)>, Self::Error>;
|
2021-04-14 10:20:56 -07:00
|
|
|
|
2023-08-02 12:45:49 -07:00
|
|
|
/// Return all unspent Sapling notes, excluding the specified note IDs.
|
2021-09-03 16:32:40 -07:00
|
|
|
fn get_spendable_sapling_notes(
|
2021-01-12 19:33:53 -08:00
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
anchor_height: BlockHeight,
|
2022-11-09 07:13:34 -08:00
|
|
|
exclude: &[Self::NoteRef],
|
2023-04-03 12:53:43 -07:00
|
|
|
) -> Result<Vec<ReceivedSaplingNote<Self::NoteRef>>, Self::Error>;
|
2021-01-12 19:33:53 -08:00
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
/// Returns a list of spendable Sapling notes sufficient to cover the specified 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,
|
2022-11-09 07:13:34 -08:00
|
|
|
exclude: &[Self::NoteRef],
|
2023-04-03 12:53:43 -07:00
|
|
|
) -> Result<Vec<ReceivedSaplingNote<Self::NoteRef>>, Self::Error>;
|
2020-12-22 06:10:13 -08:00
|
|
|
|
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,
|
2022-10-25 09:54:12 -07:00
|
|
|
) -> Result<HashMap<TransparentAddress, AddressMetadata>, Self::Error>;
|
2022-09-08 11:48:06 -07:00
|
|
|
|
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,
|
2022-11-09 07:13:34 -08:00
|
|
|
exclude: &[OutPoint],
|
2020-12-22 06:10:13 -08:00
|
|
|
) -> Result<Vec<WalletTransparentOutput>, Self::Error>;
|
2022-10-25 11:04:02 -07:00
|
|
|
|
|
|
|
/// Returns a mapping from transparent receiver to not-yet-shielded UTXO balance,
|
|
|
|
/// for each address associated with a nonzero balance.
|
|
|
|
fn get_transparent_balances(
|
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
max_height: BlockHeight,
|
|
|
|
) -> Result<HashMap<TransparentAddress, Amount>, Self::Error>;
|
2020-07-22 19:44:06 -07:00
|
|
|
}
|
|
|
|
|
2023-07-01 17:16:23 -07:00
|
|
|
/// Metadata describing the sizes of the zcash note commitment trees as of a particular block.
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub struct BlockMetadata {
|
|
|
|
block_height: BlockHeight,
|
|
|
|
block_hash: BlockHash,
|
|
|
|
sapling_tree_size: u32,
|
|
|
|
//TODO: orchard_tree_size: u32
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BlockMetadata {
|
|
|
|
/// Constructs a new [`BlockMetadata`] value from its constituent parts.
|
|
|
|
pub fn from_parts(
|
|
|
|
block_height: BlockHeight,
|
|
|
|
block_hash: BlockHash,
|
|
|
|
sapling_tree_size: u32,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
block_height,
|
|
|
|
block_hash,
|
|
|
|
sapling_tree_size,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the block height.
|
|
|
|
pub fn block_height(&self) -> BlockHeight {
|
|
|
|
self.block_height
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the hash of the block
|
|
|
|
pub fn block_hash(&self) -> BlockHash {
|
|
|
|
self.block_hash
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the size of the Sapling note commitment tree as of the block that this
|
|
|
|
/// [`BlockMetadata`] describes.
|
|
|
|
pub fn sapling_tree_size(&self) -> u32 {
|
|
|
|
self.sapling_tree_size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-09 17:10:44 -08:00
|
|
|
/// The subset of information that is relevant to this wallet that has been
|
2022-10-17 10:35:14 -07:00
|
|
|
/// decrypted and extracted from a [`CompactBlock`].
|
|
|
|
///
|
|
|
|
/// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock
|
2023-07-01 17:16:23 -07:00
|
|
|
pub struct ScannedBlock<Nf> {
|
|
|
|
metadata: BlockMetadata,
|
|
|
|
block_time: u32,
|
|
|
|
transactions: Vec<WalletTx<Nf>>,
|
2023-07-21 11:01:52 -07:00
|
|
|
sapling_nullifier_map: Vec<(TxId, u16, Vec<sapling::Nullifier>)>,
|
2023-07-01 17:16:23 -07:00
|
|
|
sapling_commitments: Vec<(sapling::Node, Retention<BlockHeight>)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<Nf> ScannedBlock<Nf> {
|
|
|
|
pub fn from_parts(
|
|
|
|
metadata: BlockMetadata,
|
|
|
|
block_time: u32,
|
|
|
|
transactions: Vec<WalletTx<Nf>>,
|
2023-07-21 11:01:52 -07:00
|
|
|
sapling_nullifier_map: Vec<(TxId, u16, Vec<sapling::Nullifier>)>,
|
2023-07-01 17:16:23 -07:00
|
|
|
sapling_commitments: Vec<(sapling::Node, Retention<BlockHeight>)>,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
metadata,
|
|
|
|
block_time,
|
|
|
|
transactions,
|
2023-07-21 11:01:52 -07:00
|
|
|
sapling_nullifier_map,
|
2023-07-01 17:16:23 -07:00
|
|
|
sapling_commitments,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn height(&self) -> BlockHeight {
|
|
|
|
self.metadata.block_height
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn block_hash(&self) -> BlockHash {
|
|
|
|
self.metadata.block_hash
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn block_time(&self) -> u32 {
|
|
|
|
self.block_time
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn metadata(&self) -> &BlockMetadata {
|
|
|
|
&self.metadata
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn transactions(&self) -> &[WalletTx<Nf>] {
|
|
|
|
&self.transactions
|
|
|
|
}
|
|
|
|
|
2023-07-21 11:01:52 -07:00
|
|
|
pub fn sapling_nullifier_map(&self) -> &[(TxId, u16, Vec<sapling::Nullifier>)] {
|
|
|
|
&self.sapling_nullifier_map
|
|
|
|
}
|
|
|
|
|
2023-07-01 17:16:23 -07:00
|
|
|
pub fn sapling_commitments(&self) -> &[(sapling::Node, Retention<BlockHeight>)] {
|
|
|
|
&self.sapling_commitments
|
|
|
|
}
|
|
|
|
|
2023-07-03 16:06:43 -07:00
|
|
|
pub fn into_sapling_commitments(self) -> Vec<(sapling::Node, Retention<BlockHeight>)> {
|
2023-07-01 17:16:23 -07:00
|
|
|
self.sapling_commitments
|
|
|
|
}
|
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,
|
2023-04-25 16:35:22 -07:00
|
|
|
pub sapling_outputs: &'a Vec<DecryptedOutput<sapling::Note>>,
|
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,
|
2022-09-26 09:59:50 -07:00
|
|
|
pub outputs: Vec<SentTransactionOutput>,
|
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>,
|
|
|
|
}
|
|
|
|
|
2023-06-26 09:12:59 -07:00
|
|
|
/// A shielded transfer protocol supported by the wallet.
|
2023-08-02 12:45:49 -07:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
2023-06-26 09:12:59 -07:00
|
|
|
pub enum ShieldedProtocol {
|
|
|
|
/// The Sapling protocol
|
|
|
|
Sapling,
|
|
|
|
// TODO: Orchard
|
|
|
|
}
|
|
|
|
|
2023-08-02 12:45:49 -07:00
|
|
|
/// A unique identifier for a shielded transaction output
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct NoteId {
|
|
|
|
txid: TxId,
|
|
|
|
protocol: ShieldedProtocol,
|
2023-08-07 10:25:27 -07:00
|
|
|
output_index: u16,
|
2023-08-02 12:45:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl NoteId {
|
2023-08-07 10:25:27 -07:00
|
|
|
/// Constructs a new `NoteId` from its parts.
|
|
|
|
pub fn new(txid: TxId, protocol: ShieldedProtocol, output_index: u16) -> Self {
|
2023-08-02 12:45:49 -07:00
|
|
|
Self {
|
|
|
|
txid,
|
|
|
|
protocol,
|
|
|
|
output_index,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-07 10:25:27 -07:00
|
|
|
/// Returns the ID of the transaction containing this note.
|
2023-08-02 12:45:49 -07:00
|
|
|
pub fn txid(&self) -> &TxId {
|
|
|
|
&self.txid
|
|
|
|
}
|
|
|
|
|
2023-08-07 10:25:27 -07:00
|
|
|
/// Returns the shielded protocol used by this note.
|
2023-08-02 12:45:49 -07:00
|
|
|
pub fn protocol(&self) -> ShieldedProtocol {
|
|
|
|
self.protocol
|
|
|
|
}
|
|
|
|
|
2023-08-07 10:25:27 -07:00
|
|
|
/// Returns the index of this note within its transaction's corresponding list of
|
|
|
|
/// shielded outputs.
|
|
|
|
pub fn output_index(&self) -> u16 {
|
2023-08-02 12:45:49 -07:00
|
|
|
self.output_index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 11:38:43 -07:00
|
|
|
/// A value pool to which the wallet supports sending transaction outputs.
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
pub enum PoolType {
|
|
|
|
/// The transparent value pool
|
|
|
|
Transparent,
|
2023-06-26 09:12:59 -07:00
|
|
|
/// A shielded value pool.
|
|
|
|
Shielded(ShieldedProtocol),
|
2022-10-10 11:38:43 -07:00
|
|
|
}
|
|
|
|
|
2022-10-11 08:07:11 -07:00
|
|
|
/// A type that represents the recipient of a transaction output; a recipient address (and, for
|
|
|
|
/// unified addresses, the pool to which the payment is sent) in the case of outgoing output, or an
|
|
|
|
/// internal account ID and the pool to which funds were sent in the case of a wallet-internal
|
|
|
|
/// output.
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum Recipient {
|
|
|
|
Transparent(TransparentAddress),
|
2023-04-03 12:53:43 -07:00
|
|
|
Sapling(sapling::PaymentAddress),
|
2022-10-11 08:07:11 -07:00
|
|
|
Unified(UnifiedAddress, PoolType),
|
|
|
|
InternalAccount(AccountId, PoolType),
|
|
|
|
}
|
|
|
|
|
2023-04-24 15:00:43 -07:00
|
|
|
/// A type that represents an output (either Sapling or transparent) that was sent by the wallet.
|
2022-09-26 09:59:50 -07:00
|
|
|
pub struct SentTransactionOutput {
|
2023-04-27 12:07:34 -07:00
|
|
|
output_index: usize,
|
|
|
|
recipient: Recipient,
|
|
|
|
value: Amount,
|
|
|
|
memo: Option<MemoBytes>,
|
2023-04-24 15:00:43 -07:00
|
|
|
sapling_change_to: Option<(AccountId, sapling::Note)>,
|
2023-04-27 12:07:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SentTransactionOutput {
|
|
|
|
pub fn from_parts(
|
|
|
|
output_index: usize,
|
|
|
|
recipient: Recipient,
|
|
|
|
value: Amount,
|
|
|
|
memo: Option<MemoBytes>,
|
2023-04-24 15:00:43 -07:00
|
|
|
sapling_change_to: Option<(AccountId, sapling::Note)>,
|
2023-04-27 12:07:34 -07:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
output_index,
|
|
|
|
recipient,
|
|
|
|
value,
|
|
|
|
memo,
|
2023-04-24 15:00:43 -07:00
|
|
|
sapling_change_to,
|
2023-04-27 12:07:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Returns the index within the transaction that contains the recipient output.
|
2021-03-23 22:54:15 -07:00
|
|
|
///
|
|
|
|
/// - 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.
|
2023-04-27 12:07:34 -07:00
|
|
|
pub fn output_index(&self) -> usize {
|
|
|
|
self.output_index
|
|
|
|
}
|
|
|
|
/// Returns the recipient address of the transaction, or the account id for wallet-internal
|
|
|
|
/// transactions.
|
|
|
|
pub fn recipient(&self) -> &Recipient {
|
|
|
|
&self.recipient
|
|
|
|
}
|
|
|
|
/// Returns the value of the newly created output.
|
|
|
|
pub fn value(&self) -> Amount {
|
|
|
|
self.value
|
|
|
|
}
|
2023-08-04 13:11:24 -07:00
|
|
|
/// Returns the memo that was attached to the output, if any. This will only be `None`
|
|
|
|
/// for transparent outputs.
|
2023-04-27 12:07:34 -07:00
|
|
|
pub fn memo(&self) -> Option<&MemoBytes> {
|
|
|
|
self.memo.as_ref()
|
|
|
|
}
|
2023-04-24 15:00:43 -07:00
|
|
|
|
2023-08-04 13:11:24 -07:00
|
|
|
/// Returns the account to which change (or wallet-internal value in the case of a shielding
|
|
|
|
/// transaction) was sent, along with the change note.
|
2023-04-24 15:00:43 -07:00
|
|
|
pub fn sapling_change_to(&self) -> Option<&(AccountId, sapling::Note)> {
|
|
|
|
self.sapling_change_to.as_ref()
|
|
|
|
}
|
2021-03-09 17:10:44 -08:00
|
|
|
}
|
2021-03-09 12:15:27 -08:00
|
|
|
|
2023-08-29 15:32:18 -07:00
|
|
|
/// A data structure used to set the birthday height for an account, and ensure that the initial
|
|
|
|
/// note commitment tree state is recorded at that height.
|
2023-08-16 10:15:10 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct AccountBirthday {
|
|
|
|
height: BlockHeight,
|
|
|
|
sapling_frontier: Frontier<Node, NOTE_COMMITMENT_TREE_DEPTH>,
|
2023-08-16 10:15:10 -07:00
|
|
|
recover_until: Option<BlockHeight>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Errors that can occur in the construction of an [`AccountBirthday`] from a [`TreeState`]
|
|
|
|
pub enum BirthdayError {
|
|
|
|
HeightInvalid(TryFromIntError),
|
|
|
|
Decode(io::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<TryFromIntError> for BirthdayError {
|
|
|
|
fn from(value: TryFromIntError) -> Self {
|
|
|
|
Self::HeightInvalid(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Error> for BirthdayError {
|
|
|
|
fn from(value: io::Error) -> Self {
|
|
|
|
Self::Decode(value)
|
|
|
|
}
|
2023-08-16 10:15:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountBirthday {
|
2023-08-29 15:32:18 -07:00
|
|
|
/// Constructs a new [`AccountBirthday`] from its constituent parts.
|
|
|
|
///
|
2023-08-16 10:15:10 -07:00
|
|
|
/// * `height`: The birthday height of the account. This is defined as the height of the first
|
|
|
|
/// block block to be scanned in wallet recovery.
|
2023-08-29 15:32:18 -07:00
|
|
|
/// * `sapling_frontier`: The Sapling note commitment tree frontier as of the end of the block
|
2023-08-16 10:15:10 -07:00
|
|
|
/// prior to the birthday height.
|
|
|
|
/// * `recover_until`: An optional height at which the wallet should exit "recovery mode". In
|
|
|
|
/// order to avoid confusing shifts in wallet balance and spendability that may temporarily be
|
|
|
|
/// visible to a user during the process of recovering from seed, wallets may optionally set a
|
|
|
|
/// "recover until" height. The wallet is considered to be in "recovery mode" until there
|
|
|
|
/// exist no unscanned ranges between the wallet's birthday height and the provided
|
|
|
|
/// `recover_until` height, exclusive.
|
|
|
|
///
|
|
|
|
/// This API is intended primarily to be used in testing contexts; under normal circumstances,
|
|
|
|
/// [`AccountBirthday::from_treestate`] should be used instead.
|
|
|
|
#[cfg(feature = "test-dependencies")]
|
2023-08-16 10:15:10 -07:00
|
|
|
pub fn from_parts(
|
|
|
|
height: BlockHeight,
|
|
|
|
sapling_frontier: Frontier<Node, NOTE_COMMITMENT_TREE_DEPTH>,
|
2023-08-16 10:15:10 -07:00
|
|
|
recover_until: Option<BlockHeight>,
|
2023-08-16 10:15:10 -07:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
height,
|
|
|
|
sapling_frontier,
|
2023-08-16 10:15:10 -07:00
|
|
|
recover_until,
|
2023-08-16 10:15:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-16 10:15:10 -07:00
|
|
|
/// Constructs a new [`AccountBirthday`] from a [`TreeState`] returned from `lightwalletd`.
|
|
|
|
///
|
|
|
|
/// * `treestate`: The tree state corresponding to the last block prior to the wallet's
|
|
|
|
/// birthday height.
|
|
|
|
/// * `recover_until`: An optional height at which the wallet should exit "recovery mode". In
|
|
|
|
/// order to avoid confusing shifts in wallet balance and spendability that may temporarily be
|
|
|
|
/// visible to a user during the process of recovering from seed, wallets may optionally set a
|
|
|
|
/// "recover until" height. The wallet is considered to be in "recovery mode" until there
|
|
|
|
/// exist no unscanned ranges between the wallet's birthday height and the provided
|
|
|
|
/// `recover_until` height, exclusive.
|
|
|
|
pub fn from_treestate(
|
|
|
|
treestate: TreeState,
|
|
|
|
recover_until: Option<BlockHeight>,
|
|
|
|
) -> Result<Self, BirthdayError> {
|
|
|
|
Ok(Self {
|
|
|
|
height: BlockHeight::try_from(treestate.height + 1)?,
|
|
|
|
sapling_frontier: treestate.sapling_tree()?.to_frontier(),
|
|
|
|
recover_until,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-29 15:32:18 -07:00
|
|
|
/// Returns the Sapling note commitment tree frontier as of the end of the block at
|
|
|
|
/// [`Self::height`].
|
2023-08-16 10:15:10 -07:00
|
|
|
pub fn sapling_frontier(&self) -> &Frontier<Node, NOTE_COMMITMENT_TREE_DEPTH> {
|
|
|
|
&self.sapling_frontier
|
|
|
|
}
|
|
|
|
|
2023-08-29 15:32:18 -07:00
|
|
|
/// Returns the birthday height of the account.
|
2023-08-16 10:15:10 -07:00
|
|
|
pub fn height(&self) -> BlockHeight {
|
|
|
|
self.height
|
|
|
|
}
|
2023-08-16 10:15:10 -07:00
|
|
|
|
|
|
|
/// Returns the height at which the wallet should exit "recovery mode".
|
|
|
|
pub fn recover_until(&self) -> Option<BlockHeight> {
|
|
|
|
self.recover_until
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "test-dependencies")]
|
|
|
|
pub fn from_sapling_activation<P: zcash_primitives::consensus::Parameters>(
|
|
|
|
params: &P,
|
|
|
|
) -> AccountBirthday {
|
|
|
|
use zcash_primitives::consensus::NetworkUpgrade;
|
|
|
|
|
|
|
|
AccountBirthday::from_parts(
|
|
|
|
params.activation_height(NetworkUpgrade::Sapling).unwrap(),
|
|
|
|
Frontier::empty(),
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
}
|
2023-08-16 10:15:10 -07: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-10-13 19:58:43 -07:00
|
|
|
/// The type of identifiers used to look up transparent UTXOs.
|
|
|
|
type UtxoRef;
|
|
|
|
|
2023-08-16 10:15:10 -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 a birthday height is having a height is below the current chain tip, this operation will
|
|
|
|
/// trigger a re-scan of the blocks at and above the provided height. The birthday height is
|
|
|
|
/// defined as the minimum block height that will be scanned for funds belonging to the wallet.
|
2022-09-14 11:20:39 -07:00
|
|
|
///
|
2023-08-16 10:15:10 -07:00
|
|
|
/// For new wallets, callers should construct the [`AccountBirthday`] using
|
|
|
|
/// [`AccountBirthday::from_treestate`] for the block at height `chain_tip_height - PRUNING_DEPTH`.
|
|
|
|
/// Setting the birthday height to a tree state below the pruning depth ensures that reorgs
|
|
|
|
/// cannot cause funds intended for the wallet to be missed; otherwise, if the chain tip height
|
|
|
|
/// were used for the wallet birthday, a transaction targeted at a height greater than the
|
|
|
|
/// chain tip could be mined at a height below that tip as part of a reorg.
|
2022-09-14 11:20:39 -07:00
|
|
|
///
|
2023-08-16 10:15:10 -07:00
|
|
|
/// 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.
|
2022-09-14 11:20:39 -07:00
|
|
|
///
|
2023-08-16 10:15:10 -07:00
|
|
|
/// 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).
|
2022-09-14 11:20:39 -07:00
|
|
|
///
|
|
|
|
/// [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>,
|
2023-08-16 10:15:10 -07:00
|
|
|
birthday: AccountBirthday,
|
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>;
|
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
/// Updates the state of the wallet database by persisting the provided block information,
|
|
|
|
/// along with the note commitments that were detected when scanning the block for transactions
|
|
|
|
/// pertaining to this wallet.
|
2023-07-06 07:18:57 -07:00
|
|
|
///
|
|
|
|
/// `blocks` must be sequential, in order of increasing block height
|
2023-07-05 22:08:00 -07:00
|
|
|
fn put_blocks(
|
2020-08-20 10:41:43 -07:00
|
|
|
&mut self,
|
2023-07-21 11:01:52 -07:00
|
|
|
blocks: Vec<ScannedBlock<sapling::Nullifier>>,
|
2023-08-02 12:45:49 -07:00
|
|
|
) -> Result<(), Self::Error>;
|
2021-03-09 17:10:44 -08:00
|
|
|
|
2023-07-06 07:37:28 -07:00
|
|
|
/// Updates the wallet's view of the blockchain.
|
|
|
|
///
|
|
|
|
/// This method is used to provide the wallet with information about the state of the
|
2023-07-13 10:25:56 -07:00
|
|
|
/// blockchain, and detect any previously scanned data that needs to be re-validated
|
|
|
|
/// before proceeding with scanning. It should be called at wallet startup prior to calling
|
2023-07-06 07:37:28 -07:00
|
|
|
/// [`WalletRead::suggest_scan_ranges`] in order to provide the wallet with the information it
|
|
|
|
/// needs to correctly prioritize scanning operations.
|
|
|
|
fn update_chain_tip(&mut self, tip_height: BlockHeight) -> Result<(), Self::Error>;
|
|
|
|
|
2022-02-01 10:05:51 -08:00
|
|
|
/// Caches a decrypted transaction in the persistent wallet store.
|
2023-08-02 12:45:49 -07:00
|
|
|
fn store_decrypted_tx(&mut self, received_tx: DecryptedTransaction) -> Result<(), Self::Error>;
|
2021-03-09 17:10:44 -08:00
|
|
|
|
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.
|
2023-08-02 12:45:49 -07:00
|
|
|
fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result<(), Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
|
2023-04-26 15:27:56 -07:00
|
|
|
/// Truncates the wallet database to the specified height.
|
2021-01-15 10:20:29 -08:00
|
|
|
///
|
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
|
|
|
///
|
2023-04-26 15:27:56 -07:00
|
|
|
/// There may be restrictions on heights to which it is possible to truncate.
|
|
|
|
fn truncate_to_height(&mut self, block_height: BlockHeight) -> Result<(), Self::Error>;
|
2022-01-31 15:22:20 -08:00
|
|
|
|
2022-10-13 19:58:43 -07:00
|
|
|
/// Adds a transparent UTXO received by the wallet to the data store.
|
2022-01-31 15:22:20 -08:00
|
|
|
fn put_received_transparent_utxo(
|
|
|
|
&mut self,
|
|
|
|
output: &WalletTransparentOutput,
|
|
|
|
) -> Result<Self::UtxoRef, Self::Error>;
|
|
|
|
}
|
|
|
|
|
2023-06-29 15:26:22 -07:00
|
|
|
/// This trait describes a capability for manipulating wallet note commitment trees.
|
|
|
|
///
|
|
|
|
/// At present, this only serves the Sapling protocol, but it will be modified to
|
|
|
|
/// also provide operations related to Orchard note commitment trees in the future.
|
2023-04-03 12:53:43 -07:00
|
|
|
pub trait WalletCommitmentTrees {
|
|
|
|
type Error;
|
|
|
|
type SaplingShardStore<'a>: ShardStore<
|
|
|
|
H = sapling::Node,
|
|
|
|
CheckpointId = BlockHeight,
|
|
|
|
Error = Self::Error,
|
|
|
|
>;
|
|
|
|
|
|
|
|
fn with_sapling_tree_mut<F, A, E>(&mut self, callback: F) -> Result<A, E>
|
|
|
|
where
|
|
|
|
for<'a> F: FnMut(
|
|
|
|
&'a mut ShardTree<
|
|
|
|
Self::SaplingShardStore<'a>,
|
|
|
|
{ sapling::NOTE_COMMITMENT_TREE_DEPTH },
|
|
|
|
SAPLING_SHARD_HEIGHT,
|
|
|
|
>,
|
|
|
|
) -> Result<A, E>,
|
|
|
|
E: From<ShardTreeError<Self::Error>>;
|
2023-06-19 18:05:35 -07:00
|
|
|
|
|
|
|
/// Adds a sequence of note commitment tree subtree roots to the data store.
|
|
|
|
fn put_sapling_subtree_roots(
|
|
|
|
&mut self,
|
|
|
|
start_index: u64,
|
|
|
|
roots: &[CommitmentTreeRoot<sapling::Node>],
|
|
|
|
) -> Result<(), ShardTreeError<Self::Error>>;
|
2023-04-03 12:53:43 -07:00
|
|
|
}
|
|
|
|
|
2021-01-12 11:55:24 -08:00
|
|
|
#[cfg(feature = "test-dependencies")]
|
|
|
|
pub mod testing {
|
2023-06-19 18:05:35 -07:00
|
|
|
use incrementalmerkletree::Address;
|
2022-09-13 15:43:04 -07:00
|
|
|
use secrecy::{ExposeSecret, SecretVec};
|
2023-07-25 13:57:49 -07:00
|
|
|
use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree};
|
2023-08-11 15:41:38 -07:00
|
|
|
use std::{collections::HashMap, convert::Infallible, num::NonZeroU32};
|
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,
|
2023-04-03 12:53:43 -07:00
|
|
|
sapling,
|
2022-11-09 07:13:34 -08:00
|
|
|
transaction::{
|
|
|
|
components::{Amount, OutPoint},
|
|
|
|
Transaction, TxId,
|
|
|
|
},
|
2022-01-24 17:02:25 -08:00
|
|
|
zip32::{AccountId, ExtendedFullViewingKey},
|
2021-01-12 11:55:24 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
2022-10-25 09:54:12 -07:00
|
|
|
address::{AddressMetadata, UnifiedAddress},
|
2022-09-13 15:43:04 -07:00
|
|
|
keys::{UnifiedFullViewingKey, UnifiedSpendingKey},
|
2023-04-03 12:53:43 -07:00
|
|
|
wallet::{ReceivedSaplingNote, WalletTransparentOutput},
|
2021-01-12 11:55:24 -08:00
|
|
|
};
|
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
use super::{
|
2023-08-16 10:15:10 -07:00
|
|
|
chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata,
|
|
|
|
DecryptedTransaction, NoteId, NullifierQuery, ScannedBlock, SentTransaction,
|
|
|
|
WalletCommitmentTrees, WalletRead, WalletWrite, SAPLING_SHARD_HEIGHT,
|
2023-04-03 12:53:43 -07:00
|
|
|
};
|
2021-01-12 11:55:24 -08:00
|
|
|
|
2022-09-13 15:43:04 -07:00
|
|
|
pub struct MockWalletDb {
|
|
|
|
pub network: Network,
|
2023-04-03 12:53:43 -07:00
|
|
|
pub sapling_tree: ShardTree<
|
|
|
|
MemoryShardStore<sapling::Node, BlockHeight>,
|
|
|
|
{ SAPLING_SHARD_HEIGHT * 2 },
|
|
|
|
SAPLING_SHARD_HEIGHT,
|
|
|
|
>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MockWalletDb {
|
|
|
|
pub fn new(network: Network) -> Self {
|
|
|
|
Self {
|
|
|
|
network,
|
|
|
|
sapling_tree: ShardTree::new(MemoryShardStore::empty(), 100),
|
|
|
|
}
|
|
|
|
}
|
2022-09-13 15:43:04 -07:00
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
|
2021-03-26 22:17:54 -07:00
|
|
|
impl WalletRead for MockWalletDb {
|
2022-10-17 10:35:14 -07:00
|
|
|
type Error = ();
|
2021-01-12 11:55:24 -08:00
|
|
|
type NoteRef = u32;
|
|
|
|
|
2023-08-11 15:41:38 -07:00
|
|
|
fn chain_height(&self) -> Result<Option<BlockHeight>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_target_and_anchor_heights(
|
|
|
|
&self,
|
|
|
|
_min_confirmations: NonZeroU32,
|
|
|
|
) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2023-07-01 17:16:23 -07:00
|
|
|
fn block_metadata(
|
2023-04-03 12:53:43 -07:00
|
|
|
&self,
|
2023-07-01 17:16:23 -07:00
|
|
|
_height: BlockHeight,
|
|
|
|
) -> Result<Option<BlockMetadata>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_fully_scanned(&self) -> Result<Option<BlockMetadata>, Self::Error> {
|
2023-04-03 12:53:43 -07:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2023-07-06 07:37:28 -07:00
|
|
|
fn suggest_scan_ranges(&self) -> Result<Vec<ScanRange>, Self::Error> {
|
2023-04-03 12:53:43 -07:00
|
|
|
Ok(vec![])
|
|
|
|
}
|
|
|
|
|
2023-04-26 15:27:56 -07:00
|
|
|
fn get_min_unspent_height(&self) -> Result<Option<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)
|
|
|
|
}
|
|
|
|
|
2023-08-11 15:41:38 -07:00
|
|
|
fn get_max_height_hash(&self) -> Result<Option<(BlockHeight, BlockHash)>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2021-01-12 11:55:24 -08:00
|
|
|
fn get_tx_height(&self, _txid: TxId) -> Result<Option<BlockHeight>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2023-08-16 10:15:10 -07:00
|
|
|
fn get_wallet_birthday(&self) -> Result<Option<BlockHeight>, Self::Error> {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_account_birthday(&self, _account: AccountId) -> Result<BlockHeight, Self::Error> {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
|
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
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
2023-08-02 12:45:49 -07:00
|
|
|
fn get_memo(&self, _id_note: NoteId) -> Result<Option<Memo>, Self::Error> {
|
2023-05-16 09:27:40 -07:00
|
|
|
Ok(None)
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2023-08-02 12:45:49 -07:00
|
|
|
fn get_transaction(&self, _txid: TxId) -> Result<Transaction, Self::Error> {
|
2022-10-17 10:35:14 -07:00
|
|
|
Err(())
|
2021-04-13 10:02:35 -07:00
|
|
|
}
|
|
|
|
|
2023-04-03 12:53:43 -07:00
|
|
|
fn get_sapling_nullifiers(
|
|
|
|
&self,
|
|
|
|
_query: NullifierQuery,
|
|
|
|
) -> Result<Vec<(AccountId, sapling::Nullifier)>, Self::Error> {
|
2021-05-14 14:09:36 -07:00
|
|
|
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,
|
2022-11-09 07:13:34 -08:00
|
|
|
_exclude: &[Self::NoteRef],
|
2023-04-03 12:53:43 -07:00
|
|
|
) -> Result<Vec<ReceivedSaplingNote<Self::NoteRef>>, Self::Error> {
|
2021-01-12 20:51:08 -08:00
|
|
|
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,
|
2022-11-09 07:13:34 -08:00
|
|
|
_exclude: &[Self::NoteRef],
|
2023-04-03 12:53:43 -07:00
|
|
|
) -> Result<Vec<ReceivedSaplingNote<Self::NoteRef>>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
2020-12-22 06:10:13 -08:00
|
|
|
|
2022-09-08 11:48:06 -07:00
|
|
|
fn get_transparent_receivers(
|
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
2022-10-25 09:54:12 -07:00
|
|
|
) -> Result<HashMap<TransparentAddress, AddressMetadata>, Self::Error> {
|
|
|
|
Ok(HashMap::new())
|
2022-09-08 11:48:06 -07:00
|
|
|
}
|
|
|
|
|
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,
|
2022-11-09 07:13:34 -08:00
|
|
|
_exclude: &[OutPoint],
|
2020-12-22 06:10:13 -08:00
|
|
|
) -> Result<Vec<WalletTransparentOutput>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
2022-10-25 11:04:02 -07:00
|
|
|
|
|
|
|
fn get_transparent_balances(
|
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_max_height: BlockHeight,
|
|
|
|
) -> Result<HashMap<TransparentAddress, Amount>, Self::Error> {
|
|
|
|
Ok(HashMap::new())
|
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2021-03-26 22:17:54 -07:00
|
|
|
impl WalletWrite for MockWalletDb {
|
2022-10-13 19:58:43 -07:00
|
|
|
type UtxoRef = u32;
|
|
|
|
|
2022-09-13 15:43:04 -07:00
|
|
|
fn create_account(
|
|
|
|
&mut self,
|
2022-09-14 11:20:39 -07:00
|
|
|
seed: &SecretVec<u8>,
|
2023-08-16 10:15:10 -07:00
|
|
|
_birthday: AccountBirthday,
|
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))
|
2022-10-17 10:35:14 -07:00
|
|
|
.map_err(|_| ())
|
2022-09-13 15:43:04 -07:00
|
|
|
}
|
|
|
|
|
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)]
|
2023-07-05 22:08:00 -07:00
|
|
|
fn put_blocks(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
2023-07-05 22:08:00 -07:00
|
|
|
_blocks: Vec<ScannedBlock<sapling::Nullifier>>,
|
2023-08-02 12:45:49 -07:00
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2023-07-06 07:37:28 -07:00
|
|
|
fn update_chain_tip(&mut self, _tip_height: BlockHeight) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-29 13:35:18 -07:00
|
|
|
fn store_decrypted_tx(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
2023-04-03 12:53:43 -07:00
|
|
|
_received_tx: DecryptedTransaction,
|
2023-08-02 12:45:49 -07:00
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2023-08-02 12:45:49 -07:00
|
|
|
fn store_sent_tx(&mut self, _sent_tx: &SentTransaction) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
|
|
|
|
2023-04-26 15:27:56 -07:00
|
|
|
fn truncate_to_height(&mut self, _block_height: BlockHeight) -> Result<(), Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-10-13 19:58:43 -07:00
|
|
|
|
|
|
|
/// Adds a transparent UTXO received by the wallet to the data store.
|
|
|
|
fn put_received_transparent_utxo(
|
|
|
|
&mut self,
|
|
|
|
_output: &WalletTransparentOutput,
|
|
|
|
) -> Result<Self::UtxoRef, Self::Error> {
|
|
|
|
Ok(0)
|
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|
2023-04-03 12:53:43 -07:00
|
|
|
|
|
|
|
impl WalletCommitmentTrees for MockWalletDb {
|
|
|
|
type Error = Infallible;
|
|
|
|
type SaplingShardStore<'a> = MemoryShardStore<sapling::Node, BlockHeight>;
|
|
|
|
|
|
|
|
fn with_sapling_tree_mut<F, A, E>(&mut self, mut callback: F) -> Result<A, E>
|
|
|
|
where
|
|
|
|
for<'a> F: FnMut(
|
|
|
|
&'a mut ShardTree<
|
|
|
|
Self::SaplingShardStore<'a>,
|
|
|
|
{ sapling::NOTE_COMMITMENT_TREE_DEPTH },
|
|
|
|
SAPLING_SHARD_HEIGHT,
|
|
|
|
>,
|
|
|
|
) -> Result<A, E>,
|
|
|
|
E: From<ShardTreeError<Infallible>>,
|
|
|
|
{
|
|
|
|
callback(&mut self.sapling_tree)
|
|
|
|
}
|
2023-06-19 18:05:35 -07:00
|
|
|
|
|
|
|
fn put_sapling_subtree_roots(
|
|
|
|
&mut self,
|
|
|
|
start_index: u64,
|
|
|
|
roots: &[CommitmentTreeRoot<sapling::Node>],
|
|
|
|
) -> Result<(), ShardTreeError<Self::Error>> {
|
|
|
|
self.with_sapling_tree_mut(|t| {
|
|
|
|
for (root, i) in roots.iter().zip(0u64..) {
|
|
|
|
let root_addr =
|
|
|
|
Address::from_parts(SAPLING_SHARD_HEIGHT.into(), start_index + i);
|
|
|
|
t.insert(root_addr, *root.root_hash())?;
|
|
|
|
}
|
|
|
|
Ok::<_, ShardTreeError<Self::Error>>(())
|
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-04-03 12:53:43 -07:00
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
}
|