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:
parent
736bfd555b
commit
5290d13942
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(¬e))?;
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Reference in New Issue