diff --git a/zcash_extras/src/wallet.rs b/zcash_extras/src/wallet.rs index c097bd99ba..3fa96cb190 100644 --- a/zcash_extras/src/wallet.rs +++ b/zcash_extras/src/wallet.rs @@ -2,12 +2,13 @@ use std::fmt::{Debug, Display}; use zcash_primitives::{ - consensus::{self, BranchId}, + consensus::{self, BranchId, NetworkUpgrade}, memo::MemoBytes, sapling::prover::TxProver, transaction::{ builder::Builder, components::{amount::DEFAULT_FEE, Amount}, + Transaction, }, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, }; @@ -15,10 +16,48 @@ use zcash_primitives::{ use crate::WalletWrite; use zcash_client_backend::{ address::RecipientAddress, - data_api::{error::Error, SentTransaction}, + data_api::{error::Error, ReceivedTransaction, SentTransaction}, + decrypt_transaction, wallet::{AccountId, OvkPolicy}, }; +/// Scans a [`Transaction`] for any information that can be decrypted by the accounts in +/// the wallet, and saves it to the wallet. +pub async fn decrypt_and_store_transaction( + params: &P, + data: &mut D, + tx: &Transaction, +) -> Result<(), E> +where + E: From>, + P: consensus::Parameters, + D: WalletWrite, +{ + // Fetch the ExtendedFullViewingKeys we are tracking + let extfvks = data.get_extended_full_viewing_keys().await?; + + let max_height = data.block_height_extrema().await?.map(|(_, max)| max + 1); + let height = data + .get_tx_height(tx.txid()) + .await? + .or(max_height) + .or_else(|| params.activation_height(NetworkUpgrade::Sapling)) + .ok_or(Error::SaplingNotActive)?; + + let outputs = decrypt_transaction(params, height, tx, &extfvks); + if outputs.is_empty() { + Ok(()) + } else { + data.store_received_tx(&ReceivedTransaction { + tx, + outputs: &outputs, + }) + .await?; + + Ok(()) + } +} + #[allow(clippy::needless_doctest_main)] /// Creates a transaction paying the specified address from the given account. /// @@ -182,8 +221,7 @@ where RecipientAddress::Shielded(to) => { builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) } - - RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), + RecipientAddress::Transparent(to) => builder.add_transparent_output(to, value), } .map_err(Error::Builder)?; @@ -209,6 +247,11 @@ where } }; + // Automatically decrypt and store any outputs sent to our wallet, including change. + // This uses our viewing keys to find any outputs we can decrypt, creates decrypted + // note data for spendability, and saves them to the wallet database. + decrypt_and_store_transaction(params, wallet_db, &tx).await?; + wallet_db .store_sent_tx(&SentTransaction { tx: &tx, diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 0b9eac4f9e..5954352f50 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -634,7 +634,6 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { // Change output // - /* if change.is_positive() { // Send change to the specified change address. If no change address // was set, send change to the first Sapling address given as input. @@ -655,7 +654,6 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { self.add_sapling_output(Some(change_address.0), change_address.1, change, None)?; } - */ // // Record initial positions of spends and outputs