Trait WalletWrite

Source
pub trait WalletWrite: WalletRead {
    type UtxoRef;

Show 13 methods // Required methods fn create_account( &mut self, account_name: &str, seed: &SecretVec<u8>, birthday: &AccountBirthday, key_source: Option<&str>, ) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error>; fn import_account_hd( &mut self, account_name: &str, seed: &SecretVec<u8>, account_index: AccountId, birthday: &AccountBirthday, key_source: Option<&str>, ) -> Result<(Self::Account, UnifiedSpendingKey), Self::Error>; fn import_account_ufvk( &mut self, account_name: &str, unified_key: &UnifiedFullViewingKey, birthday: &AccountBirthday, purpose: AccountPurpose, key_source: Option<&str>, ) -> Result<Self::Account, Self::Error>; fn get_next_available_address( &mut self, account: Self::AccountId, request: UnifiedAddressRequest, ) -> Result<Option<(UnifiedAddress, DiversifierIndex)>, Self::Error>; fn get_address_for_index( &mut self, account: Self::AccountId, diversifier_index: DiversifierIndex, request: UnifiedAddressRequest, ) -> Result<Option<UnifiedAddress>, Self::Error>; fn update_chain_tip( &mut self, tip_height: BlockHeight, ) -> Result<(), Self::Error>; fn put_blocks( &mut self, from_state: &ChainState, blocks: Vec<ScannedBlock<Self::AccountId>>, ) -> Result<(), Self::Error>; fn put_received_transparent_utxo( &mut self, output: &WalletTransparentOutput, ) -> Result<Self::UtxoRef, Self::Error>; fn store_decrypted_tx( &mut self, received_tx: DecryptedTransaction<'_, Self::AccountId>, ) -> Result<(), Self::Error>; fn store_transactions_to_be_sent( &mut self, transactions: &[SentTransaction<'_, Self::AccountId>], ) -> Result<(), Self::Error>; fn truncate_to_height( &mut self, max_height: BlockHeight, ) -> Result<BlockHeight, Self::Error>; fn set_transaction_status( &mut self, _txid: TxId, _status: TransactionStatus, ) -> Result<(), Self::Error>; // Provided method fn reserve_next_n_ephemeral_addresses( &mut self, _account_id: Self::AccountId, _n: usize, ) -> Result<Vec<(TransparentAddress, TransparentAddressMetadata)>, Self::Error> { ... }
}
Expand description

This trait encapsulates the write capabilities required to update stored wallet data.

§Adding accounts

This trait provides several methods for adding accounts to the wallet data:

All of these methods take an AccountBirthday. The birthday height is defined as the minimum block height that will be scanned for funds belonging to the wallet. If birthday.height() is below the current chain tip, the account addition operation will trigger a re-scan of the blocks at and above the provided height.

The order in which you call these methods will affect the resulting wallet structure:

Note that an error will be returned on an FVK collision even if the UFVKs do not match exactly, e.g. if they have different subsets of components.

A future change to this trait might introduce a method to “upgrade” an imported account with derivation information. See zcash/librustzcash#1284 for details.

Users of the WalletWrite trait should generally distinguish in their APIs and wallet UIs between creating a new account, and importing an account that previously existed. By convention, wallets should only allow a new account to be generated after confirmed funds have been received by the newest existing account; this allows automated account recovery to discover and recover all funds within a particular seed.

§Creating a new wallet

To create a new wallet:

Callers should construct the AccountBirthday using AccountBirthday::from_treestate for the block at height chain_tip_height - 100. 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.

§Restoring a wallet from backup

To restore a backed-up wallet:

  • Derive the seed from its BIP 39 mnemonic phrase.
  • Use WalletWrite::import_account_hd once for each ZIP 32 account index that the user wants to restore.
  • If the highest previously-used ZIP 32 account index was not restored by the user, remember this index separately as index_max. The first time the user wants to generate a new account, use WalletWrite::import_account_hd to create the account index_max + 1.
  • WalletWrite::create_account can be used to generate subsequent new accounts in the restored wallet.

Automated account recovery has not yet been implemented by this crate. A wallet app that supports multiple accounts can implement it manually by tracking account balances relative to WalletSummary::fully_scanned_height, and creating new accounts as funds appear in existing accounts.

If the number of accounts is known in advance, the wallet should create all accounts before scanning the chain so that the scan can be done in a single pass for all accounts.

Required Associated Types§

Source

type UtxoRef

The type of identifiers used to look up transparent UTXOs.

Required Methods§

Source

fn create_account( &mut self, account_name: &str, seed: &SecretVec<u8>, birthday: &AccountBirthday, key_source: Option<&str>, ) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error>

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.

The “next available account” is defined as the ZIP-32 account index immediately following the highest existing account index among all accounts in the wallet that share the given seed. Users of the WalletWrite trait that only call this method are guaranteed to have accounts with sequential indices.

Returns the account identifier for the newly-created wallet database entry, along with the associated UnifiedSpendingKey. Note that the unique account identifier should not be assumed equivalent to the ZIP 32 account index. It is an opaque identifier for a pool of funds or set of outputs controlled by a single spending authority.

The ZIP-32 account index may be obtained by calling WalletRead::get_account with the returned account identifier.

The WalletWrite trait documentation has more details about account creation and import.

§Arguments
  • account_name: A human-readable name for the account.
  • seed: The 256-byte (at least) HD seed from which to derive the account UFVK.
  • birthday: Metadata about where to start scanning blocks to find transactions intended for the account.
  • key_source: A string identifier or other metadata describing the source of the seed. This is treated as opaque metadata by the wallet backend; it is provided for use by applications which need to track additional identifying information for an account.
§Implementation notes

Implementations of this method MUST NOT “fill in gaps” by selecting an account index that is lower than any existing account index among all accounts in the wallet that share the given seed.

§Panics

Panics if the length of the seed is not between 32 and 252 bytes inclusive.

Source

fn import_account_hd( &mut self, account_name: &str, seed: &SecretVec<u8>, account_index: AccountId, birthday: &AccountBirthday, key_source: Option<&str>, ) -> Result<(Self::Account, UnifiedSpendingKey), Self::Error>

Tells the wallet to track a specific account index for a given seed.

Returns details about the imported account, including the unique account identifier for the newly-created wallet database entry, along with the associated UnifiedSpendingKey. Note that the unique account identifier should not be assumed equivalent to the ZIP 32 account index. It is an opaque identifier for a pool of funds or set of outputs controlled by a single spending authority.

Import accounts with indices that are exactly one greater than the highest existing account index to ensure account indices are contiguous, thereby facilitating automated account recovery.

The WalletWrite trait documentation has more details about account creation and import.

§Arguments
  • account_name: A human-readable name for the account.
  • seed: The 256-byte (at least) HD seed from which to derive the account UFVK.
  • account_index: The ZIP 32 account-level component of the HD derivation path at which to derive the account’s UFVK.
  • birthday: Metadata about where to start scanning blocks to find transactions intended for the account.
  • key_source: A string identifier or other metadata describing the source of the seed. This is treated as opaque metadata by the wallet backend; it is provided for use by applications which need to track additional identifying information for an account.
§Panics

Panics if the length of the seed is not between 32 and 252 bytes inclusive.

Source

fn import_account_ufvk( &mut self, account_name: &str, unified_key: &UnifiedFullViewingKey, birthday: &AccountBirthday, purpose: AccountPurpose, key_source: Option<&str>, ) -> Result<Self::Account, Self::Error>

Tells the wallet to track an account using a unified full viewing key.

Returns details about the imported account, including the unique account identifier for the newly-created wallet database entry. Unlike the other account creation APIs (Self::create_account and Self::import_account_hd), no spending key is returned because the wallet has no information about how the UFVK was derived.

Certain optimizations are possible for accounts which will never be used to spend funds. If spending_key_available is false, the wallet may choose to optimize for this case, in which case any attempt to spend funds from the account will result in an error.

The WalletWrite trait documentation has more details about account creation and import.

§Arguments
  • account_name: A human-readable name for the account.
  • unified_key: The UFVK used to detect transactions involving the account.
  • birthday: Metadata about where to start scanning blocks to find transactions intended for the account.
  • purpose: Metadata describing whether or not data required for spending should be tracked by the wallet.
  • key_source: A string identifier or other metadata describing the source of the seed. This is treated as opaque metadata by the wallet backend; it is provided for use by applications which need to track additional identifying information for an account.
§Panics

Panics if the length of the seed is not between 32 and 252 bytes inclusive.

Source

fn get_next_available_address( &mut self, account: Self::AccountId, request: UnifiedAddressRequest, ) -> Result<Option<(UnifiedAddress, DiversifierIndex)>, Self::Error>

Generates, persists, and marks as exposed the next available diversified address for the specified account, given the current addresses known to the wallet.

Returns Ok(None) if the account identifier does not correspond to a known account.

Source

fn get_address_for_index( &mut self, account: Self::AccountId, diversifier_index: DiversifierIndex, request: UnifiedAddressRequest, ) -> Result<Option<UnifiedAddress>, Self::Error>

Generates, persists, and marks as exposed a diversified address for the specified account at the provided diversifier index.

Returns Ok(None) in the case that it is not possible to generate an address conforming to the provided request at the specified diversifier index. Such a result might arise from the diversifier index not being valid for a ReceiverRequirement::Require’ed receiver. Some implementations of this trait may return Err(_) in some cases to expose more information, which is only accessible in a backend-specific context.

Address generation should fail if an address has already been exposed for the given diversifier index and the given request produced an address having different receivers than what was originally exposed.

§WARNINGS

If an address generated using this method has a transparent receiver and the chosen diversifier index would be outside the wallet’s internally-configured gap limit, funds sent to these address are likely to not be discovered on recovery from seed. It up to the caller of this method to either ensure that they only request transparent receivers with indices within the range of a reasonable gap limit, or that they ensure that their wallet provides backup facilities that can be used to ensure that funds sent to such addresses are recoverable after a loss of wallet data.

Source

fn update_chain_tip( &mut self, tip_height: BlockHeight, ) -> Result<(), Self::Error>

Updates the wallet’s view of the blockchain.

This method is used to provide the wallet with information about the state of the 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 WalletRead::suggest_scan_ranges in order to provide the wallet with the information it needs to correctly prioritize scanning operations.

Source

fn put_blocks( &mut self, from_state: &ChainState, blocks: Vec<ScannedBlock<Self::AccountId>>, ) -> Result<(), Self::Error>

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.

§Arguments
  • from_state must be the chain state for the block height prior to the first block in blocks.
  • blocks must be sequential, in order of increasing block height.
Source

fn put_received_transparent_utxo( &mut self, output: &WalletTransparentOutput, ) -> Result<Self::UtxoRef, Self::Error>

Adds a transparent UTXO received by the wallet to the data store.

Source

fn store_decrypted_tx( &mut self, received_tx: DecryptedTransaction<'_, Self::AccountId>, ) -> Result<(), Self::Error>

Caches a decrypted transaction in the persistent wallet store.

Source

fn store_transactions_to_be_sent( &mut self, transactions: &[SentTransaction<'_, Self::AccountId>], ) -> Result<(), Self::Error>

Saves information about transactions constructed by the wallet to the persistent wallet store.

This must be called before the transactions are sent to the network.

Transactions that have been stored by this method should be retransmitted while it is still possible that they could be mined.

Source

fn truncate_to_height( &mut self, max_height: BlockHeight, ) -> Result<BlockHeight, Self::Error>

Truncates the wallet database to at most the specified height.

Implementations of this method may choose a lower block height to which the data store will be truncated if it is not possible to truncate exactly to the specified height. Upon successful truncation, this method returns the height to which the data store was actually truncated.

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 at most the requested block height.

After calling this method, the block at the returned height will be the most recent block and all other operations will treat this block as the chain tip for balance determination purposes.

There may be restrictions on heights to which it is possible to truncate. Specifically, it will only be possible to truncate to heights at which is is possible to create a witness given the current state of the wallet’s note commitment tree.

Source

fn set_transaction_status( &mut self, _txid: TxId, _status: TransactionStatus, ) -> Result<(), Self::Error>

Updates the wallet backend with respect to the status of a specific transaction, from the perspective of the main chain.

Fully transparent transactions, and transactions that do not contain either shielded inputs or shielded outputs belonging to the wallet, may not be discovered by the process of chain scanning; as a consequence, the wallet must actively query to determine whether such transactions have been mined.

Provided Methods§

Source

fn reserve_next_n_ephemeral_addresses( &mut self, _account_id: Self::AccountId, _n: usize, ) -> Result<Vec<(TransparentAddress, TransparentAddressMetadata)>, Self::Error>

Available on crate feature transparent-inputs only.

Reserves the next n available ephemeral addresses for the given account. This cannot be undone, so as far as possible, errors associated with transaction construction should have been reported before calling this method.

To ensure that sufficient information is stored on-chain to allow recovering funds sent back to any of the used addresses, a “gap limit” of 20 addresses should be observed as described in BIP 44.

Returns an error if there is insufficient space within the gap limit to allocate the given number of addresses, or if the account identifier does not correspond to a known account.

Implementors§

Source§

impl WalletWrite for MockWalletDb

Available on crate feature test-dependencies only.