diff --git a/zcash_client_backend/CHANGELOG.md b/zcash_client_backend/CHANGELOG.md index ee29ab2e4..bc7f3a4c5 100644 --- a/zcash_client_backend/CHANGELOG.md +++ b/zcash_client_backend/CHANGELOG.md @@ -26,7 +26,8 @@ funds to those addresses. See [ZIP 320](https://zips.z.cash/zip-0320) for detail ### Added - `zcash_client_backend::data_api`: - `chain::BlockCache` trait, behind the `sync` feature flag. - - `WalletRead::get_spendable_transparent_outputs`. + - `WalletRead::get_spendable_transparent_outputs` + - `DecryptedTransaction::mined_height` - `zcash_client_backend::fees`: - `EphemeralBalance` - `ChangeValue::shielded, is_ephemeral` @@ -68,6 +69,7 @@ funds to those addresses. See [ZIP 320](https://zips.z.cash/zip-0320) for detail - `error::Error` has new `Address` and (when the "transparent-inputs" feature is enabled) `PaysEphemeralTransparentAddress` variants. - `wallet::input_selection::InputSelectorError` has a new `Address` variant. + - `DecryptedTransaction::new` takes an additional `mined_height` argument. - `zcash_client_backend::data_api::fees` - When the "transparent-inputs" feature is enabled, `ChangeValue` can also represent an ephemeral transparent output in a proposal. Accordingly, the diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 9c8e5a932..cd80d169b 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -1263,6 +1263,7 @@ impl ScannedBlock { /// The purpose of this struct is to permit atomic updates of the /// wallet database when transactions are successfully decrypted. pub struct DecryptedTransaction<'a, AccountId> { + mined_height: Option, tx: &'a Transaction, sapling_outputs: Vec>, #[cfg(feature = "orchard")] @@ -1272,6 +1273,7 @@ pub struct DecryptedTransaction<'a, AccountId> { impl<'a, AccountId> DecryptedTransaction<'a, AccountId> { /// Constructs a new [`DecryptedTransaction`] from its constituent parts. pub fn new( + mined_height: Option, tx: &'a Transaction, sapling_outputs: Vec>, #[cfg(feature = "orchard")] orchard_outputs: Vec< @@ -1279,6 +1281,7 @@ impl<'a, AccountId> DecryptedTransaction<'a, AccountId> { >, ) -> Self { Self { + mined_height, tx, sapling_outputs, #[cfg(feature = "orchard")] @@ -1286,6 +1289,10 @@ impl<'a, AccountId> DecryptedTransaction<'a, AccountId> { } } + /// Returns the height at which the transaction was mined, if known. + pub fn mined_height(&self) -> Option { + self.mined_height + } /// Returns the raw transaction data. pub fn tx(&self) -> &Transaction { self.tx diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index 51f9abcdb..b48d077b5 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -215,6 +215,7 @@ pub fn decrypt_transaction<'a, P: consensus::Parameters, AccountId: Copy>( .collect(); DecryptedTransaction::new( + Some(height), tx, sapling_outputs, #[cfg(feature = "orchard")] diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 69f8ebac8..7496eb7c3 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -124,6 +124,9 @@ use wallet::{ SubtreeScanProgress, }; +#[cfg(feature = "transparent-inputs")] +use wallet::transparent::{find_account_for_transparent_address, put_transparent_output}; + #[cfg(test)] mod testing; @@ -1320,6 +1323,24 @@ impl WalletWrite for WalletDb #[cfg(feature = "transparent-inputs")] wallet::transparent::ephemeral::mark_ephemeral_address_as_seen(wdb, &address, tx_ref)?; + // If the output belongs to the wallet, add it to `transparent_received_outputs`. + #[cfg(feature = "transparent-inputs")] + if let Some(account_id) = find_account_for_transparent_address( + wdb.conn.0, + &wdb.params, + &address + )? { + put_transparent_output( + wdb.conn.0, + &wdb.params, + &OutPoint::new(d_tx.tx().txid().into(), u32::try_from(output_index).unwrap()), + txout, + d_tx.mined_height(), + &address, + account_id + )?; + } + // If a transaction we observe contains spends from our wallet, we will // store its transparent outputs in the same way they would be stored by // create_spend_to_address. diff --git a/zcash_client_sqlite/src/testing/pool.rs b/zcash_client_sqlite/src/testing/pool.rs index fbd06e6a6..ae6f37197 100644 --- a/zcash_client_sqlite/src/testing/pool.rs +++ b/zcash_client_sqlite/src/testing/pool.rs @@ -582,6 +582,7 @@ pub(crate) fn send_multi_step_proposed_transfer() { .unwrap(); let txid = build_result.transaction().txid(); let decrypted_tx = DecryptedTransaction::::new( + None, build_result.transaction(), vec![], #[cfg(feature = "orchard")] diff --git a/zcash_primitives/src/legacy.rs b/zcash_primitives/src/legacy.rs index defe8e376..23ba7cf83 100644 --- a/zcash_primitives/src/legacy.rs +++ b/zcash_primitives/src/legacy.rs @@ -1,6 +1,7 @@ //! Support for legacy transparent addresses and scripts. use byteorder::{ReadBytesExt, WriteBytesExt}; +use zcash_address::TryFromRawAddress; use std::fmt; use std::io::{self, Read, Write}; @@ -407,6 +408,22 @@ impl TransparentAddress { } } +impl TryFromRawAddress for TransparentAddress { + type Error = (); + + fn try_from_raw_transparent_p2pkh( + data: [u8; 20], + ) -> Result> { + Ok(TransparentAddress::PublicKeyHash(data)) + } + + fn try_from_raw_transparent_p2sh( + data: [u8; 20], + ) -> Result> { + Ok(TransparentAddress::ScriptHash(data)) + } +} + #[cfg(any(test, feature = "test-dependencies"))] pub mod testing { use proptest::prelude::{any, prop_compose};