zcash_client_sqlite: Store received UTXOs in `store_decrypted_tx`.

This fixes an issue wherein transparent outputs of transactions added to
the wallet via `decrypt_and_store_transaction` would not be properly
recorded as UTXOs belonging to the wallet.

Part of #1434
This commit is contained in:
Kris Nuttycombe 2024-07-26 08:47:01 -06:00
parent 5ad3205e58
commit 3cec9ee4a7
6 changed files with 50 additions and 1 deletions

View File

@ -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

View File

@ -1263,6 +1263,7 @@ impl<A> ScannedBlock<A> {
/// 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<BlockHeight>,
tx: &'a Transaction,
sapling_outputs: Vec<DecryptedOutput<sapling::Note, AccountId>>,
#[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<BlockHeight>,
tx: &'a Transaction,
sapling_outputs: Vec<DecryptedOutput<sapling::Note, AccountId>>,
#[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<BlockHeight> {
self.mined_height
}
/// Returns the raw transaction data.
pub fn tx(&self) -> &Transaction {
self.tx

View File

@ -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")]

View File

@ -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<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
#[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.

View File

@ -582,6 +582,7 @@ pub(crate) fn send_multi_step_proposed_transfer<T: ShieldedPoolTester>() {
.unwrap();
let txid = build_result.transaction().txid();
let decrypted_tx = DecryptedTransaction::<AccountId>::new(
None,
build_result.transaction(),
vec![],
#[cfg(feature = "orchard")]

View File

@ -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<Self, zcash_address::ConversionError<Self::Error>> {
Ok(TransparentAddress::PublicKeyHash(data))
}
fn try_from_raw_transparent_p2sh(
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(TransparentAddress::ScriptHash(data))
}
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use proptest::prelude::{any, prop_compose};