zcash_client_backend: Move `Recipient::EphemeralTransparent` behind the `transparent-inputs` feature flag.

This variant should not have been a part of the public API unless
`tranpsarent-inputs` was enabled, as it's necessary for the wallet to be
able to spend a transparent input in order for a ZIP 320 transaction to
be properly constructed and authorized.

In addition, this simplifies the `Recipient` API by removing its type
parameters in favor of concrete types, made possible by using a
separate type for the build process.
This commit is contained in:
Kris Nuttycombe 2024-12-23 17:09:54 -07:00
parent 736bfd555b
commit 5290d13942
5 changed files with 262 additions and 241 deletions

View File

@ -9,6 +9,18 @@ and this library adheres to Rust's notion of
### Changed
- Migrated to `nonempty 0.11`
- `zcash_client_backend::wallet::Recipient` has changed:
- The `Recipient::External` variant is now a structured variant.
- The `Recipient::EphemeralTransparent` variant is now only available if
`zcash_client_backend` is built using the `transparent-inputs` feature flag.
- The `N` and `O` type pararameters to this type have been replaced by
concrete uses of `Box<Note>` and `Outpoint` instead. The
`map_internal_account_note` and `map_ephemeral_transparent_outpoint` and
`internal_account_note_transpose_option` methods have consequently been
removed.
- `zcash_client_backend::data_api::WalletRead::get_known_ephemeral_addresses`
now takes a `Range<zcash_transparent::keys::NonHardenedChildIndex>` as its
argument instead of a `Range<u32>`
### Deprecated
- `zcash_client_backend::address` (use `zcash_keys::address` instead)

View File

@ -68,7 +68,6 @@ use std::{
use incrementalmerkletree::{frontier::Frontier, Retention};
use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree};
use ::transparent::bundle::OutPoint;
use zcash_keys::{
address::UnifiedAddress,
keys::{
@ -99,7 +98,8 @@ use crate::{
#[cfg(feature = "transparent-inputs")]
use {
crate::wallet::TransparentAddressMetadata, ::transparent::address::TransparentAddress,
crate::wallet::TransparentAddressMetadata,
::transparent::{address::TransparentAddress, bundle::OutPoint},
std::ops::Range,
};
@ -1966,7 +1966,7 @@ impl<'a, AccountId> SentTransaction<'a, AccountId> {
/// This type is capable of representing both shielded and transparent outputs.
pub struct SentTransactionOutput<AccountId> {
output_index: usize,
recipient: Recipient<AccountId, Note, OutPoint>,
recipient: Recipient<AccountId>,
value: Zatoshis,
memo: Option<MemoBytes>,
}
@ -1983,7 +1983,7 @@ impl<AccountId> SentTransactionOutput<AccountId> {
/// * `memo` - the memo that was sent with this output
pub fn from_parts(
output_index: usize,
recipient: Recipient<AccountId, Note, OutPoint>,
recipient: Recipient<AccountId>,
value: Zatoshis,
memo: Option<MemoBytes>,
) -> Self {
@ -2006,7 +2006,7 @@ impl<AccountId> SentTransactionOutput<AccountId> {
}
/// Returns the recipient address of the transaction, or the account id and
/// resulting note/outpoint for wallet-internal outputs.
pub fn recipient(&self) -> &Recipient<AccountId, Note, OutPoint> {
pub fn recipient(&self) -> &Recipient<AccountId> {
&self.recipient
}
/// Returns the value of the newly created output.

View File

@ -35,18 +35,9 @@ to a wallet-internal shielded address, as described in [ZIP 316](https://zips.z.
use nonempty::NonEmpty;
use rand_core::OsRng;
use sapling::{
note_encryption::{try_sapling_note_decryption, PreparedIncomingViewingKey},
prover::{OutputProver, SpendProver},
};
use shardtree::error::{QueryError, ShardTreeError};
use std::num::NonZeroU32;
use zcash_keys::{
address::Address,
keys::{UnifiedFullViewingKey, UnifiedSpendingKey},
};
use zcash_protocol::{PoolType, ShieldedProtocol};
use zip321::Payment;
use shardtree::error::{QueryError, ShardTreeError};
use super::InputSource;
use crate::{
@ -61,10 +52,18 @@ use crate::{
proposal::{Proposal, ProposalError, Step, StepOutputIndex},
wallet::{Note, OvkPolicy, Recipient},
};
use ::sapling::{
note_encryption::{try_sapling_note_decryption, PreparedIncomingViewingKey},
prover::{OutputProver, SpendProver},
};
use ::transparent::{
address::TransparentAddress, builder::TransparentSigningSet, bundle::OutPoint,
};
use zcash_address::ZcashAddress;
use zcash_keys::{
address::Address,
keys::{UnifiedFullViewingKey, UnifiedSpendingKey},
};
use zcash_primitives::transaction::{
builder::{BuildConfig, BuildResult, Builder},
components::sapling::zip212_enforcement,
@ -75,8 +74,10 @@ use zcash_protocol::{
consensus::{self, BlockHeight, NetworkUpgrade},
memo::MemoBytes,
value::Zatoshis,
PoolType, ShieldedProtocol,
};
use zip32::Scope;
use zip321::Payment;
#[cfg(feature = "transparent-inputs")]
use {
@ -100,7 +101,6 @@ use {
},
sapling::note_encryption::SaplingDomain,
serde::{Deserialize, Serialize},
zcash_address::ZcashAddress,
zcash_note_encryption::try_output_recovery_with_pkd_esk,
zcash_protocol::{
consensus::NetworkConstants,
@ -133,25 +133,32 @@ struct ProposalInfo<AccountId> {
#[derive(Serialize, Deserialize)]
enum PcztRecipient<AccountId> {
External,
EphemeralTransparent { receiving_account: AccountId },
InternalAccount { receiving_account: AccountId },
#[cfg(feature = "transparent-inputs")]
EphemeralTransparent {
receiving_account: AccountId,
},
InternalAccount {
receiving_account: AccountId,
},
}
#[cfg(feature = "pczt")]
impl<AccountId: Copy> PcztRecipient<AccountId> {
fn from_recipient<N, O>(recipient: Recipient<AccountId, N, O>) -> (Self, Option<ZcashAddress>) {
fn from_recipient(recipient: BuildRecipient<AccountId>) -> (Self, Option<ZcashAddress>) {
match recipient {
Recipient::External(addr, _) => (PcztRecipient::External, Some(addr)),
Recipient::EphemeralTransparent {
BuildRecipient::External {
recipient_address, ..
} => (PcztRecipient::External, Some(recipient_address)),
#[cfg(feature = "transparent-inputs")]
BuildRecipient::EphemeralTransparent {
receiving_account, ..
} => (
PcztRecipient::EphemeralTransparent { receiving_account },
None,
),
Recipient::InternalAccount {
BuildRecipient::InternalAccount {
receiving_account,
external_address,
..
} => (
PcztRecipient::InternalAccount { receiving_account },
external_address,
@ -536,6 +543,72 @@ where
Ok(NonEmpty::from_vec(txids).expect("proposal.steps is NonEmpty"))
}
#[derive(Debug, Clone)]
enum BuildRecipient<AccountId> {
External {
recipient_address: ZcashAddress,
output_pool: PoolType,
},
#[cfg(feature = "transparent-inputs")]
EphemeralTransparent {
receiving_account: AccountId,
ephemeral_address: TransparentAddress,
},
InternalAccount {
receiving_account: AccountId,
external_address: Option<ZcashAddress>,
},
}
impl<AccountId> BuildRecipient<AccountId> {
fn into_recipient_with_note(self, note: impl FnOnce() -> Note) -> Recipient<AccountId> {
match self {
BuildRecipient::External {
recipient_address,
output_pool,
} => Recipient::External {
recipient_address,
output_pool,
},
#[cfg(feature = "transparent-inputs")]
BuildRecipient::EphemeralTransparent { .. } => unreachable!(),
BuildRecipient::InternalAccount {
receiving_account,
external_address,
} => Recipient::InternalAccount {
receiving_account,
external_address,
note: Box::new(note()),
},
}
}
fn into_recipient_with_outpoint(
self,
#[cfg(feature = "transparent-inputs")] outpoint: OutPoint,
) -> Recipient<AccountId> {
match self {
BuildRecipient::External {
recipient_address,
output_pool,
} => Recipient::External {
recipient_address,
output_pool,
},
#[cfg(feature = "transparent-inputs")]
BuildRecipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
} => Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint,
},
BuildRecipient::InternalAccount { .. } => unreachable!(),
}
}
}
#[allow(clippy::type_complexity)]
struct BuildState<'a, P, AccountId> {
#[cfg(feature = "transparent-inputs")]
@ -544,18 +617,10 @@ struct BuildState<'a, P, AccountId> {
#[cfg(feature = "transparent-inputs")]
transparent_input_addresses: HashMap<TransparentAddress, TransparentAddressMetadata>,
#[cfg(feature = "orchard")]
orchard_output_meta: Vec<(
Recipient<AccountId, PoolType, OutPoint>,
Zatoshis,
Option<MemoBytes>,
)>,
sapling_output_meta: Vec<(
Recipient<AccountId, PoolType, OutPoint>,
Zatoshis,
Option<MemoBytes>,
)>,
orchard_output_meta: Vec<(BuildRecipient<AccountId>, Zatoshis, Option<MemoBytes>)>,
sapling_output_meta: Vec<(BuildRecipient<AccountId>, Zatoshis, Option<MemoBytes>)>,
transparent_output_meta: Vec<(
Recipient<AccountId, Note, ()>,
BuildRecipient<AccountId>,
TransparentAddress,
Zatoshis,
StepOutputIndex,
@ -884,12 +949,10 @@ where
};
#[cfg(feature = "orchard")]
let mut orchard_output_meta: Vec<(Recipient<_, PoolType, _>, Zatoshis, Option<MemoBytes>)> =
vec![];
let mut sapling_output_meta: Vec<(Recipient<_, PoolType, _>, Zatoshis, Option<MemoBytes>)> =
vec![];
let mut orchard_output_meta: Vec<(BuildRecipient<_>, Zatoshis, Option<MemoBytes>)> = vec![];
let mut sapling_output_meta: Vec<(BuildRecipient<_>, Zatoshis, Option<MemoBytes>)> = vec![];
let mut transparent_output_meta: Vec<(
Recipient<_, _, ()>,
BuildRecipient<_>,
TransparentAddress,
Zatoshis,
StepOutputIndex,
@ -915,7 +978,10 @@ where
let memo = payment.memo().map_or_else(MemoBytes::empty, |m| m.clone());
builder.add_sapling_output(sapling_external_ovk, to, payment.amount(), memo.clone())?;
sapling_output_meta.push((
Recipient::External(recipient_address.clone(), PoolType::SAPLING),
BuildRecipient::External {
recipient_address: recipient_address.clone(),
output_pool: PoolType::SAPLING,
},
payment.amount(),
Some(memo),
));
@ -936,7 +1002,10 @@ where
memo.clone(),
)?;
orchard_output_meta.push((
Recipient::External(recipient_address.clone(), PoolType::ORCHARD),
BuildRecipient::External {
recipient_address: recipient_address.clone(),
output_pool: PoolType::ORCHARD,
},
payment.amount(),
Some(memo),
));
@ -962,7 +1031,10 @@ where
}
builder.add_transparent_output(&to, payment.amount())?;
transparent_output_meta.push((
Recipient::External(recipient_address.clone(), PoolType::TRANSPARENT),
BuildRecipient::External {
recipient_address: recipient_address.clone(),
output_pool: PoolType::TRANSPARENT,
},
to,
payment.amount(),
StepOutputIndex::Payment(payment_index),
@ -1031,10 +1103,9 @@ where
memo.clone(),
)?;
sapling_output_meta.push((
Recipient::InternalAccount {
BuildRecipient::InternalAccount {
receiving_account: account_id,
external_address: None,
note: output_pool,
},
change_value.value(),
Some(memo),
@ -1055,10 +1126,9 @@ where
memo.clone(),
)?;
orchard_output_meta.push((
Recipient::InternalAccount {
BuildRecipient::InternalAccount {
receiving_account: account_id,
external_address: None,
note: output_pool,
},
change_value.value(),
Some(memo),
@ -1100,10 +1170,9 @@ where
// if a later step does not consume it.
builder.add_transparent_output(&ephemeral_address, change_value.value())?;
transparent_output_meta.push((
Recipient::EphemeralTransparent {
BuildRecipient::EphemeralTransparent {
receiving_account: account_id,
ephemeral_address,
outpoint_metadata: (),
},
ephemeral_address,
change_value.value(),
@ -1208,20 +1277,17 @@ where
.output_action_index(i)
.expect("An action should exist in the transaction for each Orchard output.");
let recipient = recipient
.map_internal_account_note(|pool| {
assert!(pool == PoolType::ORCHARD);
build_result
.transaction()
.orchard_bundle()
.and_then(|bundle| {
bundle
.decrypt_output_with_key(output_index, &orchard_internal_ivk)
.map(|(note, _, _)| Note::Orchard(note))
})
})
.internal_account_note_transpose_option()
.expect("Wallet-internal outputs must be decryptable with the wallet's IVK");
let recipient = recipient.into_recipient_with_note(|| {
build_result
.transaction()
.orchard_bundle()
.and_then(|bundle| {
bundle
.decrypt_output_with_key(output_index, &orchard_internal_ivk)
.map(|(note, _, _)| Note::Orchard(note))
})
.expect("Wallet-internal outputs must be decryptable with the wallet's IVK")
});
SentTransactionOutput::from_parts(output_index, recipient, value, memo)
},
@ -1237,23 +1303,20 @@ where
.output_index(i)
.expect("An output should exist in the transaction for each Sapling payment.");
let recipient = recipient
.map_internal_account_note(|pool| {
assert!(pool == PoolType::SAPLING);
build_result
.transaction()
.sapling_bundle()
.and_then(|bundle| {
try_sapling_note_decryption(
&sapling_internal_ivk,
&bundle.shielded_outputs()[output_index],
zip212_enforcement(params, min_target_height),
)
.map(|(note, _, _)| Note::Sapling(note))
})
})
.internal_account_note_transpose_option()
.expect("Wallet-internal outputs must be decryptable with the wallet's IVK");
let recipient = recipient.into_recipient_with_note(|| {
build_result
.transaction()
.sapling_bundle()
.and_then(|bundle| {
try_sapling_note_decryption(
&sapling_internal_ivk,
&bundle.shielded_outputs()[output_index],
zip212_enforcement(params, min_target_height),
)
.map(|(note, _, _)| Note::Sapling(note))
})
.expect("Wallet-internal outputs must be decryptable with the wallet's IVK")
});
SentTransactionOutput::from_parts(output_index, recipient, value, memo)
},
@ -1280,7 +1343,11 @@ where
// would not usefully improve privacy.
let outpoint = OutPoint::new(txid, n as u32);
let recipient = recipient.map_ephemeral_transparent_outpoint(|()| outpoint.clone());
let recipient = recipient.into_recipient_with_outpoint(
#[cfg(feature = "transparent-inputs")]
outpoint.clone(),
);
#[cfg(feature = "transparent-inputs")]
unused_transparent_outputs.insert(
StepOutput::new(build_state.step_index, step_output_index),
@ -1819,12 +1886,14 @@ where
let note_value = Zatoshis::try_from(note_value(&note))?;
let recipient = match (pczt_recipient, external_address) {
(PcztRecipient::External, Some(addr)) => {
Ok(Recipient::External(addr, PoolType::Shielded(output_pool)))
}
(PcztRecipient::External, Some(addr)) => Ok(Recipient::External {
recipient_address: addr,
output_pool: PoolType::Shielded(output_pool),
}),
(PcztRecipient::External, None) => Err(PcztError::Invalid(
"external recipient needs to have its user_address field set".into(),
)),
#[cfg(feature = "transparent-inputs")]
(PcztRecipient::EphemeralTransparent { .. }, _) => Err(PcztError::Invalid(
"shielded output cannot be EphemeralTransparent".into(),
)),
@ -1832,7 +1901,7 @@ where
Ok(Recipient::InternalAccount {
receiving_account,
external_address,
note: wallet_note(note),
note: Box::new(wallet_note(note)),
})
}
}?;
@ -1927,11 +1996,15 @@ where
let recipient = match (pczt_recipient, external_address) {
(PcztRecipient::External, Some(addr)) => {
Ok(Recipient::External(addr, PoolType::Transparent))
Ok(Recipient::External {
recipient_address: addr,
output_pool: PoolType::Transparent,
})
}
(PcztRecipient::External, None) => Err(PcztError::Invalid(
"external recipient needs to have its user_address field set".into(),
)),
#[cfg(feature = "transparent-inputs")]
(PcztRecipient::EphemeralTransparent { receiving_account }, _) => output
.recipient_address()
.ok_or(PcztError::Invalid(
@ -1941,7 +2014,7 @@ where
.map(|ephemeral_address| Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata: outpoint,
outpoint,
}),
(
PcztRecipient::InternalAccount {

View File

@ -1,11 +1,12 @@
//! Structs representing transaction data scanned from the block chain by a wallet or
//! light client.
use incrementalmerkletree::Position;
use ::transparent::{
address::TransparentAddress,
bundle::{OutPoint, TxOut},
};
use incrementalmerkletree::Position;
use zcash_address::ZcashAddress;
use zcash_note_encryption::EphemeralKeyBytes;
use zcash_primitives::transaction::{fees::transparent as transparent_fees, TxId};
@ -60,111 +61,31 @@ impl NoteId {
}
/// A type that represents the recipient of a transaction output:
///
/// * a recipient address;
/// * for external unified addresses, the pool to which the payment is sent;
/// * for ephemeral transparent addresses, the internal account ID and metadata about the outpoint;
/// * for wallet-internal outputs, the internal account ID and metadata about the note.
/// * if the `transparent-inputs` feature is enabled, for ephemeral transparent outputs, the
/// internal account ID and metadata about the outpoint;
#[derive(Debug, Clone)]
pub enum Recipient<AccountId, N, O> {
External(ZcashAddress, PoolType),
pub enum Recipient<AccountId> {
External {
recipient_address: ZcashAddress,
output_pool: PoolType,
},
#[cfg(feature = "transparent-inputs")]
EphemeralTransparent {
receiving_account: AccountId,
ephemeral_address: TransparentAddress,
outpoint_metadata: O,
outpoint: OutPoint,
},
InternalAccount {
receiving_account: AccountId,
external_address: Option<ZcashAddress>,
note: N,
note: Box<Note>,
},
}
impl<AccountId, N, O> Recipient<AccountId, N, O> {
/// Return a copy of this `Recipient` with `f` applied to the note metadata, if any.
pub fn map_internal_account_note<B, F: FnOnce(N) -> B>(
self,
f: F,
) -> Recipient<AccountId, B, O> {
match self {
Recipient::External(addr, pool) => Recipient::External(addr, pool),
Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata,
} => Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata,
},
Recipient::InternalAccount {
receiving_account,
external_address,
note,
} => Recipient::InternalAccount {
receiving_account,
external_address,
note: f(note),
},
}
}
/// Return a copy of this `Recipient` with `f` applied to the output metadata, if any.
pub fn map_ephemeral_transparent_outpoint<B, F: FnOnce(O) -> B>(
self,
f: F,
) -> Recipient<AccountId, N, B> {
match self {
Recipient::External(addr, pool) => Recipient::External(addr, pool),
Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata,
} => Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata: f(outpoint_metadata),
},
Recipient::InternalAccount {
receiving_account,
external_address,
note,
} => Recipient::InternalAccount {
receiving_account,
external_address,
note,
},
}
}
}
impl<AccountId, N, O> Recipient<AccountId, Option<N>, O> {
/// Return a copy of this `Recipient` with optional note metadata transposed to
/// an optional result.
pub fn internal_account_note_transpose_option(self) -> Option<Recipient<AccountId, N, O>> {
match self {
Recipient::External(addr, pool) => Some(Recipient::External(addr, pool)),
Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata,
} => Some(Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata,
}),
Recipient::InternalAccount {
receiving_account,
external_address,
note,
} => note.map(|n0| Recipient::InternalAccount {
receiving_account,
external_address,
note: n0,
}),
}
}
}
/// The shielded subset of a [`Transaction`]'s data that is relevant to a particular wallet.
///
/// [`Transaction`]: zcash_primitives::transaction::Transaction

View File

@ -84,7 +84,6 @@ use std::ops::RangeInclusive;
use tracing::{debug, warn};
use ::transparent::bundle::OutPoint;
use zcash_address::ZcashAddress;
use zcash_client_backend::{
data_api::{
@ -114,8 +113,9 @@ use zcash_protocol::{
value::{ZatBalance, Zatoshis},
PoolType, ShieldedProtocol,
};
use zip32::{self, DiversifierIndex, Scope};
use zip32::{DiversifierIndex, Scope};
use self::scanning::{parse_priority_code, priority_code, replace_queue_entries};
use crate::{
error::SqliteClientError,
wallet::commitment_tree::{get_max_checkpointed_height, SqliteShardStore},
@ -125,9 +125,7 @@ use crate::{
use crate::{AccountUuid, TxRef, VERIFY_LOOKAHEAD};
#[cfg(feature = "transparent-inputs")]
use ::transparent::bundle::TxOut;
use self::scanning::{parse_priority_code, priority_code, replace_queue_entries};
use ::transparent::bundle::{OutPoint, TxOut};
#[cfg(feature = "orchard")]
use {crate::ORCHARD_TABLES_PREFIX, zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT};
@ -2269,55 +2267,53 @@ pub(crate) fn store_transaction_to_be_sent<P: consensus::Parameters>(
match output.recipient() {
Recipient::InternalAccount {
receiving_account,
note: Note::Sapling(note),
note,
..
} => {
sapling::put_received_note(
wdb.conn.0,
&DecryptedOutput::new(
output.output_index(),
note.clone(),
*receiving_account,
output
.memo()
.map_or_else(MemoBytes::empty, |memo| memo.clone()),
TransferType::WalletInternal,
),
tx_ref,
None,
)?;
}
#[cfg(feature = "orchard")]
Recipient::InternalAccount {
receiving_account,
note: Note::Orchard(note),
..
} => {
orchard::put_received_note(
wdb.conn.0,
&DecryptedOutput::new(
output.output_index(),
*note,
*receiving_account,
output
.memo()
.map_or_else(MemoBytes::empty, |memo| memo.clone()),
TransferType::WalletInternal,
),
tx_ref,
None,
)?;
}
} => match note.as_ref() {
Note::Sapling(note) => {
sapling::put_received_note(
wdb.conn.0,
&DecryptedOutput::new(
output.output_index(),
note.clone(),
*receiving_account,
output
.memo()
.map_or_else(MemoBytes::empty, |memo| memo.clone()),
TransferType::WalletInternal,
),
tx_ref,
None,
)?;
}
#[cfg(feature = "orchard")]
Note::Orchard(note) => {
orchard::put_received_note(
wdb.conn.0,
&DecryptedOutput::new(
output.output_index(),
*note,
*receiving_account,
output
.memo()
.map_or_else(MemoBytes::empty, |memo| memo.clone()),
TransferType::WalletInternal,
),
tx_ref,
None,
)?;
}
},
#[cfg(feature = "transparent-inputs")]
Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
outpoint_metadata,
outpoint,
} => {
transparent::put_transparent_output(
wdb.conn.0,
&wdb.params,
outpoint_metadata,
outpoint,
&TxOut {
value: output.value(),
script_pubkey: ephemeral_address.script(),
@ -2733,11 +2729,14 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
TransferType::Outgoing => {
let recipient = {
let receiver = Receiver::Sapling(output.note().recipient());
let wallet_address =
let recipient_address =
select_receiving_address(params, conn, *output.account(), &receiver)?
.unwrap_or_else(|| receiver.to_zcash_address(params.network_type()));
Recipient::External(wallet_address, PoolType::SAPLING)
Recipient::External {
recipient_address,
output_pool: PoolType::SAPLING,
}
};
put_sent_output(
@ -2757,7 +2756,7 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
let recipient = Recipient::InternalAccount {
receiving_account: *output.account(),
external_address: None,
note: Note::Sapling(output.note().clone()),
note: Box::new(Note::Sapling(output.note().clone())),
};
put_sent_output(
@ -2791,7 +2790,7 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
}),
)
},
note: Note::Sapling(output.note().clone()),
note: Box::new(Note::Sapling(output.note().clone())),
};
put_sent_output(
@ -2819,11 +2818,14 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
TransferType::Outgoing => {
let recipient = {
let receiver = Receiver::Orchard(output.note().recipient());
let wallet_address =
let recipient_address =
select_receiving_address(params, conn, *output.account(), &receiver)?
.unwrap_or_else(|| receiver.to_zcash_address(params.network_type()));
Recipient::External(wallet_address, PoolType::ORCHARD)
Recipient::External {
recipient_address,
output_pool: PoolType::ORCHARD,
}
};
put_sent_output(
@ -2843,7 +2845,7 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
let recipient = Recipient::InternalAccount {
receiving_account: *output.account(),
external_address: None,
note: Note::Orchard(*output.note()),
note: Box::new(Note::Orchard(*output.note())),
};
put_sent_output(
@ -2878,7 +2880,7 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
}),
)
},
note: Note::Orchard(*output.note()),
note: Box::new(Note::Orchard(*output.note())),
};
put_sent_output(
@ -2989,14 +2991,17 @@ pub(crate) fn store_decrypted_tx<P: consensus::Parameters>(
let receiver = Receiver::Transparent(address);
#[cfg(feature = "transparent-inputs")]
let recipient_addr =
let recipient_address =
select_receiving_address(params, conn, account_uuid, &receiver)?
.unwrap_or_else(|| receiver.to_zcash_address(params.network_type()));
#[cfg(not(feature = "transparent-inputs"))]
let recipient_addr = receiver.to_zcash_address(params.network_type());
let recipient_address = receiver.to_zcash_address(params.network_type());
let recipient = Recipient::External(recipient_addr, PoolType::TRANSPARENT);
let recipient = Recipient::External {
recipient_address,
output_pool: PoolType::TRANSPARENT,
};
put_sent_output(
conn,
@ -3295,13 +3300,23 @@ pub(crate) fn notify_tx_retrieved(
// and `put_sent_output`
fn recipient_params<P: consensus::Parameters>(
conn: &Connection,
params: &P,
_params: &P,
from: AccountUuid,
to: &Recipient<AccountUuid, Note, OutPoint>,
to: &Recipient<AccountUuid>,
) -> Result<(AccountRef, Option<String>, Option<AccountRef>, PoolType), SqliteClientError> {
let from_account_id = get_account_ref(conn, from)?;
match to {
Recipient::External(addr, pool) => Ok((from_account_id, Some(addr.encode()), None, *pool)),
Recipient::External {
recipient_address,
output_pool,
..
} => Ok((
from_account_id,
Some(recipient_address.encode()),
None,
*output_pool,
)),
#[cfg(feature = "transparent-inputs")]
Recipient::EphemeralTransparent {
receiving_account,
ephemeral_address,
@ -3310,7 +3325,7 @@ fn recipient_params<P: consensus::Parameters>(
let to_account = get_account_ref(conn, *receiving_account)?;
Ok((
from_account_id,
Some(ephemeral_address.encode(params)),
Some(ephemeral_address.encode(_params)),
Some(to_account),
PoolType::TRANSPARENT,
))
@ -3414,7 +3429,7 @@ pub(crate) fn put_sent_output<P: consensus::Parameters>(
from_account_uuid: AccountUuid,
tx_ref: TxRef,
output_index: usize,
recipient: &Recipient<AccountUuid, Note, OutPoint>,
recipient: &Recipient<AccountUuid>,
value: Zatoshis,
memo: Option<&MemoBytes>,
) -> Result<(), SqliteClientError> {