Apply suggestions from code review
Co-authored-by: str4d <thestr4d@gmail.com>
This commit is contained in:
parent
050a124cb6
commit
184286c430
|
@ -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`
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue