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
|
|
|
|
2020-07-22 19:44:06 -07:00
|
|
|
use zcash_primitives::{
|
|
|
|
block::BlockHash,
|
2021-01-12 20:10:34 -08:00
|
|
|
consensus::BlockHeight,
|
2020-08-18 12:48:59 -07:00
|
|
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
2020-08-25 14:02:44 -07:00
|
|
|
note_encryption::Memo,
|
2021-01-11 17:13:40 -08:00
|
|
|
primitives::{Note, Nullifier, PaymentAddress},
|
2020-08-06 17:13:24 -07:00
|
|
|
sapling::Node,
|
2020-08-25 14:02:44 -07:00
|
|
|
transaction::{components::Amount, Transaction, TxId},
|
2020-08-04 19:49:41 -07:00
|
|
|
zip32::ExtendedFullViewingKey,
|
2020-07-22 19:44:06 -07:00
|
|
|
};
|
|
|
|
|
2020-08-20 10:41:43 -07:00
|
|
|
use crate::{
|
2020-08-25 17:31:21 -07:00
|
|
|
address::RecipientAddress,
|
2020-08-26 14:47:47 -07:00
|
|
|
data_api::wallet::ANCHOR_OFFSET,
|
2020-08-25 14:02:44 -07:00
|
|
|
decrypt::DecryptedOutput,
|
2020-08-20 10:41:43 -07:00
|
|
|
proto::compact_formats::CompactBlock,
|
2020-08-26 14:47:47 -07:00
|
|
|
wallet::{AccountId, SpendableNote, WalletShieldedOutput, WalletTx},
|
2020-08-20 10:41:43 -07: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
|
|
|
///
|
|
|
|
/// This trait defines the read-only portion of the storage
|
|
|
|
/// interface atop which higher-level wallet operations are
|
2021-01-11 17:13:40 -08:00
|
|
|
/// implemented. It serves to allow wallet functions to be
|
2021-01-08 11:49:10 -08:00
|
|
|
/// 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,
|
|
|
|
) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error> {
|
|
|
|
self.block_height_extrema().map(|heights| {
|
|
|
|
heights.map(|(min_height, max_height)| {
|
|
|
|
let target_height = max_height + 1;
|
|
|
|
|
|
|
|
// Select an anchor ANCHOR_OFFSET back from the target block,
|
|
|
|
// unless that would be before the earliest block we have.
|
|
|
|
let anchor_height = BlockHeight::from(cmp::max(
|
|
|
|
u32::from(target_height).saturating_sub(ANCHOR_OFFSET),
|
|
|
|
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>;
|
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the payment 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.
|
2021-01-12 20:10:34 -08:00
|
|
|
fn get_address(&self, account: AccountId) -> Result<Option<PaymentAddress>, Self::Error>;
|
2020-08-05 16:01:22 -07:00
|
|
|
|
2021-01-15 10:20:29 -08:00
|
|
|
/// Returns all extended full viewing keys known about by this wallet.
|
2021-01-12 17:24:18 -08:00
|
|
|
fn get_extended_full_viewing_keys(
|
2020-08-25 14:02:44 -07:00
|
|
|
&self,
|
2021-01-11 17:13:40 -08:00
|
|
|
) -> Result<HashMap<AccountId, ExtendedFullViewingKey>, Self::Error>;
|
2020-08-25 14:02:44 -07:00
|
|
|
|
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
|
|
|
|
2021-02-02 12:16:16 -08:00
|
|
|
/// Returns the memo for a note, if it is known and a valid UTF-8 string.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// This will return `Ok(None)` if the note identifier does not appear in the
|
|
|
|
/// database as a known note ID.
|
2021-02-03 11:46:47 -08:00
|
|
|
fn get_memo_as_utf8(&self, id_note: Self::NoteRef) -> Result<Option<String>, Self::Error>;
|
2020-08-06 13:11:25 -07:00
|
|
|
|
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
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns the unspent nullifiers, along with the account identifiers
|
|
|
|
/// with which they are associated.
|
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
|
|
|
|
2021-01-12 19:33:53 -08:00
|
|
|
/// Return all spendable notes.
|
|
|
|
fn get_spendable_notes(
|
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error>;
|
|
|
|
|
2021-01-08 11:49:10 -08:00
|
|
|
/// Returns a list of spendable notes sufficient to cover the specified
|
2021-01-12 20:10:34 -08:00
|
|
|
/// target value, if possible.
|
2020-08-26 14:47:47 -07:00
|
|
|
fn select_spendable_notes(
|
|
|
|
&self,
|
|
|
|
account: AccountId,
|
|
|
|
target_value: Amount,
|
|
|
|
anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error>;
|
2020-07-22 19:44:06 -07:00
|
|
|
}
|
|
|
|
|
2021-03-09 12:15:27 -08:00
|
|
|
// /// The subset of information that is relevant to this wallet that has been
|
|
|
|
// /// decrypted and extracted from a [CompactBlock].
|
|
|
|
// pub struct PrunedBlock {
|
|
|
|
// block_height: BlockHeight,
|
|
|
|
// block_hash: BlockHash,
|
|
|
|
// block_time: u32,
|
|
|
|
// commitment_tree: &CommitmentTree<Node>,
|
|
|
|
// transactions: &Vec<ChainTx>,
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// pub struct BlockInserted {
|
|
|
|
// witnesses: Vec<(Self::NoteRef, IncrementalWitness<Node>)>
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// pub trait WalletWrite2: WalletRead {
|
|
|
|
// fn insert_pruned_block(
|
|
|
|
// &mut self,
|
|
|
|
// block: &PrunedBlock
|
|
|
|
// ) -> Result<BlockInserted, Self::Error>;
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
|
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 {
|
2021-01-08 17:08:02 -08:00
|
|
|
/// Perform one or more write operations of this trait transactionally.
|
|
|
|
/// Implementations of this method must ensure that all mutations to the
|
2021-01-11 17:13:40 -08:00
|
|
|
/// state of the data store made by the provided closure must be performed
|
|
|
|
/// atomically and modifications to state must be automatically rolled back
|
2021-01-08 17:08:02 -08:00
|
|
|
/// if the provided closure returns an error.
|
2020-08-26 16:52:21 -07:00
|
|
|
fn transactionally<F, A>(&mut self, f: F) -> Result<A, Self::Error>
|
|
|
|
where
|
|
|
|
F: FnOnce(&mut Self) -> Result<A, Self::Error>;
|
|
|
|
|
2021-01-08 17:08:02 -08:00
|
|
|
/// Add the data for a block to the data store.
|
2020-08-20 10:41:43 -07:00
|
|
|
fn insert_block(
|
|
|
|
&mut self,
|
|
|
|
block_height: BlockHeight,
|
|
|
|
block_hash: BlockHash,
|
|
|
|
block_time: u32,
|
|
|
|
commitment_tree: &CommitmentTree<Node>,
|
|
|
|
) -> Result<(), Self::Error>;
|
|
|
|
|
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-26 16:52:21 -07:00
|
|
|
|
2021-01-08 17:08:02 -08:00
|
|
|
/// Add wallet-relevant metadata for a specific transaction to the data
|
|
|
|
/// store.
|
2020-08-25 14:02:44 -07:00
|
|
|
fn put_tx_meta(
|
|
|
|
&mut self,
|
|
|
|
tx: &WalletTx,
|
|
|
|
height: BlockHeight,
|
|
|
|
) -> Result<Self::TxRef, Self::Error>;
|
|
|
|
|
2021-01-08 20:49:38 -08:00
|
|
|
/// Add a full transaction contents to the data store.
|
2020-08-26 14:47:47 -07:00
|
|
|
fn put_tx_data(
|
|
|
|
&mut self,
|
|
|
|
tx: &Transaction,
|
|
|
|
created_at: Option<time::OffsetDateTime>,
|
|
|
|
) -> Result<Self::TxRef, Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
|
2021-01-11 17:13:40 -08:00
|
|
|
/// Mark the specified transaction as spent and record the nullifier.
|
2021-01-08 20:49:38 -08:00
|
|
|
fn mark_spent(&mut self, tx_ref: Self::TxRef, nf: &Nullifier) -> Result<(), Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
/// Record a note as having been received, along with its nullifier and the transaction
|
2021-01-11 17:13:40 -08:00
|
|
|
/// within which the note was created.
|
2021-01-13 14:20:11 -08:00
|
|
|
///
|
2021-01-19 11:41:12 -08:00
|
|
|
/// Implementations of this method must be exclusively additive with respect to stored
|
2021-01-13 14:20:11 -08:00
|
|
|
/// data; passing `None` for the nullifier should not be interpreted as deleting nullifier
|
|
|
|
/// information from the underlying store.
|
2021-01-19 11:41:12 -08:00
|
|
|
///
|
|
|
|
/// Implementations of this method must ensure that attempting to record the same note
|
|
|
|
/// with a different nullifier to that already stored will return an error.
|
2020-08-25 14:02:44 -07:00
|
|
|
fn put_received_note<T: ShieldedOutput>(
|
2020-08-20 10:41:43 -07:00
|
|
|
&mut self,
|
2020-08-25 14:02:44 -07:00
|
|
|
output: &T,
|
2020-08-20 10:41:43 -07:00
|
|
|
tx_ref: Self::TxRef,
|
|
|
|
) -> Result<Self::NoteRef, Self::Error>;
|
|
|
|
|
2021-01-12 12:55:22 -08:00
|
|
|
/// Add the incremental witness for the specified note to the database.
|
2020-08-20 10:41:43 -07:00
|
|
|
fn insert_witness(
|
|
|
|
&mut self,
|
|
|
|
note_id: Self::NoteRef,
|
|
|
|
witness: &IncrementalWitness<Node>,
|
|
|
|
height: BlockHeight,
|
|
|
|
) -> Result<(), Self::Error>;
|
|
|
|
|
2021-01-19 11:41:12 -08:00
|
|
|
/// Remove all incremental witness data before the specified block height.
|
2021-01-12 12:55:22 -08:00
|
|
|
// TODO: this is a backend-specific optimization that probably shouldn't be part of
|
|
|
|
// the public API
|
2020-08-20 10:41:43 -07:00
|
|
|
fn prune_witnesses(&mut self, from_height: BlockHeight) -> Result<(), Self::Error>;
|
|
|
|
|
2021-01-12 12:55:22 -08:00
|
|
|
/// Remove the spent marker from any received notes that had been spent in a
|
|
|
|
/// transaction constructed by the wallet, but which transaction had not been mined
|
|
|
|
/// by the specified block height.
|
|
|
|
// TODO: this is a backend-specific optimization that probably shouldn't be part of
|
|
|
|
// the public API
|
2020-08-20 10:41:43 -07:00
|
|
|
fn update_expired_notes(&mut self, from_height: BlockHeight) -> Result<(), Self::Error>;
|
2020-08-25 14:02:44 -07:00
|
|
|
|
2021-01-12 12:55:22 -08:00
|
|
|
/// Add the decrypted contents of a sent note to the database if it does not exist;
|
|
|
|
/// otherwise, update the note. This is useful in the case of a wallet restore where
|
|
|
|
/// the send of the note is being discovered via trial decryption.
|
2021-01-12 17:24:18 -08:00
|
|
|
fn put_sent_note(
|
2020-08-25 14:02:44 -07:00
|
|
|
&mut self,
|
|
|
|
output: &DecryptedOutput,
|
|
|
|
tx_ref: Self::TxRef,
|
|
|
|
) -> Result<(), Self::Error>;
|
2020-08-25 17:31:21 -07:00
|
|
|
|
2021-01-12 12:55:22 -08:00
|
|
|
/// Add the decrypted contents of a sent note to the database.
|
2021-01-12 17:24:18 -08:00
|
|
|
fn insert_sent_note(
|
2020-08-25 17:31:21 -07:00
|
|
|
&mut self,
|
|
|
|
tx_ref: Self::TxRef,
|
|
|
|
output_index: usize,
|
|
|
|
account: AccountId,
|
|
|
|
to: &RecipientAddress,
|
|
|
|
value: Amount,
|
|
|
|
memo: Option<Memo>,
|
|
|
|
) -> Result<(), Self::Error>;
|
2020-08-20 10:41:43 -07:00
|
|
|
}
|
|
|
|
|
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 20:10:34 -08:00
|
|
|
/// This trait provides a generalization over shielded output representations
|
2021-01-12 12:55:22 -08:00
|
|
|
/// that allows a wallet to avoid coupling to a specific one.
|
2021-01-12 20:10:34 -08:00
|
|
|
// TODO: it'd probably be better not to unify the definitions of
|
2021-01-12 12:55:22 -08:00
|
|
|
// `WalletShieldedOutput` and `DecryptedOutput` via a compositional
|
|
|
|
// approach, if possible.
|
2020-08-25 14:02:44 -07:00
|
|
|
pub trait ShieldedOutput {
|
|
|
|
fn index(&self) -> usize;
|
|
|
|
fn account(&self) -> AccountId;
|
|
|
|
fn to(&self) -> &PaymentAddress;
|
|
|
|
fn note(&self) -> &Note;
|
|
|
|
fn memo(&self) -> Option<&Memo>;
|
|
|
|
fn is_change(&self) -> Option<bool>;
|
2021-03-09 12:15:27 -08:00
|
|
|
fn nullifier(&self) -> Option<Nullifier>;
|
2020-08-25 14:02:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ShieldedOutput for WalletShieldedOutput {
|
|
|
|
fn index(&self) -> usize {
|
|
|
|
self.index
|
|
|
|
}
|
|
|
|
fn account(&self) -> AccountId {
|
2021-01-08 20:49:38 -08:00
|
|
|
self.account
|
2020-08-25 14:02:44 -07:00
|
|
|
}
|
|
|
|
fn to(&self) -> &PaymentAddress {
|
|
|
|
&self.to
|
|
|
|
}
|
|
|
|
fn note(&self) -> &Note {
|
|
|
|
&self.note
|
|
|
|
}
|
|
|
|
fn memo(&self) -> Option<&Memo> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
fn is_change(&self) -> Option<bool> {
|
|
|
|
Some(self.is_change)
|
|
|
|
}
|
2021-03-09 12:15:27 -08:00
|
|
|
|
|
|
|
fn nullifier(&self) -> Option<Nullifier> {
|
|
|
|
self.nf.clone()
|
|
|
|
}
|
2020-08-25 14:02:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ShieldedOutput for DecryptedOutput {
|
|
|
|
fn index(&self) -> usize {
|
|
|
|
self.index
|
|
|
|
}
|
|
|
|
fn account(&self) -> AccountId {
|
2021-01-11 17:13:40 -08:00
|
|
|
self.account
|
2020-08-25 14:02:44 -07:00
|
|
|
}
|
|
|
|
fn to(&self) -> &PaymentAddress {
|
|
|
|
&self.to
|
|
|
|
}
|
|
|
|
fn note(&self) -> &Note {
|
|
|
|
&self.note
|
|
|
|
}
|
|
|
|
fn memo(&self) -> Option<&Memo> {
|
|
|
|
Some(&self.memo)
|
|
|
|
}
|
|
|
|
fn is_change(&self) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
2021-03-09 12:15:27 -08:00
|
|
|
fn nullifier(&self) -> Option<Nullifier> {
|
|
|
|
None
|
|
|
|
}
|
2020-08-25 14:02:44 -07:00
|
|
|
}
|
2021-01-12 11:55:24 -08:00
|
|
|
|
|
|
|
#[cfg(feature = "test-dependencies")]
|
|
|
|
pub mod testing {
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use zcash_primitives::{
|
|
|
|
block::BlockHash,
|
2021-01-13 15:12:19 -08:00
|
|
|
consensus::BlockHeight,
|
2021-01-12 11:55:24 -08:00
|
|
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
|
|
|
note_encryption::Memo,
|
|
|
|
primitives::{Nullifier, PaymentAddress},
|
|
|
|
sapling::Node,
|
|
|
|
transaction::{components::Amount, Transaction, TxId},
|
|
|
|
zip32::ExtendedFullViewingKey,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
address::RecipientAddress,
|
|
|
|
decrypt::DecryptedOutput,
|
|
|
|
proto::compact_formats::CompactBlock,
|
2021-01-12 20:10:34 -08:00
|
|
|
wallet::{AccountId, SpendableNote, WalletTx},
|
2021-01-12 11:55:24 -08:00
|
|
|
};
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
use super::{error::Error, BlockSource, ShieldedOutput, WalletRead, WalletWrite};
|
2021-01-12 11:55:24 -08:00
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
pub struct MockWalletDB {}
|
2021-01-12 11:55:24 -08: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)
|
|
|
|
}
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
fn get_address(&self, _account: AccountId) -> Result<Option<PaymentAddress>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2021-01-12 17:24:18 -08:00
|
|
|
fn get_extended_full_viewing_keys(
|
2021-01-12 20:10:34 -08:00
|
|
|
&self,
|
2021-01-12 11:55:24 -08:00
|
|
|
) -> Result<HashMap<AccountId, ExtendedFullViewingKey>, Self::Error> {
|
|
|
|
Ok(HashMap::new())
|
|
|
|
}
|
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
2021-02-03 11:46:47 -08:00
|
|
|
fn get_memo_as_utf8(&self, _id_note: Self::NoteRef) -> Result<Option<String>, Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
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-01-12 20:51:08 -08:00
|
|
|
fn get_spendable_notes(
|
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
|
|
|
|
2021-01-12 11:55:24 -08:00
|
|
|
fn select_spendable_notes(
|
|
|
|
&self,
|
|
|
|
_account: AccountId,
|
|
|
|
_target_value: Amount,
|
|
|
|
_anchor_height: BlockHeight,
|
|
|
|
) -> Result<Vec<SpendableNote>, Self::Error> {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WalletWrite for MockWalletDB {
|
|
|
|
fn transactionally<F, A>(&mut self, f: F) -> Result<A, Self::Error>
|
|
|
|
where
|
2021-01-12 20:10:34 -08:00
|
|
|
F: FnOnce(&mut Self) -> Result<A, Self::Error>,
|
|
|
|
{
|
2021-01-12 11:55:24 -08:00
|
|
|
f(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn insert_block(
|
|
|
|
&mut self,
|
|
|
|
_block_height: BlockHeight,
|
|
|
|
_block_hash: BlockHash,
|
|
|
|
_block_time: u32,
|
|
|
|
_commitment_tree: &CommitmentTree<Node>,
|
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-01-12 20:10:34 -08:00
|
|
|
fn rewind_to_height(&mut self, _block_height: BlockHeight) -> Result<(), Self::Error> {
|
2021-01-12 11:55:24 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn put_tx_meta(
|
|
|
|
&mut self,
|
|
|
|
_tx: &WalletTx,
|
|
|
|
_height: BlockHeight,
|
|
|
|
) -> Result<Self::TxRef, Self::Error> {
|
|
|
|
Ok(TxId([0u8; 32]))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn put_tx_data(
|
|
|
|
&mut self,
|
|
|
|
_tx: &Transaction,
|
|
|
|
_created_at: Option<time::OffsetDateTime>,
|
|
|
|
) -> Result<Self::TxRef, Self::Error> {
|
|
|
|
Ok(TxId([0u8; 32]))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mark_spent(&mut self, _tx_ref: Self::TxRef, _nf: &Nullifier) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn put_received_note<T: ShieldedOutput>(
|
|
|
|
&mut self,
|
|
|
|
_output: &T,
|
|
|
|
_tx_ref: Self::TxRef,
|
|
|
|
) -> Result<Self::NoteRef, Self::Error> {
|
|
|
|
Ok(0u32)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn insert_witness(
|
|
|
|
&mut self,
|
|
|
|
_note_id: Self::NoteRef,
|
|
|
|
_witness: &IncrementalWitness<Node>,
|
|
|
|
_height: BlockHeight,
|
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn prune_witnesses(&mut self, _from_height: BlockHeight) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_expired_notes(&mut self, _from_height: BlockHeight) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-01-12 17:24:18 -08:00
|
|
|
fn put_sent_note(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
|
|
|
_output: &DecryptedOutput,
|
|
|
|
_tx_ref: Self::TxRef,
|
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-01-12 17:24:18 -08:00
|
|
|
fn insert_sent_note(
|
2021-01-12 11:55:24 -08:00
|
|
|
&mut self,
|
|
|
|
_tx_ref: Self::TxRef,
|
|
|
|
_output_index: usize,
|
|
|
|
_account: AccountId,
|
|
|
|
_to: &RecipientAddress,
|
|
|
|
_value: Amount,
|
|
|
|
_memo: Option<Memo>,
|
|
|
|
) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|