From 6aa3917e95aaac05f0a81be325dcbab39337b0cc Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Tue, 19 Nov 2024 10:57:51 +0100 Subject: [PATCH 1/4] add change output to unspent/received notes --- zcash_extras/src/wallet.rs | 51 +++++++++++++++++++-- zcash_primitives/src/transaction/builder.rs | 2 - 2 files changed, 47 insertions(+), 6 deletions(-) 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 From d78f4dfd7adaa259af8f37bb1803a7cad7ec6a1d Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Tue, 19 Nov 2024 14:31:08 +0100 Subject: [PATCH 2/4] comment out adding sapling_output --- zcash_primitives/src/transaction/builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 5954352f50..0b9eac4f9e 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -634,6 +634,7 @@ 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. @@ -654,6 +655,7 @@ 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 From ced91072a8a48889b6e600e1ed718e2a6eaa95d9 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Thu, 5 Dec 2024 18:37:33 +0100 Subject: [PATCH 3/4] use max_height for unmined transactions --- zcash_extras/src/wallet.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zcash_extras/src/wallet.rs b/zcash_extras/src/wallet.rs index 3fa96cb190..f89607dd9a 100644 --- a/zcash_extras/src/wallet.rs +++ b/zcash_extras/src/wallet.rs @@ -36,13 +36,15 @@ where // 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 max_height = data + .block_height_extrema() + .await? + .map(|(_, max)| max + 1) + .ok_or(Error::ScanRequired)?; let height = data .get_tx_height(tx.txid()) .await? - .or(max_height) - .or_else(|| params.activation_height(NetworkUpgrade::Sapling)) - .ok_or(Error::SaplingNotActive)?; + .unwrap_or_else(|| max_height); let outputs = decrypt_transaction(params, height, tx, &extfvks); if outputs.is_empty() { From c910d19edb12dcbf3a457c79761962eea6c1d69a Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Fri, 6 Dec 2024 18:54:37 +0100 Subject: [PATCH 4/4] fix nit --- zcash_extras/src/wallet.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/zcash_extras/src/wallet.rs b/zcash_extras/src/wallet.rs index f89607dd9a..56546eb0e2 100644 --- a/zcash_extras/src/wallet.rs +++ b/zcash_extras/src/wallet.rs @@ -2,7 +2,7 @@ use std::fmt::{Debug, Display}; use zcash_primitives::{ - consensus::{self, BranchId, NetworkUpgrade}, + consensus::{self, BranchId}, memo::MemoBytes, sapling::prover::TxProver, transaction::{ @@ -41,10 +41,7 @@ where .await? .map(|(_, max)| max + 1) .ok_or(Error::ScanRequired)?; - let height = data - .get_tx_height(tx.txid()) - .await? - .unwrap_or_else(|| max_height); + let height = data.get_tx_height(tx.txid()).await?.unwrap_or(max_height); let outputs = decrypt_transaction(params, height, tx, &extfvks); if outputs.is_empty() {