Apply suggestions from code review

Co-authored-by: str4d <thestr4d@gmail.com>
This commit is contained in:
Kris Nuttycombe 2024-02-21 17:32:15 -07:00 committed by Kris Nuttycombe
parent 050a124cb6
commit 184286c430
10 changed files with 98 additions and 62 deletions

View File

@ -65,7 +65,7 @@ and this library adheres to Rust's notion of
- `zcash_client_backend::wallet`: - `zcash_client_backend::wallet`:
- `Note` - `Note`
- `ReceivedNote` - `ReceivedNote`
- `Recipient::{map, transpose}` - `Recipient::{map_internal_account, internal_account_transpose_option}`
- `WalletSaplingOutput::recipient_key_scope` - `WalletSaplingOutput::recipient_key_scope`
- `TransparentAddressMetadata` (which replaces `zcash_keys::address::AddressMetadata`). - `TransparentAddressMetadata` (which replaces `zcash_keys::address::AddressMetadata`).
- `impl {Debug, Clone} for OvkPolicy` - `impl {Debug, Clone} for OvkPolicy`

View File

@ -148,7 +148,7 @@ where
Error::UnsupportedChangeType(t) => write!(f, "Attempted to send change to an unsupported pool type: {}", t), Error::UnsupportedChangeType(t) => write!(f, "Attempted to send change to an unsupported pool type: {}", t),
Error::NoSupportedReceivers(ua) => write!( Error::NoSupportedReceivers(ua) => write!(
f, f,
"A recipient's unified address does not contain any receivers to which the wallet can send funds; required {}", "A recipient's unified address does not contain any receivers to which the wallet can send funds; required one of {}",
ua.receiver_types().iter().enumerate().map(|(i, tc)| format!("{}{:?}", if i > 0 { ", " } else { "" }, tc)).collect::<String>() ua.receiver_types().iter().enumerate().map(|(i, tc)| format!("{}{:?}", if i > 0 { ", " } else { "" }, tc)).collect::<String>()
), ),
Error::NoSpendingKey(addr) => write!(f, "No spending key available for address: {}", addr), Error::NoSpendingKey(addr) => write!(f, "No spending key available for address: {}", addr),

View File

@ -894,7 +894,20 @@ where
let mut orchard_output_meta = vec![]; let mut orchard_output_meta = vec![];
let mut sapling_output_meta = vec![]; let mut sapling_output_meta = vec![];
let mut transparent_output_meta = vec![]; let mut transparent_output_meta = vec![];
for payment in proposal_step.transaction_request().payments().values() { for (payment, output_pool) in proposal_step
.payment_pools()
.iter()
.map(|(idx, output_pool)| {
let payment = proposal_step
.transaction_request()
.payments()
.get(idx)
.expect(
"The mapping between payment index and payment is checked in step construction",
);
(payment, output_pool)
})
{
match &payment.recipient_address { match &payment.recipient_address {
Address::Unified(ua) => { Address::Unified(ua) => {
let memo = payment let memo = payment
@ -902,54 +915,57 @@ where
.as_ref() .as_ref()
.map_or_else(MemoBytes::empty, |m| m.clone()); .map_or_else(MemoBytes::empty, |m| m.clone());
#[cfg(feature = "orchard")] match output_pool {
if let Some(orchard_receiver) = ua.orchard() { #[cfg(not(feature = "orchard"))]
builder.add_orchard_output( PoolType::Shielded(ShieldedProtocol::Orchard) => {
orchard_external_ovk.clone(), return Err(Error::ProposalNotSupported);
*orchard_receiver, }
payment.amount.into(), #[cfg(feature = "orchard")]
memo.clone(), PoolType::Shielded(ShieldedProtocol::Orchard) => {
)?; builder.add_orchard_output(
orchard_output_meta.push(( orchard_external_ovk.clone(),
Recipient::Unified( *ua.orchard().expect("The mapping between payment pool and receiver is checked in step construction"),
ua.clone(), payment.amount.into(),
PoolType::Shielded(ShieldedProtocol::Orchard), memo.clone(),
), )?;
payment.amount, orchard_output_meta.push((
Some(memo), Recipient::Unified(
)); ua.clone(),
continue; PoolType::Shielded(ShieldedProtocol::Orchard),
} ),
payment.amount,
Some(memo),
));
}
if let Some(sapling_receiver) = ua.sapling() { PoolType::Shielded(ShieldedProtocol::Sapling) => {
builder.add_sapling_output( builder.add_sapling_output(
sapling_external_ovk, sapling_external_ovk,
*sapling_receiver, *ua.sapling().expect("The mapping between payment pool and receiver is checked in step construction"),
payment.amount, payment.amount,
memo.clone(), memo.clone(),
)?; )?;
sapling_output_meta.push(( sapling_output_meta.push((
Recipient::Unified( Recipient::Unified(
ua.clone(), ua.clone(),
PoolType::Shielded(ShieldedProtocol::Sapling), PoolType::Shielded(ShieldedProtocol::Sapling),
), ),
payment.amount, payment.amount,
Some(memo), Some(memo),
)); ));
}
continue; PoolType::Transparent => {
} if payment.memo.is_some() {
return Err(Error::MemoForbidden);
if let Some(taddr) = ua.transparent() { } else {
if payment.memo.is_some() { builder.add_transparent_output(
return Err(Error::MemoForbidden); ua.transparent().expect("The mapping between payment pool and receiver is checked in step construction."),
} else { payment.amount
builder.add_transparent_output(taddr, payment.amount)?; )?;
continue; }
} }
} }
return Err(Error::NoSupportedReceivers(Box::new(ua.clone())));
} }
Address::Sapling(addr) => { Address::Sapling(addr) => {
let memo = payment let memo = payment

View File

@ -30,6 +30,9 @@ pub struct SingleOutputChangeStrategy {
impl SingleOutputChangeStrategy { impl SingleOutputChangeStrategy {
/// Constructs a new [`SingleOutputChangeStrategy`] with the specified fee rule /// Constructs a new [`SingleOutputChangeStrategy`] with the specified fee rule
/// and change memo. /// and change memo.
///
/// `fallback_change_pool` is used when more than one shielded pool is enabled via
/// feature flags, and the transaction has no shielded inputs.
pub fn new( pub fn new(
fee_rule: FixedFeeRule, fee_rule: FixedFeeRule,
change_memo: Option<MemoBytes>, change_memo: Option<MemoBytes>,

View File

@ -35,6 +35,9 @@ pub struct SingleOutputChangeStrategy {
impl SingleOutputChangeStrategy { impl SingleOutputChangeStrategy {
/// Constructs a new [`SingleOutputChangeStrategy`] with the specified ZIP 317 /// Constructs a new [`SingleOutputChangeStrategy`] with the specified ZIP 317
/// fee parameters. /// fee parameters.
///
/// `fallback_change_pool` is used when more than one shielded pool is enabled via
/// feature flags, and the transaction has no shielded inputs.
pub fn new( pub fn new(
fee_rule: StandardFeeRule, fee_rule: StandardFeeRule,
change_memo: Option<MemoBytes>, change_memo: Option<MemoBytes>,

View File

@ -34,6 +34,9 @@ pub struct SingleOutputChangeStrategy {
impl SingleOutputChangeStrategy { impl SingleOutputChangeStrategy {
/// Constructs a new [`SingleOutputChangeStrategy`] with the specified ZIP 317 /// Constructs a new [`SingleOutputChangeStrategy`] with the specified ZIP 317
/// fee parameters and change memo. /// fee parameters and change memo.
///
/// `fallback_change_pool` is used when more than one shielded pool is enabled via
/// feature flags, and the transaction has no shielded inputs.
pub fn new( pub fn new(
fee_rule: Zip317FeeRule, fee_rule: Zip317FeeRule,
change_memo: Option<MemoBytes>, change_memo: Option<MemoBytes>,

View File

@ -520,10 +520,10 @@ impl<NoteRef> Step<NoteRef> {
sapling_in || sapling_out || sapling_change sapling_in || sapling_out || sapling_change
} }
#[cfg(not(feature = "orchard"))]
PoolType::Shielded(ShieldedProtocol::Orchard) => false,
#[cfg(feature = "orchard")]
PoolType::Shielded(ShieldedProtocol::Orchard) => { PoolType::Shielded(ShieldedProtocol::Orchard) => {
#[cfg(not(feature = "orchard"))]
let orchard_in = false;
#[cfg(feature = "orchard")]
let orchard_in = self.shielded_inputs.iter().any(|s_in| { let orchard_in = self.shielded_inputs.iter().any(|s_in| {
s_in.notes() s_in.notes()
.iter() .iter()

View File

@ -402,34 +402,34 @@ impl<NoteRef> orchard_fees::InputView<NoteRef> for ReceivedNote<NoteRef, orchard
/// [ZIP 310]: https://zips.z.cash/zip-0310 /// [ZIP 310]: https://zips.z.cash/zip-0310
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum OvkPolicy { pub enum OvkPolicy {
/// Use the outgoing viewing key from the sender's [`ExtendedFullViewingKey`]. /// Use the outgoing viewing key from the sender's [`UnifiedFullViewingKey`].
/// ///
/// Transaction outputs will be decryptable by the sender, in addition to the /// Transaction outputs will be decryptable by the sender, in addition to the
/// recipients. /// recipients.
/// ///
/// [`ExtendedFullViewingKey`]: sapling::zip32::ExtendedFullViewingKey /// [`UnifiedFullViewingKey`]: zcash_keys::keys::UnifiedFullViewingKey
Sender, Sender,
/// Use a custom outgoing viewing key. This might for instance be derived from a /// Use custom outgoing viewing keys. These might for instance be derived from a
/// separate seed than the wallet's spending keys. /// different seed than the wallet's spending keys.
/// ///
/// Transaction outputs will be decryptable by the recipients, and whoever controls /// Transaction outputs will be decryptable by the recipients, and whoever controls
/// the provided outgoing viewing key. /// the provided outgoing viewing keys.
Custom { Custom {
sapling: sapling::keys::OutgoingViewingKey, sapling: sapling::keys::OutgoingViewingKey,
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
orchard: orchard::keys::OutgoingViewingKey, orchard: orchard::keys::OutgoingViewingKey,
}, },
/// Use no outgoing viewing key. Transaction outputs will be decryptable by their /// Use no outgoing viewing keys. Transaction outputs will be decryptable by their
/// recipients, but not by the sender. /// recipients, but not by the sender.
Discard, Discard,
} }
impl OvkPolicy { impl OvkPolicy {
/// Construct an [`OvkPolicy::Custom`] value from an arbitrary 32-byte key. /// Constructs an [`OvkPolicy::Custom`] value from a single arbitrary 32-byte key.
/// ///
/// Transactions constructed under this OVK policy will have their outputs /// Outputs of transactions created with this OVK policy will be recoverable using
/// recoverable using this key irrespective of the output pool. /// this key irrespective of the output pool.
pub fn custom_from_common_bytes(key: &[u8; 32]) -> Self { pub fn custom_from_common_bytes(key: &[u8; 32]) -> Self {
OvkPolicy::Custom { OvkPolicy::Custom {
sapling: sapling::keys::OutgoingViewingKey(*key), sapling: sapling::keys::OutgoingViewingKey(*key),

View File

@ -183,10 +183,18 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> InputSource for
fn get_spendable_note( fn get_spendable_note(
&self, &self,
txid: &TxId, txid: &TxId,
_protocol: ShieldedProtocol, protocol: ShieldedProtocol,
index: u32, index: u32,
) -> Result<Option<ReceivedNote<Self::NoteRef, Note>>, Self::Error> { ) -> Result<Option<ReceivedNote<Self::NoteRef, Note>>, Self::Error> {
wallet::sapling::get_spendable_sapling_note(self.conn.borrow(), &self.params, txid, index) match protocol {
ShieldedProtocol::Sapling => wallet::sapling::get_spendable_sapling_note(
self.conn.borrow(),
&self.params,
txid,
index,
),
ShieldedProtocol::Orchard => Ok(None),
}
} }
fn select_spendable_notes( fn select_spendable_notes(

View File

@ -193,7 +193,10 @@ impl UnifiedAddress {
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
let result = result.chain(self.orchard.map(|_| Typecode::Orchard)); let result = result.chain(self.orchard.map(|_| Typecode::Orchard));
let result = result.chain(self.sapling.map(|_| Typecode::Sapling)); let result = result.chain(self.sapling.map(|_| Typecode::Sapling));
let result = result.chain(self.transparent.map(|_| Typecode::P2pkh)); let result = result.chain(self.transparent.map(|taddr| match taddr {
TransparentAddress::PublicKeyHash(_) => Typecode::P2pkh,
TransparentAddress::ScriptHash(_) => Typecode::P2sh,
}));
let result = result.chain( let result = result.chain(
self.unknown() self.unknown()
.iter() .iter()