Add support for sending Orchard funds in `z_sendmany`
Closes zcash/zcash#5665.
This commit is contained in:
parent
d49c8a2865
commit
c2220f4eb9
|
@ -110,7 +110,8 @@ class WalletAccountsTest(BitcoinTestFramework):
|
||||||
self.check_balance(0, 0, ua0, {})
|
self.check_balance(0, 0, ua0, {})
|
||||||
self.check_balance(0, 1, ua1, {})
|
self.check_balance(0, 1, ua1, {})
|
||||||
|
|
||||||
# Manually send funds to one of the receivers in the UA.
|
# Send coinbase funds to the UA.
|
||||||
|
print('Sending coinbase funds to account')
|
||||||
recipients = [{'address': ua0, 'amount': Decimal('10')}]
|
recipients = [{'address': ua0, 'amount': Decimal('10')}]
|
||||||
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
|
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
|
||||||
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||||
|
@ -133,7 +134,8 @@ class WalletAccountsTest(BitcoinTestFramework):
|
||||||
# The default minconf should now detect the balance.
|
# The default minconf should now detect the balance.
|
||||||
self.check_balance(0, 0, ua0, {'sapling': 10})
|
self.check_balance(0, 0, ua0, {'sapling': 10})
|
||||||
|
|
||||||
# Manually send funds from the UA receiver.
|
# Send Sapling funds from the UA.
|
||||||
|
print('Sending account funds to Sapling address')
|
||||||
node1sapling = self.nodes[1].z_getnewaddress('sapling')
|
node1sapling = self.nodes[1].z_getnewaddress('sapling')
|
||||||
recipients = [{'address': node1sapling, 'amount': Decimal('1')}]
|
recipients = [{'address': node1sapling, 'amount': Decimal('1')}]
|
||||||
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
|
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
|
||||||
|
@ -154,17 +156,19 @@ class WalletAccountsTest(BitcoinTestFramework):
|
||||||
self.check_balance(0, 0, ua0, {'sapling': 9}, 0)
|
self.check_balance(0, 0, ua0, {'sapling': 9}, 0)
|
||||||
|
|
||||||
# Activate NU5
|
# Activate NU5
|
||||||
|
print('Activating NU5')
|
||||||
self.nodes[2].generate(9)
|
self.nodes[2].generate(9)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 210)
|
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 210)
|
||||||
|
|
||||||
# Send more coinbase funds to the UA.
|
# Send more coinbase funds to the UA.
|
||||||
|
print('Sending coinbase funds to account')
|
||||||
recipients = [{'address': ua0, 'amount': Decimal('10')}]
|
recipients = [{'address': ua0, 'amount': Decimal('10')}]
|
||||||
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
|
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
|
||||||
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||||
|
|
||||||
# The wallet should detect the new note as belonging to the UA.
|
# The wallet should detect the new note as belonging to the UA.
|
||||||
# TODO: Uncomment once z_viewtransaction shows Orchard details.
|
# TODO: Uncomment once z_viewtransaction shows Orchard details (#5186).
|
||||||
#tx_details = self.nodes[0].z_viewtransaction(txid)
|
#tx_details = self.nodes[0].z_viewtransaction(txid)
|
||||||
#assert_equal(len(tx_details['outputs']), 1)
|
#assert_equal(len(tx_details['outputs']), 1)
|
||||||
#assert_equal(tx_details['outputs'][0]['type'], 'orchard')
|
#assert_equal(tx_details['outputs'][0]['type'], 'orchard')
|
||||||
|
@ -183,6 +187,33 @@ class WalletAccountsTest(BitcoinTestFramework):
|
||||||
self.nodes[2].generate(1)
|
self.nodes[2].generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
|
# Send Orchard funds from the UA.
|
||||||
|
print('Sending account funds to Orchard-only UA')
|
||||||
|
node1account = self.nodes[1].z_getnewaccount()['account']
|
||||||
|
node1orchard = self.nodes[1].z_getaddressforaccount(node1account, ['orchard'])['unifiedaddress']
|
||||||
|
recipients = [{'address': node1orchard, 'amount': Decimal('1')}]
|
||||||
|
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
|
||||||
|
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||||
|
|
||||||
|
# The wallet should detect the spent note as belonging to the UA.
|
||||||
|
# TODO ORCHARD: Uncomment this once z_viewtransaction shows Orchard details (#5186).
|
||||||
|
# tx_details = self.nodes[0].z_viewtransaction(txid)
|
||||||
|
# assert_equal(len(tx_details['spends']), 1)
|
||||||
|
# assert_equal(tx_details['spends'][0]['type'], 'orchard')
|
||||||
|
# assert_equal(tx_details['spends'][0]['address'], ua0)
|
||||||
|
# assert_equal(len(tx_details['outputs']), 1)
|
||||||
|
# assert_equal(tx_details['outputs'][0]['type'], 'orchard')
|
||||||
|
# assert_equal(tx_details['outputs'][0]['address'], ua0)
|
||||||
|
|
||||||
|
# The balances of the account should reflect whether zero-conf transactions are
|
||||||
|
# being considered. The Sapling balance should remain at 9, while the Orchard
|
||||||
|
# balance will show either 0 (because the spent 10-ZEC note is never shown, as
|
||||||
|
# that transaction has been created and broadcast, and _might_ get mined up until
|
||||||
|
# the transaction expires), or 9 (if we include the unmined transaction).
|
||||||
|
self.sync_all()
|
||||||
|
self.check_balance(0, 0, ua0, {'sapling': 9})
|
||||||
|
self.check_balance(0, 0, ua0, {'sapling': 9, 'orchard': 9}, 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
WalletAccountsTest().main()
|
WalletAccountsTest().main()
|
||||||
|
|
|
@ -112,8 +112,10 @@ class WalletOrchardTest(BitcoinTestFramework):
|
||||||
self.nodes[2].generate(1)
|
self.nodes[2].generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
|
# The remaining change from ua2's Sapling note has been sent to the
|
||||||
|
# account's internal Orchard change address.
|
||||||
assert_equal(
|
assert_equal(
|
||||||
{'pools': {'sapling': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1},
|
{'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1},
|
||||||
self.nodes[2].z_getbalanceforaccount(acct2))
|
self.nodes[2].z_getbalanceforaccount(acct2))
|
||||||
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
|
@ -137,7 +139,7 @@ class WalletOrchardTest(BitcoinTestFramework):
|
||||||
# un-mined and returned to the mempool
|
# un-mined and returned to the mempool
|
||||||
assert_equal(set([rollback_tx]), set(self.nodes[2].getrawmempool()))
|
assert_equal(set([rollback_tx]), set(self.nodes[2].getrawmempool()))
|
||||||
|
|
||||||
# acct2's sole Sapling note is spent by a transaction in the mempool, so our
|
# acct2's sole Orchard note is spent by a transaction in the mempool, so our
|
||||||
# confirmed balance is currently 0
|
# confirmed balance is currently 0
|
||||||
assert_equal(
|
assert_equal(
|
||||||
{'pools': {}, 'minimum_confirmations': 1},
|
{'pools': {}, 'minimum_confirmations': 1},
|
||||||
|
@ -145,7 +147,7 @@ class WalletOrchardTest(BitcoinTestFramework):
|
||||||
|
|
||||||
# acct2's incoming change (unconfirmed, still in the mempool) is 9 zec
|
# acct2's incoming change (unconfirmed, still in the mempool) is 9 zec
|
||||||
assert_equal(
|
assert_equal(
|
||||||
{'pools': {'sapling': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 0},
|
{'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 0},
|
||||||
self.nodes[2].z_getbalanceforaccount(acct2, 0))
|
self.nodes[2].z_getbalanceforaccount(acct2, 0))
|
||||||
|
|
||||||
# The transaction was un-mined, so acct3 should have no confirmed balance
|
# The transaction was un-mined, so acct3 should have no confirmed balance
|
||||||
|
@ -168,7 +170,7 @@ class WalletOrchardTest(BitcoinTestFramework):
|
||||||
|
|
||||||
# The un-mined transaction should now have been re-mined
|
# The un-mined transaction should now have been re-mined
|
||||||
assert_equal(
|
assert_equal(
|
||||||
{'pools': {'sapling': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1},
|
{'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1},
|
||||||
self.nodes[2].z_getbalanceforaccount(acct2))
|
self.nodes[2].z_getbalanceforaccount(acct2))
|
||||||
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
|
|
|
@ -287,6 +287,10 @@ void TransactionBuilder::SetExpiryHeight(uint32_t nExpiryHeight)
|
||||||
mtx.nExpiryHeight = nExpiryHeight;
|
mtx.nExpiryHeight = nExpiryHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TransactionBuilder::SupportsOrchard() const {
|
||||||
|
return orchardBuilder.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
bool TransactionBuilder::AddOrchardSpend(
|
bool TransactionBuilder::AddOrchardSpend(
|
||||||
libzcash::OrchardSpendingKey sk,
|
libzcash::OrchardSpendingKey sk,
|
||||||
orchard::SpendInfo spendInfo)
|
orchard::SpendInfo spendInfo)
|
||||||
|
|
|
@ -342,6 +342,8 @@ public:
|
||||||
|
|
||||||
void SetFee(CAmount fee);
|
void SetFee(CAmount fee);
|
||||||
|
|
||||||
|
bool SupportsOrchard() const;
|
||||||
|
|
||||||
bool AddOrchardSpend(
|
bool AddOrchardSpend(
|
||||||
libzcash::OrchardSpendingKey sk,
|
libzcash::OrchardSpendingKey sk,
|
||||||
orchard::SpendInfo spendInfo);
|
orchard::SpendInfo spendInfo);
|
||||||
|
|
|
@ -78,26 +78,47 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
|
||||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||||
txOutputAmounts_.sapling_outputs_total += recipient.amount;
|
txOutputAmounts_.sapling_outputs_total += recipient.amount;
|
||||||
recipientPools_.insert(OutputPool::Sapling);
|
recipientPools_.insert(OutputPool::Sapling);
|
||||||
if (ztxoSelector_.SelectsSprout() && !allowRevealedAmounts_) {
|
if (!(ztxoSelector_.SelectsSapling() || allowRevealedAmounts_)) {
|
||||||
|
if (ztxoSelector_.SelectsSprout()) {
|
||||||
throw JSONRPCError(
|
throw JSONRPCError(
|
||||||
RPC_INVALID_PARAMETER,
|
RPC_INVALID_PARAMETER,
|
||||||
"Sending between shielded pools is not enabled by default because it will "
|
"Sending from the Sprout shielded pool to the Sapling "
|
||||||
|
"shielded pool is not enabled by default because it will "
|
||||||
"publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. "
|
"publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. "
|
||||||
"Resubmit with the `allowRevealedAmounts` parameter set to `true` if "
|
"Resubmit with the `allowRevealedAmounts` parameter set to `true` if "
|
||||||
"you wish to allow this transaction to proceed anyway.");
|
"you wish to allow this transaction to proceed anyway.");
|
||||||
}
|
}
|
||||||
},
|
if (builder_.SupportsOrchard() && ztxoSelector_.SelectsOrchard()) {
|
||||||
[&](const libzcash::OrchardRawAddress& addr) {
|
|
||||||
txOutputAmounts_.orchard_outputs_total += recipient.amount;
|
|
||||||
// TODO ORCHARD: Add to recipientPools_
|
|
||||||
if ((ztxoSelector_.SelectsSprout() || ztxoSelector_.SelectsSapling()) && !allowRevealedAmounts_) {
|
|
||||||
throw JSONRPCError(
|
throw JSONRPCError(
|
||||||
RPC_INVALID_PARAMETER,
|
RPC_INVALID_PARAMETER,
|
||||||
"Sending between shielded pools is not enabled by default because it will "
|
"Sending from the Orchard shielded pool to the Sapling "
|
||||||
|
"shielded pool is not enabled by default because it will "
|
||||||
"publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. "
|
"publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. "
|
||||||
"Resubmit with the `allowRevealedAmounts` parameter set to `true` if "
|
"Resubmit with the `allowRevealedAmounts` parameter set to `true` if "
|
||||||
"you wish to allow this transaction to proceed anyway.");
|
"you wish to allow this transaction to proceed anyway.");
|
||||||
}
|
}
|
||||||
|
// If the source selects transparent then we don't show an
|
||||||
|
// error because we are necessarily revealing information.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&](const libzcash::OrchardRawAddress& addr) {
|
||||||
|
txOutputAmounts_.orchard_outputs_total += recipient.amount;
|
||||||
|
recipientPools_.insert(OutputPool::Orchard);
|
||||||
|
// No transaction allows sends from Sprout to Orchard.
|
||||||
|
assert(!ztxoSelector_.SelectsSprout());
|
||||||
|
if (!((builder_.SupportsOrchard() && ztxoSelector_.SelectsOrchard()) || allowRevealedAmounts_)) {
|
||||||
|
if (ztxoSelector_.SelectsSapling()) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_INVALID_PARAMETER,
|
||||||
|
"Sending from the Sapling shielded pool to the Orchard "
|
||||||
|
"shielded pool is not enabled by default because it will "
|
||||||
|
"publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. "
|
||||||
|
"Resubmit with the `allowRevealedAmounts` parameter set to `true` if "
|
||||||
|
"you wish to allow this transaction to proceed anyway.");
|
||||||
|
}
|
||||||
|
// If the source selects transparent then we don't show an
|
||||||
|
// error because we are necessarily revealing information.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, recipient.address);
|
}, recipient.address);
|
||||||
}
|
}
|
||||||
|
@ -251,6 +272,9 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
for (const auto& t : spendable.saplingNoteEntries) {
|
for (const auto& t : spendable.saplingNoteEntries) {
|
||||||
z_inputs_total += t.note.value();
|
z_inputs_total += t.note.value();
|
||||||
}
|
}
|
||||||
|
for (const auto& t : spendable.orchardNoteMetadata) {
|
||||||
|
z_inputs_total += t.GetNoteValue();
|
||||||
|
}
|
||||||
|
|
||||||
if (z_inputs_total > 0 && mindepth_ == 0) {
|
if (z_inputs_total > 0 && mindepth_ == 0) {
|
||||||
throw JSONRPCError(
|
throw JSONRPCError(
|
||||||
|
@ -365,7 +389,9 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
allowedChangeTypes.insert(OutputPool::Sapling);
|
allowedChangeTypes.insert(OutputPool::Sapling);
|
||||||
break;
|
break;
|
||||||
case ReceiverType::Orchard:
|
case ReceiverType::Orchard:
|
||||||
// TODO
|
if (builder_.SupportsOrchard()) {
|
||||||
|
allowedChangeTypes.insert(OutputPool::Orchard);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,12 +428,28 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch Sapling anchor and witnesses
|
// Fetch Sapling anchor and witnesses, and Orchard Merkle paths.
|
||||||
uint256 anchor;
|
uint256 anchor;
|
||||||
std::vector<std::optional<SaplingWitness>> witnesses;
|
std::vector<std::optional<SaplingWitness>> witnesses;
|
||||||
|
std::vector<std::pair<libzcash::OrchardSpendingKey, orchard::SpendInfo>> orchardSpendInfo;
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||||
pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, witnesses, anchor);
|
pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, witnesses, anchor);
|
||||||
|
orchardSpendInfo = pwalletMain->GetOrchardSpendInfo(spendable.orchardNoteMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Orchard spends
|
||||||
|
for (size_t i = 0; i < orchardSpendInfo.size(); i++) {
|
||||||
|
auto spendInfo = std::move(orchardSpendInfo[i]);
|
||||||
|
if (!builder_.AddOrchardSpend(
|
||||||
|
std::move(spendInfo.first),
|
||||||
|
std::move(spendInfo.second)))
|
||||||
|
{
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_WALLET_ERROR,
|
||||||
|
"Failed to add Orchard note to transaction (check debug.log for details)"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Sapling spends
|
// Add Sapling spends
|
||||||
|
@ -424,7 +466,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
builder_.AddSaplingSpend(saplingKeys[i].expsk, saplingNotes[i], anchor, witnesses[i].value());
|
builder_.AddSaplingSpend(saplingKeys[i].expsk, saplingNotes[i], anchor, witnesses[i].value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Sapling and transparent outputs
|
// Add outputs
|
||||||
for (const auto& r : recipients_) {
|
for (const auto& r : recipients_) {
|
||||||
std::visit(match {
|
std::visit(match {
|
||||||
[&](const CKeyID& keyId) {
|
[&](const CKeyID& keyId) {
|
||||||
|
@ -508,7 +550,25 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs(const SpendableInputs& spendable) const {
|
std::pair<uint256, uint256> AsyncRPCOperation_sendmany::SelectOVKs(const SpendableInputs& spendable) const {
|
||||||
uint256 internalOVK;
|
uint256 internalOVK;
|
||||||
uint256 externalOVK;
|
uint256 externalOVK;
|
||||||
if (!spendable.saplingNoteEntries.empty()) {
|
if (!spendable.orchardNoteMetadata.empty()) {
|
||||||
|
std::optional<OrchardFullViewingKey> fvk;
|
||||||
|
std::visit(match {
|
||||||
|
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||||
|
fvk = ufvk.GetOrchardKey().value();
|
||||||
|
},
|
||||||
|
[&](const AccountZTXOPattern& acct) {
|
||||||
|
auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId());
|
||||||
|
fvk = ufvk.value().GetOrchardKey().value();
|
||||||
|
},
|
||||||
|
[&](const auto& other) {
|
||||||
|
throw std::runtime_error("unreachable");
|
||||||
|
}
|
||||||
|
}, this->ztxoSelector_.GetPattern());
|
||||||
|
assert(fvk.has_value());
|
||||||
|
|
||||||
|
internalOVK = fvk.value().ToInternalOutgoingViewingKey();
|
||||||
|
externalOVK = fvk.value().ToExternalOutgoingViewingKey();
|
||||||
|
} else if (!spendable.saplingNoteEntries.empty()) {
|
||||||
std::optional<SaplingDiversifiableFullViewingKey> dfvk;
|
std::optional<SaplingDiversifiableFullViewingKey> dfvk;
|
||||||
std::visit(match {
|
std::visit(match {
|
||||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||||
|
|
|
@ -1937,6 +1937,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
||||||
bool selectTransparent{selector.SelectsTransparent()};
|
bool selectTransparent{selector.SelectsTransparent()};
|
||||||
bool selectSprout{selector.SelectsSprout()};
|
bool selectSprout{selector.SelectsSprout()};
|
||||||
bool selectSapling{selector.SelectsSapling()};
|
bool selectSapling{selector.SelectsSapling()};
|
||||||
|
bool selectOrchard{selector.SelectsOrchard()};
|
||||||
|
|
||||||
SpendableInputs unspent;
|
SpendableInputs unspent;
|
||||||
for (auto const& [wtxid, wtx] : mapWallet) {
|
for (auto const& [wtxid, wtx] : mapWallet) {
|
||||||
|
@ -2063,6 +2064,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectOrchard) {
|
||||||
// for Orchard, we select both the internal and external IVKs.
|
// for Orchard, we select both the internal and external IVKs.
|
||||||
auto orchardIvks = std::visit(match {
|
auto orchardIvks = std::visit(match {
|
||||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) -> std::vector<OrchardIncomingViewingKey> {
|
[&](const libzcash::UnifiedFullViewingKey& ufvk) -> std::vector<OrchardIncomingViewingKey> {
|
||||||
|
@ -2102,6 +2104,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return unspent;
|
return unspent;
|
||||||
}
|
}
|
||||||
|
@ -6361,6 +6364,9 @@ NoteFilter NoteFilter::ForPaymentAddresses(const std::vector<libzcash::PaymentAd
|
||||||
[&](const libzcash::UnifiedAddress& uaddr) {
|
[&](const libzcash::UnifiedAddress& uaddr) {
|
||||||
for (auto& receiver : uaddr) {
|
for (auto& receiver : uaddr) {
|
||||||
std::visit(match {
|
std::visit(match {
|
||||||
|
[&](const libzcash::OrchardRawAddress& addr) {
|
||||||
|
addrs.orchardAddresses.insert(addr);
|
||||||
|
},
|
||||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||||
addrs.saplingAddresses.insert(addr);
|
addrs.saplingAddresses.insert(addr);
|
||||||
},
|
},
|
||||||
|
@ -6385,6 +6391,13 @@ bool CWallet::HasSpendingKeys(const NoteFilter& addrSet) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& addr : addrSet.GetOrchardAddresses()) {
|
||||||
|
if (!orchardWallet.GetSpendingKeyForAddress(addr).has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7068,6 +7081,13 @@ bool ZTXOSelector::SelectsSapling() const {
|
||||||
[](const auto& addr) { return false; }
|
[](const auto& addr) { return false; }
|
||||||
}, this->pattern);
|
}, this->pattern);
|
||||||
}
|
}
|
||||||
|
bool ZTXOSelector::SelectsOrchard() const {
|
||||||
|
return std::visit(match {
|
||||||
|
[](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetOrchardKey().has_value(); },
|
||||||
|
[](const AccountZTXOPattern& acct) { return acct.IncludesOrchard(); },
|
||||||
|
[](const auto& addr) { return false; }
|
||||||
|
}, this->pattern);
|
||||||
|
}
|
||||||
|
|
||||||
bool SpendableInputs::LimitToAmount(
|
bool SpendableInputs::LimitToAmount(
|
||||||
const CAmount amountRequired,
|
const CAmount amountRequired,
|
||||||
|
|
|
@ -758,6 +758,10 @@ public:
|
||||||
return receiverTypes.empty() || receiverTypes.count(libzcash::ReceiverType::Sapling) > 0;
|
return receiverTypes.empty() || receiverTypes.count(libzcash::ReceiverType::Sapling) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IncludesOrchard() const {
|
||||||
|
return receiverTypes.empty() || receiverTypes.count(libzcash::ReceiverType::Orchard) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
friend bool operator==(const AccountZTXOPattern &a, const AccountZTXOPattern &b) {
|
friend bool operator==(const AccountZTXOPattern &a, const AccountZTXOPattern &b) {
|
||||||
return a.accountId == b.accountId && a.receiverTypes == b.receiverTypes;
|
return a.accountId == b.accountId && a.receiverTypes == b.receiverTypes;
|
||||||
}
|
}
|
||||||
|
@ -798,6 +802,7 @@ public:
|
||||||
bool SelectsTransparent() const;
|
bool SelectsTransparent() const;
|
||||||
bool SelectsSprout() const;
|
bool SelectsSprout() const;
|
||||||
bool SelectsSapling() const;
|
bool SelectsSapling() const;
|
||||||
|
bool SelectsOrchard() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue