Merge pull request #5468 from nuttycom/merge/master_ua

Merge master branch back to the wallet/feature_unified_addresses branch.
This commit is contained in:
str4d 2022-01-13 14:15:15 +00:00 committed by GitHub
commit 6118432712
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1851 additions and 2627 deletions

View File

@ -58,3 +58,31 @@ RPC Changes
- The results of the 'getwalletinfo' RPC have been modified to return two new fields:
`mnemonic_seedfp` and `legacy_seedfp`, the latter of which replaces the field that
was previously named `seedfp`.
Wallet
------
'z_sendmany'
------------
- The 'z_sendmany' RPC call no longer permits Sprout recipients in the
list of recipient addresses. Transactions spending Sprout funds will
still result in change being sent back into the Sprout pool, but no
other `Sprout->Sprout` transactions will be constructed by the Zcashd
wallet.
- The restriction that prohibited `Sprout->Sapling` transactions has been
lifted; however, since such transactions reveal the amount crossing
pool boundaries, they must be explicitly enabled via a parameter to
the 'z_sendmany' call.
- A new boolean parameter, `allowRevealedAmounts`, has been added to the
list of arguments accepted by 'z_sendmany'. This parameter defaults to
`false` and is only required when the transaction being constructed
would reveal transaction amounts as a consequence of ZEC value crossing
shielded pool boundaries via the turnstile.
- Since Sprout outputs are no longer created (with the exception of change)
'z_sendmany' no longer generates payment disclosures (which were only
available for Sprout outputs) when the `-paymentdisclosure` experimental
feature flag is set.

View File

@ -62,7 +62,6 @@ BASE_SCRIPTS= [
'reorg_limit.py',
'mempool_limit.py',
'p2p-fullblocktest.py',
'paymentdisclosure.py',
# vv Tests less than 30s vv
'wallet_1941.py',
'wallet_addresses.py',

View File

@ -131,10 +131,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
# Mine a block with a Sprout shielded tx and verify the final Sapling root does not change
zaddr1 = self.nodes[1].z_getnewaddress('sprout')
recipients = []
recipients.append({"address": zaddr1, "amount": Decimal('10')})
myopid = self.nodes[0].z_sendmany(taddr0, recipients, 1, 0)
wait_and_assert_operationid_status(self.nodes[0], myopid)
result = self.nodes[0].z_shieldcoinbase(taddr0, zaddr1, 0, 1)
wait_and_assert_operationid_status(self.nodes[0], result['opid'])
self.sync_all()
self.nodes[0].generate(1)

View File

@ -29,8 +29,8 @@ class MergeToAddressMixedNotes(BitcoinTestFramework):
saplingAddr = self.nodes[0].z_getnewaddress('sapling')
t_addr = self.nodes[1].getnewaddress()
opid = self.nodes[0].z_sendmany(coinbase_addr, [{"address": sproutAddr, "amount": Decimal('10')}], 1, 0)
wait_and_assert_operationid_status(self.nodes[0], opid)
result = self.nodes[0].z_shieldcoinbase(coinbase_addr, sproutAddr, 0, 1)
wait_and_assert_operationid_status(self.nodes[0], result['opid'])
self.nodes[0].generate(1)
self.sync_all()
assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('10'))

View File

@ -1,218 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2017 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, \
start_node, connect_nodes_bi, wait_and_assert_operationid_status, \
get_coinbase_address, DEFAULT_FEE
from decimal import Decimal
class PaymentDisclosureTest (BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 3
self.setup_clean_chain = True
def setup_network(self, split=False):
args = ['-debug=zrpcunsafe,paymentdisclosure', '-experimentalfeatures', '-paymentdisclosure', '-txindex=1']
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, args))
self.nodes.append(start_node(1, self.options.tmpdir, args))
# node 2 does not enable payment disclosure
args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-txindex=1']
self.nodes.append(start_node(2, self.options.tmpdir, args2))
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
self.is_network_split=False
self.sync_all()
def run_test (self):
print("Mining blocks...")
self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40)
assert_equal(walletinfo['balance'], 0)
self.sync_all()
self.nodes[2].generate(3)
self.sync_all()
self.nodes[1].generate(101)
self.sync_all()
assert_equal(self.nodes[0].getbalance(), 40)
assert_equal(self.nodes[1].getbalance(), 10)
assert_equal(self.nodes[2].getbalance(), 30)
mytaddr = get_coinbase_address(self.nodes[0])
myzaddr = self.nodes[0].z_getnewaddress('sprout')
# Check that Node 2 has payment disclosure disabled.
try:
self.nodes[2].z_getpaymentdisclosure("invalidtxid", 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("payment disclosure is disabled" in errorString)
# Check that Node 0 returns an error for an unknown txid
try:
self.nodes[0].z_getpaymentdisclosure("invalidtxid", 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("No information available about transaction" in errorString)
# Shield coinbase utxos from node 0 of value 40, default fee
recipients = [{"address": myzaddr, "amount": Decimal('40.0') - DEFAULT_FEE}]
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
txid = wait_and_assert_operationid_status(self.nodes[0], myopid)
# Check the tx has joinsplits
assert( len(self.nodes[0].getrawtransaction("" + txid, 1)["vjoinsplit"]) > 0 )
# Sync mempools
self.sync_all()
# Confirm that you can't create a payment disclosure for an unconfirmed tx
try:
self.nodes[0].z_getpaymentdisclosure(txid, 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Transaction has not been confirmed yet" in errorString)
try:
self.nodes[1].z_getpaymentdisclosure(txid, 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Transaction has not been confirmed yet" in errorString)
# Mine tx
self.nodes[0].generate(1)
self.sync_all()
# Confirm that Node 1 cannot create a payment disclosure for a transaction which does not impact its wallet
try:
self.nodes[1].z_getpaymentdisclosure(txid, 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Transaction does not belong to the wallet" in errorString)
# Check that an invalid joinsplit index is rejected
try:
self.nodes[0].z_getpaymentdisclosure(txid, 1, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Invalid js_index" in errorString)
try:
self.nodes[0].z_getpaymentdisclosure(txid, -1, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Invalid js_index" in errorString)
# Check that an invalid output index is rejected
try:
self.nodes[0].z_getpaymentdisclosure(txid, 0, 2)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Invalid output_index" in errorString)
try:
self.nodes[0].z_getpaymentdisclosure(txid, 0, -1)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Invalid output_index" in errorString)
# Ask Node 0 to create and validate a payment disclosure for output 0
message = "Here is proof of my payment!"
pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, message)
result = self.nodes[0].z_validatepaymentdisclosure(pd)
assert(result["valid"])
output_value_sum = Decimal(result["value"])
# Ask Node 1 to confirm the payment disclosure is valid
result = self.nodes[1].z_validatepaymentdisclosure(pd)
assert(result["valid"])
assert_equal(result["message"], message)
assert_equal(result["value"], output_value_sum)
# Confirm that payment disclosure begins with prefix zpd:
assert(pd.startswith("zpd:"))
# Confirm that payment disclosure without prefix zpd: fails validation
try:
self.nodes[1].z_validatepaymentdisclosure(pd[4:])
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("payment disclosure prefix not found" in errorString)
# Check that total value of output index 0 and index 1 should equal shielding amount of 40 less standard fee.
pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 1)
result = self.nodes[0].z_validatepaymentdisclosure(pd)
output_value_sum += Decimal(result["value"])
assert_equal(output_value_sum, Decimal('40.0') - DEFAULT_FEE)
# Create a z->z transaction, sending shielded funds from node 0 to node 1
node1zaddr = self.nodes[1].z_getnewaddress('sprout')
recipients = [{"address":node1zaddr, "amount":Decimal('1')}]
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
txid = wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
# Confirm that Node 0 can create a valid payment disclosure
pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 0, "a message of your choice")
result = self.nodes[0].z_validatepaymentdisclosure(pd)
assert(result["valid"])
# Confirm that Node 1, even as recipient of shielded funds, cannot create a payment disclosure
# as the transaction was created by Node 0 and Node 1's payment disclosure database does not
# contain the necessary data to do so, where the data would only have been available on Node 0
# when executing z_shieldcoinbase.
try:
self.nodes[1].z_getpaymentdisclosure(txid, 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Could not find payment disclosure info for the given joinsplit output" in errorString)
# Payment disclosures cannot be created for transparent transactions.
txid = self.nodes[2].sendtoaddress(mytaddr, 1.0)
self.sync_all()
# No matter the type of transaction, if it has not been confirmed, it is ignored.
try:
self.nodes[0].z_getpaymentdisclosure(txid, 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Transaction has not been confirmed yet" in errorString)
self.nodes[0].generate(1)
self.sync_all()
# Confirm that a payment disclosure can only be generated for a shielded transaction.
try:
self.nodes[0].z_getpaymentdisclosure(txid, 0, 0)
assert(False)
except JSONRPCException as e:
errorString = e.error['message']
assert("Transaction is not a shielded transaction" in errorString)
if __name__ == '__main__':
PaymentDisclosureTest().main()

View File

@ -59,14 +59,6 @@ class RemoveSproutShieldingTest (BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
# Create taddr -> Sprout transaction and mine on node 0 before it is Canopy-aware. Should pass
sendmany_tx_0 = self.nodes[0].z_sendmany(taddr_0, [{"address": self.nodes[1].z_getnewaddress('sprout'), "amount": 1}])
wait_and_assert_operationid_status(self.nodes[0], sendmany_tx_0)
print("taddr -> Sprout z_sendmany tx accepted before Canopy on node 0")
self.nodes[0].generate(1)
self.sync_all()
# Create mergetoaddress taddr -> Sprout transaction and mine on node 0 before it is Canopy-aware. Should pass
merge_tx_0 = self.nodes[0].z_mergetoaddress(["ANY_TADDR"], self.nodes[1].z_getnewaddress('sprout'))
wait_and_assert_operationid_status(self.nodes[0], merge_tx_0['opid'])
@ -75,7 +67,7 @@ class RemoveSproutShieldingTest (BitcoinTestFramework):
# Mine to one block before Canopy activation on node 0; adding value
# to the Sprout pool will fail now since the transaction must be
# included in the next (or later) block, after Canopy has activated.
self.nodes[0].generate(4)
self.nodes[0].generate(5)
self.sync_all()
# Shield coinbase to Sprout on node 0. Should fail
@ -91,7 +83,7 @@ class RemoveSproutShieldingTest (BitcoinTestFramework):
sprout_addr = self.nodes[1].z_getnewaddress('sprout')
assert_raises_message(
JSONRPCException,
"Sprout shielding is not supported after Canopy",
"Sending funds into the Sprout pool is not supported by z_sendmany",
self.nodes[0].z_sendmany,
taddr_0, [{"address": sprout_addr, "amount": 1}])
print("taddr -> Sprout z_sendmany tx rejected at Canopy activation on node 0")

View File

@ -158,8 +158,8 @@ class SproutSaplingMigration(BitcoinTestFramework):
def send_to_sprout_zaddr(self, tAddr, sproutAddr):
# Send some ZEC to a Sprout address
opid = self.nodes[0].z_sendmany(tAddr, [{"address": sproutAddr, "amount": Decimal('10')}], 1, 0)
wait_and_assert_operationid_status(self.nodes[0], opid)
result = self.nodes[0].z_shieldcoinbase(tAddr, sproutAddr, 0, 1)
wait_and_assert_operationid_status(self.nodes[0], result['opid'])
self.nodes[0].generate(1)
self.sync_all()

View File

@ -29,6 +29,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
fail,
get_coinbase_address,
start_node, start_nodes,
sync_blocks, sync_mempools,
@ -88,9 +89,14 @@ class TurnstileTest (BitcoinTestFramework):
# Node 0 shields some funds
dest_addr = self.nodes[0].z_getnewaddress(POOL_NAME.lower())
taddr0 = get_coinbase_address(self.nodes[0])
recipients = []
recipients.append({"address": dest_addr, "amount": Decimal('10')})
myopid = self.nodes[0].z_sendmany(taddr0, recipients, 1, 0)
if (POOL_NAME == "SPROUT"):
myopid = self.nodes[0].z_shieldcoinbase(taddr0, dest_addr, 0, 1)['opid']
elif (POOL_NAME == "SAPLING"):
recipients = []
recipients.append({"address": dest_addr, "amount": Decimal('10')})
myopid = self.nodes[0].z_sendmany(taddr0, recipients, 1, 0)
else:
fail("Unrecognized pool name: " + POOL_NAME)
wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
self.nodes[0].generate(1)

View File

@ -81,15 +81,11 @@ class WalletChangeAddressesTest(BitcoinTestFramework):
taddr = self.nodes[0].getnewaddress()
saplingAddr = self.nodes[0].z_getnewaddress('sapling')
sproutAddr = self.nodes[0].z_getnewaddress('sprout')
print()
print('Checking z_sendmany(taddr->Sapling)')
check_change_taddr_reuse(saplingAddr)
print()
print('Checking z_sendmany(taddr->Sprout)')
check_change_taddr_reuse(sproutAddr)
print()
print('Checking z_sendmany(taddr->taddr)')
check_change_taddr_reuse(taddr)

View File

@ -6,7 +6,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
get_coinbase_address,
wait_and_assert_operationid_status,
DEFAULT_FEE
)
@ -20,7 +19,6 @@ class WalletListNotes(BitcoinTestFramework):
# Current height = 200
assert_equal(200, self.nodes[0].getblockcount())
sproutzaddr = self.nodes[0].z_getnewaddress('sprout')
saplingzaddr = self.nodes[0].z_getnewaddress('sapling')
# we've got lots of coinbase (taddr) but no shielded funds yet
assert_equal(0, Decimal(self.nodes[0].z_gettotalbalance()['private']))
@ -30,11 +28,10 @@ class WalletListNotes(BitcoinTestFramework):
self.sync_all()
assert_equal(201, self.nodes[0].getblockcount())
# Shield coinbase funds (must be a multiple of 10, no change allowed)
receive_amount_10 = Decimal('10.0') - DEFAULT_FEE
recipients = [{"address":sproutzaddr, "amount":receive_amount_10}]
myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients)
txid_1 = wait_and_assert_operationid_status(self.nodes[0], myopid)
# Shield one coinbase output
receive_amount_1 = Decimal('10.0') - DEFAULT_FEE
result = self.nodes[0].z_shieldcoinbase('*', sproutzaddr, DEFAULT_FEE, 1)
txid_1 = wait_and_assert_operationid_status(self.nodes[0], result['opid'])
self.sync_all()
# No funds (with (default) one or more confirmations) in sproutzaddr yet
@ -51,7 +48,7 @@ class WalletListNotes(BitcoinTestFramework):
assert_equal(txid_1, unspent_cb[0]['txid'])
assert_equal(True, unspent_cb[0]['spendable'])
assert_equal(sproutzaddr, unspent_cb[0]['address'])
assert_equal(receive_amount_10, unspent_cb[0]['amount'])
assert_equal(receive_amount_1, unspent_cb[0]['amount'])
# list unspent, filtering by address, should produce same result
unspent_cb_filter = self.nodes[0].z_listunspent(0, 9999, False, [sproutzaddr])
@ -64,13 +61,13 @@ class WalletListNotes(BitcoinTestFramework):
# Current height = 202
assert_equal(202, self.nodes[0].getblockcount())
# Send 1.0 minus default fee from sproutzaddr to a new zaddr
sproutzaddr2 = self.nodes[0].z_getnewaddress('sprout')
receive_amount_1 = Decimal('1.0') - DEFAULT_FEE
change_amount_9 = receive_amount_10 - Decimal('1.0')
assert_equal('sprout', self.nodes[0].z_validateaddress(sproutzaddr2)['type'])
recipients = [{"address": sproutzaddr2, "amount":receive_amount_1}]
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients)
# Send 1.0 minus default fee from sproutzaddr to a new Sapling zaddr
saplingzaddr = self.nodes[0].z_getnewaddress('sapling')
receive_amount_2 = Decimal('1.0')
change_amount_2 = receive_amount_1 - receive_amount_2 - DEFAULT_FEE
assert_equal('sapling', self.nodes[0].z_validateaddress(saplingzaddr)['type'])
recipients = [{"address": saplingzaddr, "amount":receive_amount_2}]
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients, 1, DEFAULT_FEE, True)
txid_2 = wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
@ -82,16 +79,16 @@ class WalletListNotes(BitcoinTestFramework):
assert_equal(False, unspent_tx[0]['change'])
assert_equal(txid_2, unspent_tx[0]['txid'])
assert_equal(True, unspent_tx[0]['spendable'])
assert_equal(sproutzaddr2, unspent_tx[0]['address'])
assert_equal(receive_amount_1, unspent_tx[0]['amount'])
assert_equal(saplingzaddr, unspent_tx[0]['address'])
assert_equal(receive_amount_2, unspent_tx[0]['amount'])
assert_equal(True, unspent_tx[1]['change'])
assert_equal(txid_2, unspent_tx[1]['txid'])
assert_equal(True, unspent_tx[1]['spendable'])
assert_equal(sproutzaddr, unspent_tx[1]['address'])
assert_equal(change_amount_9, unspent_tx[1]['amount'])
assert_equal(change_amount_2, unspent_tx[1]['amount'])
unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, [sproutzaddr2])
unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, [saplingzaddr])
assert_equal(1, len(unspent_tx_filter))
assert_equal(unspent_tx[0], unspent_tx_filter[0])
@ -99,15 +96,15 @@ class WalletListNotes(BitcoinTestFramework):
assert_equal(1, len(unspent_tx_filter))
assert_equal(unspent_tx[1], unspent_tx_filter[0])
# No funds in saplingzaddr yet
assert_equal(0, len(self.nodes[0].z_listunspent(0, 9999, False, [saplingzaddr])))
self.nodes[0].generate(1)
self.sync_all()
# Send 2.0 minus default fee to our sapling zaddr
# (sending from a sprout zaddr to a sapling zaddr is disallowed,
# so send from coin base)
receive_amount_2 = Decimal('2.0') - DEFAULT_FEE
recipients = [{"address": saplingzaddr, "amount":receive_amount_2}]
myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients)
# Send 2.0 minus default fee to a new sapling zaddr
saplingzaddr2 = self.nodes[0].z_getnewaddress('sapling')
receive_amount_3 = Decimal('2.0')
change_amount_3 = change_amount_2 - receive_amount_3 - DEFAULT_FEE
recipients = [{"address": saplingzaddr2, "amount":receive_amount_3}]
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients, 1, DEFAULT_FEE, True)
txid_3 = wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
unspent_tx = self.nodes[0].z_listunspent(0)
@ -119,31 +116,31 @@ class WalletListNotes(BitcoinTestFramework):
assert_equal(False, unspent_tx[0]['change'])
assert_equal(txid_2, unspent_tx[0]['txid'])
assert_equal(True, unspent_tx[0]['spendable'])
assert_equal(sproutzaddr2, unspent_tx[0]['address'])
assert_equal(receive_amount_1, unspent_tx[0]['amount'])
assert_equal(saplingzaddr, unspent_tx[0]['address'])
assert_equal(receive_amount_2, unspent_tx[0]['amount'])
assert_equal(False, unspent_tx[1]['change'])
assert_equal(txid_3, unspent_tx[1]['txid'])
assert_equal(True, unspent_tx[1]['spendable'])
assert_equal(saplingzaddr, unspent_tx[1]['address'])
assert_equal(receive_amount_2, unspent_tx[1]['amount'])
assert_equal(saplingzaddr2, unspent_tx[1]['address'])
assert_equal(receive_amount_3, unspent_tx[1]['amount'])
assert_equal(True, unspent_tx[2]['change'])
assert_equal(txid_2, unspent_tx[2]['txid'])
assert_equal(txid_3, unspent_tx[2]['txid'])
assert_equal(True, unspent_tx[2]['spendable'])
assert_equal(sproutzaddr, unspent_tx[2]['address'])
assert_equal(change_amount_9, unspent_tx[2]['amount'])
assert_equal(change_amount_3, unspent_tx[2]['amount'])
unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, [saplingzaddr])
assert_equal(1, len(unspent_tx_filter))
assert_equal(unspent_tx[1], unspent_tx_filter[0])
assert_equal(unspent_tx[0], unspent_tx_filter[0])
# test that pre- and post-sapling can be filtered in a single call
unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False,
[sproutzaddr, saplingzaddr])
assert_equal(2, len(unspent_tx_filter))
unspent_tx_filter = sorted(unspent_tx_filter, key=lambda k: k['amount'])
assert_equal(unspent_tx[1], unspent_tx_filter[0])
assert_equal(unspent_tx[0], unspent_tx_filter[0])
assert_equal(unspent_tx[2], unspent_tx_filter[1])
# so far, this node has no watchonly addresses, so results are the same

View File

@ -226,7 +226,7 @@ class ListReceivedTest (BitcoinTestFramework):
assert_equal(3, c[release], "Count of unconfirmed notes should be 3(2 in zaddr1 + 1 in zaddr2)")
def run_test(self):
self.run_test_release('sprout', 200)
#self.run_test_release('sprout', 200)
self.run_test_release('sapling', 214)
if __name__ == '__main__':

View File

@ -72,10 +72,8 @@ class WalletOverwinterTxTest (BitcoinTestFramework):
# Node 0 shields to Node 2, a coinbase utxo of value 10.0 less default fee
zsendamount = Decimal('10.0') - DEFAULT_FEE
recipients = []
recipients.append({"address":zaddr2, "amount": zsendamount})
myopid = self.nodes[0].z_sendmany(taddr0, recipients)
txid_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
result = self.nodes[0].z_shieldcoinbase(taddr0, zaddr2, DEFAULT_FEE, 1)
txid_shielded = wait_and_assert_operationid_status(self.nodes[0], result['opid'])
# Skip over the three blocks prior to activation; no transactions can be mined
# in them due to the nearly-expiring restrictions.
@ -143,10 +141,8 @@ class WalletOverwinterTxTest (BitcoinTestFramework):
# Node 0 shields to Node 3, a coinbase utxo of value 10.0 less default fee
zsendamount = Decimal('10.0') - DEFAULT_FEE
recipients = []
recipients.append({"address":zaddr3, "amount": zsendamount})
myopid = self.nodes[0].z_sendmany(taddr0, recipients)
txid_shielded = wait_and_assert_operationid_status(self.nodes[0], myopid)
result = self.nodes[0].z_shieldcoinbase(taddr0, zaddr3, DEFAULT_FEE, 1)
txid_shielded = wait_and_assert_operationid_status(self.nodes[0], result['opid'])
# Mine the first Blossom block
self.sync_all()

View File

@ -169,7 +169,7 @@ class WalletSaplingTest(BitcoinTestFramework):
)
raise AssertionError("Should have thrown an exception")
except JSONRPCException as e:
assert_equal("Cannot send to both Sprout and Sapling addresses using z_sendmany", e.error['message'])
assert_equal("Sending funds into the Sprout pool is not supported by z_sendmany", e.error['message'])
if __name__ == '__main__':
WalletSaplingTest().main()

View File

@ -26,7 +26,7 @@ class WalletSendManyAnyTaddr(BitcoinTestFramework):
self.nodes[3],
self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 100}]),
'failed',
'Could not find any non-coinbase UTXOs to spend. Coinbase UTXOs can only be sent to a single zaddr recipient from a single taddr.',
'Insufficient funds: have 0.00, need 100.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.',
)
# Prepare some non-coinbase UTXOs

View File

@ -151,7 +151,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
assert_equal(result["remainingUTXOs"], Decimal('0'))
opid2 = result['opid']
# wait for both aysnc operations to complete
# wait for both async operations to complete
wait_and_assert_operationid_status(self.nodes[0], opid1)
wait_and_assert_operationid_status(self.nodes[0], opid2)

View File

@ -61,10 +61,10 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), 0)
assert_equal(self.nodes[3].getbalance(), 0)
check_value_pool(self.nodes[0], 'sprout', 0)
check_value_pool(self.nodes[1], 'sprout', 0)
check_value_pool(self.nodes[2], 'sprout', 0)
check_value_pool(self.nodes[3], 'sprout', 0)
check_value_pool(self.nodes[0], 'sapling', 0)
check_value_pool(self.nodes[1], 'sapling', 0)
check_value_pool(self.nodes[2], 'sapling', 0)
check_value_pool(self.nodes[3], 'sapling', 0)
# Send will fail because we are enforcing the consensus rule that
# coinbase utxos can only be sent to a zaddr.
@ -77,24 +77,31 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# Prepare to send taddr->zaddr
mytaddr = get_coinbase_address(self.nodes[0])
myzaddr = self.nodes[0].z_getnewaddress('sprout')
myzaddr = self.nodes[0].z_getnewaddress('sapling')
# Node 3 will test that watch only address utxos are not selected
self.nodes[3].importaddress(mytaddr)
recipients= [{"address":myzaddr, "amount": Decimal('1')}]
myopid = self.nodes[3].z_sendmany(mytaddr, recipients)
try:
myopid = self.nodes[3].z_sendmany(mytaddr, recipients)
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Invalid from address, no spending key found for address", errorString);
wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient transparent funds, no UTXOs found for taddr from address.", 10)
# This send will fail because our wallet does not allow any change when shielding a coinbase utxo,
# as it's currently not possible to specify a change address in z_sendmany.
# This send will fail because our consensus does not allow transparent change when
# shielding a coinbase utxo.
# TODO: After upgrading to unified address support, change will be sent to the most
# recent shielded spend authority corresponding to the account of the source address
# and this send will succeed, causing this test to fail.
recipients = []
recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')})
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
error_result = wait_and_assert_operationid_status_result(self.nodes[0], myopid, "failed", ("Change 8.76542211 not allowed. "
"When shielding coinbase funds, the wallet does not allow any change "
"as there is currently no way to specify a change address in z_sendmany."), 10)
error_result = wait_and_assert_operationid_status_result(
self.nodes[0],
myopid, "failed",
"When shielding coinbase funds, the wallet does not allow any change. The proposed transaction would result in 8.76542211 in change.",
10)
# Test that the returned status object contains a params field with the operation's input parameters
assert_equal(error_result["method"], "z_sendmany")
@ -151,7 +158,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
results = self.nodes[1].z_listunspent(1, 999, False, [myzaddr])
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Invalid parameter, spending key for address does not belong to wallet" in errorString, True)
assert_equal("Invalid parameter, spending key for an address does not belong to the wallet.", errorString)
# Verify that debug=zrpcunsafe logs params, and that full txid is associated with opid
initialized_line = check_node_log(self, 0, myopid + ": z_sendmany initialized", False)
@ -165,8 +172,8 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
assert_equal(Decimal(resp["total"]), Decimal('40.0') - DEFAULT_FEE)
# The Sprout value pool should reflect the send
sproutvalue = shieldvalue
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
saplingvalue = shieldvalue
check_value_pool(self.nodes[0], 'sapling', saplingvalue)
# A custom fee of 0 is okay. Here the node will send the note value back to itself.
recipients = []
@ -181,8 +188,8 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
assert_equal(Decimal(resp["private"]), Decimal('20.0') - DEFAULT_FEE)
assert_equal(Decimal(resp["total"]), Decimal('40.0') - DEFAULT_FEE)
# The Sprout value pool should be unchanged
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
# The Sapling value pool should be unchanged
check_value_pool(self.nodes[0], 'sapling', saplingvalue)
# convert note to transparent funds
unshieldvalue = Decimal('10.0')
@ -201,12 +208,12 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
self.sync_all()
# check balances
sproutvalue -= unshieldvalue + DEFAULT_FEE
saplingvalue -= unshieldvalue + DEFAULT_FEE
resp = self.nodes[0].z_gettotalbalance()
assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
assert_equal(Decimal(resp["private"]), Decimal('10.0') - 2*DEFAULT_FEE)
assert_equal(Decimal(resp["total"]), Decimal('40.0') - 2*DEFAULT_FEE)
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
check_value_pool(self.nodes[0], 'sapling', saplingvalue)
# z_sendmany will return an error if there is transparent change output considered dust.
# UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first.
@ -215,7 +222,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
amount = Decimal('10.0') - DEFAULT_FEE - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold
recipients.append({"address":self.nodes[0].getnewaddress(), "amount":amount })
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)")
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)")
# Send will fail because send amount is too big, even when including coinbase utxos
errorString = ""
@ -229,9 +236,9 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
recipients = []
recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000.0')})
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 10.00, need 10000.00001")
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.")
myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient shielded funds, have 9.99998, need 10000.00001")
wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 9.99998, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.")
# Send will fail because of insufficient funds unless sender uses coinbase utxos
try:
@ -282,9 +289,9 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# check balance
node2balance = amount_per_recipient * num_t_recipients
sproutvalue -= node2balance + DEFAULT_FEE
saplingvalue -= node2balance + DEFAULT_FEE
assert_equal(self.nodes[2].getbalance(), node2balance)
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
check_value_pool(self.nodes[0], 'sapling', saplingvalue)
# Send will fail because fee is negative
try:
@ -330,7 +337,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
custom_fee = Decimal('0.00012345')
zbalance = self.nodes[0].z_getbalance(myzaddr)
for i in range(0,num_recipients):
newzaddr = self.nodes[2].z_getnewaddress('sprout')
newzaddr = self.nodes[2].z_getnewaddress('sapling')
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee)
wait_and_assert_operationid_status(self.nodes[0], myopid)
@ -348,8 +355,8 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
resp = self.nodes[0].z_getbalance(myzaddr)
assert_equal(Decimal(resp), zbalance - custom_fee - send_amount)
sproutvalue -= custom_fee
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
saplingvalue -= custom_fee
check_value_pool(self.nodes[0], 'sapling', saplingvalue)
notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
sum_of_notes = sum([note["amount"] for note in notes])

View File

@ -172,7 +172,7 @@ def wait_for_balance(zcash, zaddr, expected=None):
ttl = 300
def wait_and_check_balance(results, case, zcash, addr, expected):
#Wait for aysnc call to finish and persist completely to caller
#Wait for async call to finish and persist completely to caller
time.sleep(5)
balance = wait_for_balance(zcash, addr, expected)
if balance != expected and results is not None and len(case) > 0:

View File

@ -825,10 +825,10 @@ CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const {
assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight(nHeight));
KeyIO keyIO(*this);
CTxDestination address = keyIO.DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(IsValidDestination(address));
assert(IsScriptDestination(address));
CScriptID scriptID = std::get<CScriptID>(address); // address is a variant
auto address = keyIO.DecodePaymentAddress(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(address.has_value());
assert(std::holds_alternative<CScriptID>(address.value()));
CScriptID scriptID = std::get<CScriptID>(address.value());
CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return script;
}

View File

@ -9,6 +9,7 @@
#include <script/standard.h>
#include "upgrades.h"
#include "util.h"
#include "util/match.h"
namespace Consensus {
bool Params::NetworkUpgradeActive(int nHeight, Consensus::UpgradeIndex idx) const {
@ -163,17 +164,26 @@ namespace Consensus {
// Parse the address strings into concrete types.
std::vector<FundingStreamAddress> addresses;
for (auto addr : strAddresses) {
auto taddr = keyIO.DecodeDestination(addr);
if (IsValidDestination(taddr)) {
addresses.push_back(GetScriptForDestination(taddr));
} else {
auto zaddr = keyIO.DecodePaymentAddress(addr);
// If the string is not a valid transparent or Sapling address, we will
// throw here.
addresses.push_back(std::get<libzcash::SaplingPaymentAddress>(zaddr));
for (const auto& strAddr : strAddresses) {
auto addr = keyIO.DecodePaymentAddress(strAddr);
if (!addr.has_value()) {
throw std::runtime_error("Funding stream address was not a valid " PACKAGE_NAME " address.");
}
std::visit(match {
[&](const CKeyID& keyId) {
addresses.push_back(GetScriptForDestination(keyId));
},
[&](const CScriptID& scriptId) {
addresses.push_back(GetScriptForDestination(scriptId));
},
[&](const libzcash::SaplingPaymentAddress& zaddr) {
addresses.push_back(zaddr);
},
[&](const auto& zaddr) {
throw std::runtime_error("Funding stream address was not a valid transparent P2SH or Sapling address.");
}
}, addr.value());
}
auto validationResult = FundingStream::ValidateFundingStream(params, startHeight, endHeight, addresses);

View File

@ -28,11 +28,11 @@ TEST(Keys, EncodeAndDecodeSapling)
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY));
auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string);
EXPECT_TRUE(IsValidSpendingKey(spendingkey2));
EXPECT_TRUE(spendingkey2.has_value());
ASSERT_TRUE(std::get_if<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = std::get<libzcash::SaplingExtendedSpendingKey>(spendingkey2);
EXPECT_EQ(sk, sk2);
auto sk2 = std::get_if<libzcash::SaplingExtendedSpendingKey>(&spendingkey2.value());
EXPECT_NE(sk2, nullptr);
EXPECT_EQ(sk, *sk2);
}
{
auto extfvk = sk.ToXFVK();
@ -42,11 +42,11 @@ TEST(Keys, EncodeAndDecodeSapling)
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK));
auto viewingkey2 = keyIO.DecodeViewingKey(vk_string);
EXPECT_TRUE(IsValidViewingKey(viewingkey2));
EXPECT_TRUE(viewingkey2.has_value());
ASSERT_TRUE(std::get_if<libzcash::SaplingExtendedFullViewingKey>(&viewingkey2) != nullptr);
auto extfvk2 = std::get<libzcash::SaplingExtendedFullViewingKey>(viewingkey2);
EXPECT_EQ(extfvk, extfvk2);
auto extfvk2 = std::get_if<libzcash::SaplingExtendedFullViewingKey>(&viewingkey2.value());
EXPECT_NE(extfvk2, nullptr);
EXPECT_EQ(extfvk, *extfvk2);
}
{
auto addr = sk.ToXFVK().DefaultAddress();
@ -57,11 +57,11 @@ TEST(Keys, EncodeAndDecodeSapling)
Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS));
auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string);
EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2));
EXPECT_TRUE(paymentaddr2.has_value());
ASSERT_TRUE(std::get_if<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = std::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
EXPECT_EQ(addr, addr2);
auto addr2 = std::get_if<libzcash::SaplingPaymentAddress>(&paymentaddr2.value());
EXPECT_NE(addr2, nullptr);
EXPECT_EQ(addr, *addr2);
}
}
}
@ -152,8 +152,10 @@ TEST(Keys, EncodeAndDecodeUnifiedAddresses)
std::string expected(expectedBytes.begin(), expectedBytes.end());
auto decoded = keyIO.DecodePaymentAddress(expected);
ASSERT_TRUE(std::holds_alternative<libzcash::UnifiedAddress>(decoded));
EXPECT_EQ(std::get<libzcash::UnifiedAddress>(decoded), ua);
EXPECT_TRUE(decoded.has_value());
auto ua_ptr = std::get_if<libzcash::UnifiedAddress>(&decoded.value());
EXPECT_NE(ua_ptr, nullptr);
EXPECT_EQ(*ua_ptr, ua);
auto encoded = keyIO.EncodePaymentAddress(ua);
EXPECT_EQ(encoded, expected);

View File

@ -1104,16 +1104,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
KeyIO keyIO(chainparams);
#ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) {
CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]);
if (!IsValidDestination(addr)) {
// Try a payment address
auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!std::visit(IsValidMinerAddress(), std::visit(ExtractMinerAddress(), zaddr)))
{
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a Sapling or transparent address)"),
mapArgs["-mineraddress"]));
}
auto addr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!(addr.has_value() && std::visit(ExtractMinerAddress(), addr.value()).has_value())) {
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a Sapling or transparent P2PKH address)"),
mapArgs["-mineraddress"]));
}
}
#endif
@ -1684,15 +1679,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef ENABLE_WALLET
bool minerAddressInLocalWallet = false;
if (pwalletMain) {
CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]);
if (IsValidDestination(addr)) {
CKeyID keyID = std::get<CKeyID>(addr);
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
} else {
auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
minerAddressInLocalWallet = std::visit(
HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!zaddr.has_value()) {
return InitError(_("-mineraddress is not a valid " PACKAGE_NAME " address."));
}
minerAddressInLocalWallet = std::visit(
HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value());
}
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
return InitError(_("-mineraddress is not in the local wallet. Either use a local address, or set -minetolocalwallet=0"));

View File

@ -16,6 +16,7 @@
#include <string.h>
#include <algorithm>
#include <variant>
#include "util/match.h"
namespace
{
@ -113,6 +114,14 @@ private:
public:
PaymentAddressEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {}
std::string operator()(const CKeyID& id) const
{
return DestinationEncoder(keyConstants)(id);
}
std::string operator()(const CScriptID& id) const
{
return DestinationEncoder(keyConstants)(id);
}
std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -151,8 +160,6 @@ public:
zcash_address_string_free(encoded);
return res;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
class ViewingKeyEncoder
@ -193,8 +200,6 @@ public:
std::string operator()(const libzcash::UnifiedFullViewingKey& ufvk) const {
return ufvk.Encode(keyConstants);
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
class SpendingKeyEncoder
@ -231,8 +236,6 @@ public:
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
// Sizes of SaplingPaymentAddress, SaplingExtendedFullViewingKey, and
@ -359,17 +362,16 @@ std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
return std::visit(PaymentAddressEncoder(keyConstants), zaddr);
}
template<typename T1, typename T2, typename T3>
T1 DecodeAny(
const KeyConstants& keyConstants,
const std::string& str,
std::pair<KeyConstants::Base58Type, size_t> sprout,
std::pair<KeyConstants::Bech32Type, size_t> sapling)
template<typename T1, typename T2>
std::optional<T1> DecodeSprout(
const KeyConstants& keyConstants,
const std::string& str,
const std::pair<KeyConstants::Base58Type, size_t>& keyMeta)
{
std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(sprout.first);
if ((data.size() == sprout.second + prefix.size()) &&
const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(keyMeta.first);
if ((data.size() == keyMeta.second + prefix.size()) &&
std::equal(prefix.begin(), prefix.end(), data.begin())) {
CSerializeData serialized(data.begin() + prefix.size(), data.end());
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
@ -381,15 +383,26 @@ T1 DecodeAny(
}
}
data.clear();
memory_cleanse(data.data(), data.size());
return std::nullopt;
}
template<typename T1, typename T2>
std::optional<T1> DecodeSapling(
const KeyConstants& keyConstants,
const std::string& str,
const std::pair<KeyConstants::Bech32Type, size_t>& keyMeta)
{
std::vector<unsigned char> data;
auto bech = bech32::Decode(str);
if (bech.first == keyConstants.Bech32HRP(sapling.first) &&
bech.second.size() == sapling.second) {
if (bech.first == keyConstants.Bech32HRP(keyMeta.first) &&
bech.second.size() == keyMeta.second) {
// Bech32 decoding
data.reserve((bech.second.size() * 5) / 8);
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
T3 ret;
T2 ret;
ss >> ret;
memory_cleanse(data.data(), data.size());
return ret;
@ -397,7 +410,27 @@ T1 DecodeAny(
}
memory_cleanse(data.data(), data.size());
return libzcash::InvalidEncoding();
return std::nullopt;
}
template<typename T1, typename T2, typename T3>
std::optional<T1> DecodeAny(
const KeyConstants& keyConstants,
const std::string& str,
const std::pair<KeyConstants::Base58Type, size_t>& sproutKeyMeta,
const std::pair<KeyConstants::Bech32Type, size_t>& saplingKeyMeta)
{
auto sprout = DecodeSprout<T1, T2>(keyConstants, str, sproutKeyMeta);
if (sprout.has_value()) {
return sprout.value();
}
auto sapling = DecodeSapling<T1, T3>(keyConstants, str, saplingKeyMeta);
if (sapling.has_value()) {
return sapling.value();
}
return std::nullopt;
}
/**
@ -451,7 +484,7 @@ static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char*
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str)
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str)
{
// Try parsing as a Unified Address.
libzcash::UnifiedAddress ua;
@ -467,19 +500,43 @@ libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str)
return ua;
}
// Fall back on trying Sprout or Sapling.
return DecodeAny<libzcash::PaymentAddress,
libzcash::SproutPaymentAddress,
libzcash::SaplingPaymentAddress>(
// Try parsing as a Sapling address
auto sapling = DecodeSapling<libzcash::SaplingPaymentAddress, libzcash::SaplingPaymentAddress>(
keyConstants,
str,
std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize),
std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize)
);
std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize));
if (sapling.has_value()) {
return sapling.value();
}
// Try parsing as a Sprout address
auto sprout = DecodeSprout<libzcash::SproutPaymentAddress, libzcash::SproutPaymentAddress>(
keyConstants,
str,
std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize));
if (sprout.has_value()) {
return sprout.value();
}
// Finally, try parsing as transparent
return std::visit(match {
[](const CKeyID& keyIdIn) {
std::optional<libzcash::PaymentAddress> keyId = keyIdIn;
return keyId;
},
[](const CScriptID& scriptIdIn) {
std::optional<libzcash::PaymentAddress> scriptId = scriptIdIn;
return scriptId;
},
[](const CNoDestination& d) {
std::optional<libzcash::PaymentAddress> result = std::nullopt;
return result;
}
}, DecodeDestination(str));
}
bool KeyIO::IsValidPaymentAddressString(const std::string& str) {
return IsValidPaymentAddress(DecodePaymentAddress(str));
return DecodePaymentAddress(str).has_value();
}
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
@ -487,7 +544,7 @@ std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
return std::visit(ViewingKeyEncoder(keyConstants), vk);
}
libzcash::ViewingKey KeyIO::DecodeViewingKey(const std::string& str)
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str)
{
// Try parsing as a Unified full viewing key
auto ufvk = libzcash::UnifiedFullViewingKey::Decode(str, keyConstants);
@ -511,7 +568,7 @@ std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey)
return std::visit(SpendingKeyEncoder(keyConstants), zkey);
}
libzcash::SpendingKey KeyIO::DecodeSpendingKey(const std::string& str)
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str)
{
return DecodeAny<libzcash::SpendingKey,

View File

@ -37,14 +37,14 @@ public:
bool IsValidDestinationString(const std::string& str);
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str);
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str);
bool IsValidPaymentAddressString(const std::string& str);
std::string EncodeViewingKey(const libzcash::ViewingKey& vk);
libzcash::ViewingKey DecodeViewingKey(const std::string& str);
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str);
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
libzcash::SpendingKey DecodeSpendingKey(const std::string& str);
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str);
};
#endif // BITCOIN_KEY_IO_H

View File

@ -277,8 +277,9 @@ bool CBasicKeyStore::GetSaplingIncomingViewingKey(const libzcash::SaplingPayment
return false;
}
bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingExtendedSpendingKey &extskOut) const {
bool CBasicKeyStore::GetSaplingExtendedSpendingKey(
const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingExtendedSpendingKey &extskOut) const {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingExtendedFullViewingKey extfvk;
@ -288,6 +289,16 @@ bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymen
GetSaplingSpendingKey(extfvk, extskOut);
}
bool CBasicKeyStore::HaveSaplingSpendingKeyForAddress(
const libzcash::SaplingPaymentAddress &addr) const {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingExtendedFullViewingKey extfvk;
return GetSaplingIncomingViewingKey(addr, ivk) &&
GetSaplingFullViewingKey(ivk, extfvk) &&
HaveSaplingSpendingKey(extfvk);
}
//
// Unified Keys
//

View File

@ -73,6 +73,8 @@ public:
//! Check whether a Sapling spending key corresponding to a given Sapling viewing key is present in the store.
virtual bool HaveSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk) const =0;
virtual bool HaveSaplingSpendingKeyForAddress(
const libzcash::SaplingPaymentAddress &addr) const =0;
virtual bool GetSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
libzcash::SaplingExtendedSpendingKey& skOut) const =0;
@ -298,6 +300,7 @@ public:
}
return result;
}
bool HaveSaplingSpendingKeyForAddress(const libzcash::SaplingPaymentAddress &addr) const;
bool GetSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
libzcash::SaplingExtendedSpendingKey &skOut) const

View File

@ -117,9 +117,7 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
}
bool IsShieldedMinerAddress(const MinerAddress& minerAddr) {
return !(
std::holds_alternative<InvalidMinerAddress>(minerAddr) ||
std::holds_alternative<boost::shared_ptr<CReserveScript>>(minerAddr));
return !std::holds_alternative<boost::shared_ptr<CReserveScript>>(minerAddr);
}
class AddFundingStreamValueToTx
@ -241,8 +239,6 @@ public:
}
}
void operator()(const InvalidMinerAddress &invalid) const {}
// Create shielded output
void operator()(const libzcash::SaplingPaymentAddress &pa) const {
auto ctx = librustzcash_sapling_proving_ctx_init();
@ -706,24 +702,40 @@ class MinerAddressScript : public CReserveScript
void KeepScript() {}
};
std::optional<MinerAddress> ExtractMinerAddress::operator()(const CKeyID &keyID) const {
boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript());
mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return mAddr;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const CScriptID &addr) const {
return std::nullopt;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::SproutPaymentAddress &addr) const {
return std::nullopt;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::SaplingPaymentAddress &addr) const {
return addr;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::UnifiedAddress &addr) const {
for (const auto& receiver: addr) {
if (std::holds_alternative<libzcash::SaplingPaymentAddress>(receiver)) {
return std::get<libzcash::SaplingPaymentAddress>(receiver);
}
}
return std::nullopt;
}
void GetMinerAddress(MinerAddress &minerAddress)
{
KeyIO keyIO(Params());
// Try a transparent address first
auto mAddrArg = GetArg("-mineraddress", "");
CTxDestination addr = keyIO.DecodeDestination(mAddrArg);
if (IsValidDestination(addr)) {
boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript());
CKeyID keyID = std::get<CKeyID>(addr);
mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
minerAddress = mAddr;
} else {
// Try a payment address
auto zaddr = std::visit(ExtractMinerAddress(), keyIO.DecodePaymentAddress(mAddrArg));
if (std::visit(IsValidMinerAddress(), zaddr)) {
minerAddress = zaddr;
auto zaddr0 = keyIO.DecodePaymentAddress(mAddrArg);
if (zaddr0.has_value()) {
auto zaddr = std::visit(ExtractMinerAddress(), zaddr0.value());
if (zaddr.has_value()) {
minerAddress = zaddr.value();
}
}
}

View File

@ -23,14 +23,7 @@ static const int DEFAULT_GENERATE_THREADS = 1;
static const bool DEFAULT_PRINTPRIORITY = false;
class InvalidMinerAddress {
public:
friend bool operator==(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; }
friend bool operator<(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; }
};
typedef std::variant<
InvalidMinerAddress,
libzcash::SaplingPaymentAddress,
boost::shared_ptr<CReserveScript>> MinerAddress;
@ -39,36 +32,11 @@ class ExtractMinerAddress
public:
ExtractMinerAddress() {}
MinerAddress operator()(const libzcash::InvalidEncoding &invalid) const {
return InvalidMinerAddress();
}
MinerAddress operator()(const libzcash::SproutPaymentAddress &addr) const {
return InvalidMinerAddress();
}
MinerAddress operator()(const libzcash::SaplingPaymentAddress &addr) const {
return addr;
}
MinerAddress operator()(const libzcash::UnifiedAddress &addr) const {
auto recipient = RecipientForPaymentAddress()(addr);
if (recipient) {
// This looks like a recursive call, but we are actually calling
// ExtractMinerAddress with a different type:
// - libzcash::PaymentAddress has a libzcash::UnifiedAddress
// alternative, which invokes this method.
// - RecipientForPaymentAddress() returns libzcash::RawAddress,
// which does not have a libzcash::UnifiedAddress alternative.
//
// This works because std::visit does not require the visitor to
// solely match the std::variant, only that it can handle all of
// the variant's alternatives.
return std::visit(ExtractMinerAddress(), *recipient);
} else {
// Either the UA only contains unknown shielded receivers (unlikely that we
// wouldn't know about them), or it only contains transparent receivers
// (which are invalid).
return InvalidMinerAddress();
}
}
std::optional<MinerAddress> operator()(const CKeyID &keyID) const;
std::optional<MinerAddress> operator()(const CScriptID &addr) const;
std::optional<MinerAddress> operator()(const libzcash::SproutPaymentAddress &addr) const;
std::optional<MinerAddress> operator()(const libzcash::SaplingPaymentAddress &addr) const;
std::optional<MinerAddress> operator()(const libzcash::UnifiedAddress &addr) const;
};
class KeepMinerAddress
@ -76,7 +44,6 @@ class KeepMinerAddress
public:
KeepMinerAddress() {}
void operator()(const InvalidMinerAddress &invalid) const {}
void operator()(const libzcash::SaplingPaymentAddress &pa) const {}
void operator()(const boost::shared_ptr<CReserveScript> &coinbaseScript) const {
coinbaseScript->KeepScript();
@ -90,9 +57,6 @@ class IsValidMinerAddress
public:
IsValidMinerAddress() {}
bool operator()(const InvalidMinerAddress &invalid) const {
return false;
}
bool operator()(const libzcash::SaplingPaymentAddress &pa) const {
return true;
}

View File

@ -217,7 +217,27 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
class DescribePaymentAddressVisitor
{
public:
UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const CKeyID &addr) const {
UniValue obj(UniValue::VOBJ);
obj.pushKV("type", "p2pkh");
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", pwalletMain->HaveKey(addr));
}
#endif
return obj;
}
UniValue operator()(const CScriptID &addr) const {
UniValue obj(UniValue::VOBJ);
obj.pushKV("type", "p2sh");
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", pwalletMain->HaveCScript(addr));
}
#endif
return obj;
}
UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const {
UniValue obj(UniValue::VOBJ);
@ -226,7 +246,7 @@ public:
obj.pushKV("transmissionkey", zaddr.pk_enc.GetHex());
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr));
obj.pushKV("ismine", pwalletMain->HaveSproutSpendingKey(zaddr));
}
#endif
return obj;
@ -239,7 +259,7 @@ public:
obj.pushKV("diversifiedtransmissionkey", zaddr.pk_d.GetHex());
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr));
obj.pushKV("ismine", pwalletMain->HaveSaplingSpendingKeyForAddress(zaddr));
}
#endif
return obj;
@ -257,16 +277,16 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"z_validateaddress \"zaddr\"\n"
"\nReturn information about the given z address.\n"
"z_validateaddress \"address\"\n"
"\nReturn information about the given address.\n"
"\nArguments:\n"
"1. \"zaddr\" (string, required) The z address to validate\n"
"1. \"address\" (string, required) The address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"zaddr\", (string) The z address validated\n"
" \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"addr\", (string) The address validated\n"
" \"type\" : \"xxxx\", (string) \"p2pkh\", \"p2sh\", \"sprout\" or \"sapling\"\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"payingkey\" : \"hex\", (string) [sprout] The hex value of the paying key, a_pk\n"
" \"transmissionkey\" : \"hex\", (string) [sprout] The hex value of the transmission key, pk_enc\n"
" \"diversifier\" : \"hex\", (string) [sapling] The hex value of the diversifier, d\n"
@ -288,14 +308,14 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp)
KeyIO keyIO(Params());
string strAddress = params[0].get_str();
auto address = keyIO.DecodePaymentAddress(strAddress);
bool isValid = IsValidPaymentAddress(address);
bool isValid = address.has_value();
UniValue ret(UniValue::VOBJ);
ret.pushKV("isvalid", isValid);
if (isValid)
{
ret.pushKV("address", strAddress);
UniValue detail = std::visit(DescribePaymentAddressVisitor(), address);
UniValue detail = std::visit(DescribePaymentAddressVisitor(), address.value());
ret.pushKVs(detail);
}
return ret;

View File

@ -202,9 +202,9 @@ BOOST_AUTO_TEST_CASE(zc_address_test)
BOOST_CHECK(sk_string[1] == 'K');
auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string);
BOOST_CHECK(IsValidSpendingKey(spendingkey2));
BOOST_ASSERT(std::get_if<SproutSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = std::get<SproutSpendingKey>(spendingkey2);
BOOST_CHECK(spendingkey2.has_value());
BOOST_ASSERT(std::get_if<SproutSpendingKey>(&spendingkey2.value()) != nullptr);
auto sk2 = std::get<SproutSpendingKey>(spendingkey2.value());
BOOST_CHECK(sk.inner() == sk2.inner());
}
{
@ -216,10 +216,10 @@ BOOST_AUTO_TEST_CASE(zc_address_test)
BOOST_CHECK(addr_string[1] == 'c');
auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string);
BOOST_ASSERT(IsValidPaymentAddress(paymentaddr2));
BOOST_ASSERT(paymentaddr2.has_value());
BOOST_ASSERT(std::get_if<SproutPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = std::get<SproutPaymentAddress>(paymentaddr2);
BOOST_ASSERT(std::get_if<SproutPaymentAddress>(&paymentaddr2.value()) != nullptr);
auto addr2 = std::get<SproutPaymentAddress>(paymentaddr2.value());
BOOST_CHECK(addr.a_pk == addr2.a_pk);
BOOST_CHECK(addr.pk_enc == addr2.pk_enc);
}
@ -240,10 +240,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test)
BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0);
auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string);
BOOST_CHECK(IsValidSpendingKey(spendingkey2));
BOOST_CHECK(spendingkey2.has_value());
BOOST_ASSERT(std::get_if<SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = std::get<SaplingExtendedSpendingKey>(spendingkey2);
BOOST_ASSERT(std::get_if<SaplingExtendedSpendingKey>(&spendingkey2.value()) != nullptr);
auto sk2 = std::get<SaplingExtendedSpendingKey>(spendingkey2.value());
BOOST_CHECK(sk == sk2);
}
{
@ -253,10 +253,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test)
BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0);
auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string);
BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));
BOOST_CHECK(paymentaddr2.has_value());
BOOST_ASSERT(std::get_if<SaplingPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = std::get<SaplingPaymentAddress>(paymentaddr2);
BOOST_ASSERT(std::get_if<SaplingPaymentAddress>(&paymentaddr2.value()) != nullptr);
auto addr2 = std::get<SaplingPaymentAddress>(paymentaddr2.value());
BOOST_CHECK(addr == addr2);
}
}

View File

@ -126,12 +126,12 @@ TransactionBuilderResult::TransactionBuilderResult(const CTransaction& tx) : may
TransactionBuilderResult::TransactionBuilderResult(const std::string& error) : maybeError(error) {}
bool TransactionBuilderResult::IsTx() { return maybeTx != std::nullopt; }
bool TransactionBuilderResult::IsTx() { return maybeTx.has_value(); }
bool TransactionBuilderResult::IsError() { return maybeError != std::nullopt; }
bool TransactionBuilderResult::IsError() { return maybeError.has_value(); }
CTransaction TransactionBuilderResult::GetTxOrThrow() {
if (maybeTx) {
if (maybeTx.has_value()) {
return maybeTx.value();
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction: " + GetError());
@ -139,7 +139,7 @@ CTransaction TransactionBuilderResult::GetTxOrThrow() {
}
std::string TransactionBuilderResult::GetError() {
if (maybeError) {
if (maybeError.has_value()) {
return maybeError.value();
} else {
// This can only happen if isTx() is true in which case we should not call getError()
@ -216,7 +216,7 @@ void TransactionBuilder::AddSaplingOutput(
libzcash::Zip212Enabled zip_212_enabled = libzcash::Zip212Enabled::BeforeZip212;
// We use nHeight = chainActive.Height() + 1 since the output will be included in the next block
if (Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) {
if (consensusParams.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) {
zip_212_enabled = libzcash::Zip212Enabled::AfterZip212;
}
@ -522,6 +522,7 @@ void TransactionBuilder::CheckOrSetUsingSprout()
auto txVersionInfo = CurrentTxVersionInfo(consensusParams, nHeight, usingSprout.value());
mtx.nVersionGroupId = txVersionInfo.nVersionGroupId;
mtx.nVersion = txVersionInfo.nVersion;
mtx.nConsensusBranchId = std::nullopt;
}
}

View File

@ -22,6 +22,7 @@
#include "timedata.h"
#include "transaction_builder.h"
#include "util.h"
#include "util/match.h"
#include "utilmoneystr.h"
#include "utiltime.h"
#include "wallet.h"
@ -67,7 +68,7 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
CAmount fee,
UniValue contextInfo) :
tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs),
saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo)
saplingNoteInputs_(saplingNoteInputs), memo_(recipient.second), fee_(fee), contextinfo_(contextInfo)
{
if (fee < 0 || fee > MAX_MONEY) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
@ -77,10 +78,6 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs");
}
if (std::get<0>(recipient).size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing");
}
if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress");
}
@ -96,19 +93,32 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
}
KeyIO keyIO(Params());
toTaddr_ = keyIO.DecodeDestination(std::get<0>(recipient));
isToTaddr_ = IsValidDestination(toTaddr_);
isToTaddr_ = false;
isToZaddr_ = false;
if (!isToTaddr_) {
auto address = keyIO.DecodePaymentAddress(std::get<0>(recipient));
if (IsValidPaymentAddress(address)) {
std::visit(match {
[&](const CKeyID& keyId) {
toTaddr_ = keyId;
isToTaddr_ = true;
},
[&](const CScriptID& scriptId) {
toTaddr_ = scriptId;
isToTaddr_ = true;
},
[&](const libzcash::SproutPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
toPaymentAddress_ = address;
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address");
}
}
},
[&](const libzcash::SaplingPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
},
[&](const libzcash::UnifiedAddress& addr) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"z_mergetoaddress does not yet support sending to unified addresses");
},
}, recipient.first);
// Log the context info i.e. the call parameters to z_mergetoaddress
if (LogAcceptCategory("zrpcunsafe")) {
@ -331,9 +341,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (isToTaddr_) {
builder_.AddTransparentOutput(toTaddr_, sendAmount);
} else {
std::string zaddr = std::get<0>(recipient_);
std::string memo = std::get<1>(recipient_);
std::array<unsigned char, ZC_MEMO_SIZE> hexMemo = get_memo_from_hex_string(memo);
std::array<unsigned char, ZC_MEMO_SIZE> hexMemo = get_memo_from_hex_string(memo_);
auto saplingPaymentAddress = std::get_if<libzcash::SaplingPaymentAddress>(&toPaymentAddress_);
if (saplingPaymentAddress == nullptr) {
// This should never happen as we have already determined that the payment is to sapling
@ -385,14 +393,11 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
* END SCENARIO #1
*/
// Prepare raw transaction to handle JoinSplits
CMutableTransaction mtx(tx_);
ed25519_generate_keypair(&joinSplitPrivKey_, &joinSplitPubKey_);
mtx.joinSplitPubKey = joinSplitPubKey_;
tx_ = CTransaction(mtx);
std::string hexMemo = std::get<1>(recipient_);
/**
* SCENARIO #2
@ -408,8 +413,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
info.vpub_new = 0;
JSOutput jso = JSOutput(std::get<libzcash::SproutPaymentAddress>(toPaymentAddress_), sendAmount);
if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo);
if (memo_.size() > 0) {
jso.memo = get_memo_from_hex_string(memo_);
}
info.vjsout.push_back(jso);
@ -697,8 +702,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (isToZaddr_ && vpubNewProcessed) {
outputType = "target";
jso.addr = std::get<libzcash::SproutPaymentAddress>(toPaymentAddress_);
if (!hexMemo.empty()) {
jso.memo = get_memo_from_hex_string(hexMemo);
if (!memo_.empty()) {
jso.memo = get_memo_from_hex_string(memo_);
}
}
info.vjsout.push_back(jso);

View File

@ -34,7 +34,7 @@ typedef std::tuple<JSOutPoint, SproutNote, CAmount, SproutSpendingKey> MergeToAd
typedef std::tuple<SaplingOutPoint, SaplingNote, CAmount, SaplingExpandedSpendingKey> MergeToAddressInputSaplingNote;
// A recipient is a tuple of address, memo (optional if zaddr)
typedef std::tuple<std::string, std::string> MergeToAddressRecipient;
typedef std::pair<libzcash::PaymentAddress, std::string> MergeToAddressRecipient;
// Package of info which is passed to perform_joinsplit methods.
struct MergeToAddressJSInfo {
@ -89,11 +89,11 @@ private:
uint32_t consensusBranchId_;
CAmount fee_;
int mindepth_;
MergeToAddressRecipient recipient_;
bool isToTaddr_;
bool isToZaddr_;
CTxDestination toTaddr_;
PaymentAddress toPaymentAddress_;
std::string memo_;
Ed25519VerificationKey joinSplitPubKey_;
Ed25519SigningKey joinSplitPrivKey_;

View File

@ -85,7 +85,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() {
// We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying
// an anchor at height N-10 for each Sprout JoinSplit description
// Consider, should notes be sorted?
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11);
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 11);
}
CAmount availableFunds = 0;
for (const SproutNoteEntry& sproutEntry : sproutEntries) {
@ -198,8 +198,9 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration
if (mapArgs.count("-migrationdestaddress")) {
std::string migrationDestAddress = mapArgs["-migrationdestaddress"];
auto address = keyIO.DecodePaymentAddress(migrationDestAddress);
auto saplingAddress = std::get_if<libzcash::SaplingPaymentAddress>(&address);
assert(saplingAddress != nullptr); // This is checked in init.cpp
assert(address.has_value()); // This is checked in init.cpp
auto saplingAddress = std::get_if<libzcash::SaplingPaymentAddress>(&address.value());
assert(saplingAddress != nullptr); // This is also checked in init.cpp
return *saplingAddress;
}

File diff suppressed because it is too large Load Diff

View File

@ -24,54 +24,81 @@
#include <rust/ed25519/types.h>
using namespace libzcash;
class TxValues;
class FromAnyTaddr {
public:
friend bool operator==(const FromAnyTaddr &a, const FromAnyTaddr &b) { return true; }
};
typedef std::variant<FromAnyTaddr, PaymentAddress> PaymentSource;
class SendManyRecipient {
public:
std::string address;
PaymentAddress address;
CAmount amount;
std::string memo;
std::optional<std::string> memo;
SendManyRecipient(std::string address_, CAmount amount_, std::string memo_) :
SendManyRecipient(PaymentAddress address_, CAmount amount_, std::optional<std::string> memo_) :
address(address_), amount(amount_), memo(memo_) {}
};
class SendManyInputJSOP {
class SpendableInputs {
public:
JSOutPoint point;
SproutNote note;
CAmount amount;
std::vector<COutput> utxos;
std::vector<SproutNoteEntry> sproutNoteEntries;
std::vector<SaplingNoteEntry> saplingNoteEntries;
SendManyInputJSOP(JSOutPoint point_, SproutNote note_, CAmount amount_) :
point(point_), note(note_), amount(amount_) {}
/**
* Selectively discard notes that are not required to obtain the desired
* amount. Returns `false` if the available inputs do not add up to the
* desired amount.
*/
bool LimitToAmount(CAmount amount, CAmount dustThreshold);
/**
* Compute the total ZEC amount of spendable inputs.
*/
CAmount Total() const {
CAmount result = 0;
for (const auto& t : utxos) {
result += t.Value();
}
for (const auto& t : sproutNoteEntries) {
result += t.note.value();
}
for (const auto& t : saplingNoteEntries) {
result += t.note.value();
}
return result;
}
/**
* Return whether or not the set of selected UTXOs contains
* coinbase outputs.
*/
bool HasTransparentCoinbase() const;
/**
* List spendable inputs in zrpcunsafe log entries.
*/
void LogInputs(const AsyncRPCOperationId& id) const;
};
// Package of info which is passed to perform_joinsplit methods.
struct AsyncJoinSplitInfo
{
std::vector<JSInput> vjsin;
std::vector<JSOutput> vjsout;
std::vector<SproutNote> notes;
CAmount vpub_old = 0;
CAmount vpub_new = 0;
};
// A struct to help us track the witness and anchor for a given JSOutPoint
struct WitnessAnchorData {
std::optional<SproutWitness> witness;
uint256 anchor;
class TxOutputAmounts {
public:
CAmount t_outputs_total{0};
CAmount z_outputs_total{0};
};
class AsyncRPCOperation_sendmany : public AsyncRPCOperation {
public:
AsyncRPCOperation_sendmany(
std::optional<TransactionBuilder> builder,
CMutableTransaction contextualTx,
std::string fromAddress,
std::vector<SendManyRecipient> tOutputs,
std::vector<SendManyRecipient> zOutputs,
TransactionBuilder builder,
PaymentSource paymentSource,
std::vector<SendManyRecipient> recipients,
int minDepth,
CAmount fee = DEFAULT_FEE,
bool allowRevealedAmounts = false,
UniValue contextInfo = NullUniValue);
virtual ~AsyncRPCOperation_sendmany();
@ -85,65 +112,32 @@ public:
virtual UniValue getStatus() const;
bool testmode = false; // Set to true to disable sending txs and generating proofs
bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database.
bool testmode{false}; // Set to true to disable sending txs and generating proofs
private:
friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing
TransactionBuilder builder_;
PaymentSource paymentSource_;
std::vector<SendManyRecipient> recipients_;
int mindepth_{1};
CAmount fee_;
UniValue contextinfo_; // optional data to include in return value from getStatus()
bool isUsingBuilder_; // Indicates that no Sprout addresses are involved
uint32_t consensusBranchId_;
CAmount fee_;
int mindepth_;
std::string fromaddress_;
bool useanyutxo_;
bool isfromtaddr_;
bool isfromzaddr_;
CTxDestination fromtaddr_;
PaymentAddress frompaymentaddress_;
SpendingKey spendingkey_;
bool isfromtaddr_{false};
bool isfromsprout_{false};
bool isfromsapling_{false};
bool allowRevealedAmounts_{false};
uint32_t transparentRecipients_{0};
TxOutputAmounts txOutputAmounts_;
Ed25519VerificationKey joinSplitPubKey_;
Ed25519SigningKey joinSplitPrivKey_;
SpendableInputs FindSpendableInputs(bool fAcceptCoinbase);
// The key is the result string from calling JSOutPoint::ToString()
std::unordered_map<std::string, WitnessAnchorData> jsopWitnessAnchorMap;
static CAmount DefaultDustThreshold();
std::vector<SendManyRecipient> t_outputs_;
std::vector<SendManyRecipient> z_outputs_;
std::vector<COutput> t_inputs_;
std::vector<SendManyInputJSOP> z_sprout_inputs_;
std::vector<SaplingNoteEntry> z_sapling_inputs_;
static std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
TransactionBuilder builder_;
CTransaction tx_;
void add_taddr_change_output_to_tx(CReserveKey& keyChange, CAmount amount);
void add_taddr_outputs_to_tx();
bool find_unspent_notes();
bool find_utxos(bool fAcceptCoinbase, TxValues& txValues);
// Load transparent inputs into the transaction or the transactionBuilder (in case of have it)
bool load_inputs(TxValues& txValues);
std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
bool main_impl();
// JoinSplit without any input notes to spend
UniValue perform_joinsplit(AsyncJoinSplitInfo &);
// JoinSplit with input notes to spend (JSOutPoints))
UniValue perform_joinsplit(AsyncJoinSplitInfo &, std::vector<JSOutPoint> & );
// JoinSplit where you have the witnesses and anchor
UniValue perform_joinsplit(
AsyncJoinSplitInfo & info,
std::vector<std::optional < SproutWitness>> witnesses,
uint256 anchor);
// payment disclosure!
std::vector<PaymentDisclosureKeyInfo> paymentDisclosureData_;
uint256 main_impl();
};
@ -154,52 +148,14 @@ public:
TEST_FRIEND_AsyncRPCOperation_sendmany(std::shared_ptr<AsyncRPCOperation_sendmany> ptr) : delegate(ptr) {}
CTransaction getTx() {
return delegate->tx_;
}
void setTx(CTransaction tx) {
delegate->tx_ = tx;
}
// Delegated methods
void add_taddr_change_output_to_tx(CReserveKey& keyChange, CAmount amount) {
delegate->add_taddr_change_output_to_tx(keyChange, amount);
}
void add_taddr_outputs_to_tx() {
delegate->add_taddr_outputs_to_tx();
}
bool find_unspent_notes() {
return delegate->find_unspent_notes();
}
std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s) {
return delegate->get_memo_from_hex_string(s);
}
bool main_impl() {
uint256 main_impl() {
return delegate->main_impl();
}
UniValue perform_joinsplit(AsyncJoinSplitInfo &info) {
return delegate->perform_joinsplit(info);
}
UniValue perform_joinsplit(AsyncJoinSplitInfo &info, std::vector<JSOutPoint> &v ) {
return delegate->perform_joinsplit(info, v);
}
UniValue perform_joinsplit(
AsyncJoinSplitInfo & info,
std::vector<std::optional < SproutWitness>> witnesses,
uint256 anchor)
{
return delegate->perform_joinsplit(info, witnesses, anchor);
}
void set_state(OperationStatus state) {
delegate->state_.store(state);
}

View File

@ -26,6 +26,7 @@
#include "walletdb.h"
#include "script/interpreter.h"
#include "utiltime.h"
#include "util/match.h"
#include "zcash/IncrementalMerkleTree.hpp"
#include "miner.h"
#include "wallet/paymentdisclosuredb.h"
@ -63,7 +64,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
TransactionBuilder builder,
CMutableTransaction contextualTx,
std::vector<ShieldCoinbaseUTXO> inputs,
std::string toAddress,
PaymentAddress toAddress,
CAmount fee,
UniValue contextInfo) :
builder_(builder), tx_(contextualTx), inputs_(inputs), fee_(fee), contextinfo_(contextInfo)
@ -79,13 +80,23 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
}
// Check the destination address is valid for this network i.e. not testnet being used on mainnet
KeyIO keyIO(Params());
auto address = keyIO.DecodePaymentAddress(toAddress);
if (IsValidPaymentAddress(address)) {
tozaddr_ = address;
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid to address");
}
std::visit(match {
[&](CKeyID addr) {
throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2pkh address.");
},
[&](CScriptID addr) {
throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2sh address.");
},
[&](libzcash::SaplingPaymentAddress addr) {
tozaddr_ = addr;
},
[&](libzcash::SproutPaymentAddress addr) {
tozaddr_ = addr;
},
[&](libzcash::UnifiedAddress) {
throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a unified address.");
}
}, toAddress);
// Log the context info
if (LogAcceptCategory("zrpcunsafe")) {
@ -202,6 +213,14 @@ bool AsyncRPCOperation_shieldcoinbase::main_impl() {
return std::visit(ShieldToAddress(this, sendAmount), tozaddr_);
}
bool ShieldToAddress::operator()(const CKeyID &addr) const {
return false;
}
bool ShieldToAddress::operator()(const CScriptID &addr) const {
return false;
}
bool ShieldToAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const {
// update the transaction with these inputs
CMutableTransaction rawTx(m_op->tx_);
@ -264,11 +283,6 @@ bool ShieldToAddress::operator()(const libzcash::UnifiedAddress &uaddr) const {
return false;
}
bool ShieldToAddress::operator()(const libzcash::InvalidEncoding& no) const {
return false;
}
UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) {
uint32_t consensusBranchId;
uint256 anchor;

View File

@ -45,7 +45,7 @@ public:
TransactionBuilder builder,
CMutableTransaction contextualTx,
std::vector<ShieldCoinbaseUTXO> inputs,
std::string toAddress,
PaymentAddress toAddress,
CAmount fee = DEFAULT_FEE,
UniValue contextInfo = NullUniValue);
virtual ~AsyncRPCOperation_shieldcoinbase();
@ -103,10 +103,11 @@ public:
ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) :
m_op(op), sendAmount(sendAmount) {}
bool operator()(const CKeyID &zaddr) const;
bool operator()(const CScriptID &zaddr) const;
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};

View File

@ -207,11 +207,11 @@ TEST(WalletTests, FindUnspentSproutNotes) {
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
std::vector<SproutNoteEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, -1);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
@ -234,15 +234,15 @@ TEST(WalletTests, FindUnspentSproutNotes) {
// We now have an unspent and confirmed note in the wallet (depth of 1)
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
@ -272,22 +272,22 @@ TEST(WalletTests, FindUnspentSproutNotes) {
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
// The note has been spent. By default, GetFilteredNotes() ignores spent notes.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Let's include spent notes to retrieve it.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0, INT_MAX, false);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// The spent note has two confirmations.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, false);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// It does not have 3 confirmations.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 3, INT_MAX, false);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
@ -330,22 +330,22 @@ TEST(WalletTests, FindUnspentSproutNotes) {
wallet.AddToWallet(wtx3, true, NULL);
// We now have an unspent note which has one confirmation, in addition to our spent note.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Let's return the spent note too.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1, INT_MAX, false);
EXPECT_EQ(2, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// Increasing number of confirmations will exclude our new unspent note.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, false);
EXPECT_EQ(1, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();
// If we also ignore spent notes at this depth, we won't find any notes.
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true);
wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, true);
EXPECT_EQ(0, sproutEntries.size());
sproutEntries.clear();
saplingEntries.clear();

View File

@ -3,6 +3,7 @@
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "chain.h"
#include "core_io.h"
#include "key_io.h"
#include "rpc/server.h"
#include "init.h"
@ -11,6 +12,7 @@
#include "script/standard.h"
#include "sync.h"
#include "util.h"
#include "util/match.h"
#include "utiltime.h"
#include "wallet.h"
@ -400,9 +402,9 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys)
// Only include hdKeypath and seedFpStr if we have both
std::optional<std::string> hdKeypath = (vstr.size() > 3) ? std::optional<std::string>(vstr[2]) : std::nullopt;
std::optional<std::string> seedFpStr = (vstr.size() > 3) ? std::optional<std::string>(vstr[3]) : std::nullopt;
if (IsValidSpendingKey(spendingkey)) {
if (spendingkey.has_value()) {
auto addResult = std::visit(
AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey);
AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey.value());
if (addResult == KeyAlreadyExists){
LogPrint("zrpc", "Skipping import of zaddr (key already present)\n");
} else if (addResult == KeyNotAdded) {
@ -771,17 +773,17 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
KeyIO keyIO(Params());
string strSecret = params[0].get_str();
auto spendingkey = keyIO.DecodeSpendingKey(strSecret);
if (!IsValidSpendingKey(spendingkey)) {
if (!spendingkey.has_value()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
}
auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey);
auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey.value());
UniValue result(UniValue::VOBJ);
result.pushKV("type", addrInfo.first);
result.pushKV("address", keyIO.EncodePaymentAddress(addrInfo.second));
// Sapling support
auto addResult = std::visit(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey);
auto addResult = std::visit(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey.value());
if (addResult == KeyAlreadyExists && fIgnoreExistingKey) {
return result;
}
@ -866,17 +868,17 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
KeyIO keyIO(Params());
string strVKey = params[0].get_str();
auto viewingkey = keyIO.DecodeViewingKey(strVKey);
if (!IsValidViewingKey(viewingkey)) {
if (!viewingkey.has_value()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
}
auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey(Params()), viewingkey);
auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey(Params()), viewingkey.value());
UniValue result(UniValue::VOBJ);
const string strAddress = keyIO.EncodePaymentAddress(addrInfo.second);
result.pushKV("type", addrInfo.first);
result.pushKV("address", strAddress);
auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain), viewingkey);
auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain), viewingkey.value());
if (addResult == SpendingKeyExists) {
throw JSONRPCError(
RPC_WALLET_ERROR,
@ -925,16 +927,52 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
KeyIO keyIO(Params());
auto address = keyIO.DecodePaymentAddress(strAddress);
if (!IsValidPaymentAddress(address)) {
if (!address.has_value()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
}
// Sapling support
auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address);
if (!sk) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
}
return keyIO.EncodeSpendingKey(sk.value());
std::string result = std::visit(match {
[&](const CKeyID& addr) {
CKey key;
if (pwalletMain->GetKey(addr, key)) {
return keyIO.EncodeSecret(key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private key for this address.");
}
},
[&](const CScriptID& addr) {
CScript redeemScript;
if (pwalletMain->GetCScript(addr, redeemScript)) {
return FormatScript(redeemScript);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the redeem script for this P2SH address.");
}
},
[&](const libzcash::SproutPaymentAddress& addr) {
libzcash::SproutSpendingKey key;
if (pwalletMain->GetSproutSpendingKey(addr, key)) {
return keyIO.EncodeSpendingKey(key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private spending key for this Sprout address");
}
},
[&](const libzcash::SaplingPaymentAddress& addr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) {
return keyIO.EncodeSpendingKey(extsk);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private spending key for this Sapling address");
}
},
[&](const libzcash::UnifiedAddress& ua) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"No serialized form is defined for unified spending keys. "
"Use the emergency recovery phrase for this wallet for backup purposes instead.");
return std::string(); //unreachable, here to make the compiler happy
}
}, address.value());
return result;
}
UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
@ -964,11 +1002,11 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
KeyIO keyIO(Params());
auto address = keyIO.DecodePaymentAddress(strAddress);
if (!IsValidPaymentAddress(address)) {
if (!address.has_value()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
}
auto vk = std::visit(GetViewingKeyForPaymentAddress(pwalletMain), address);
auto vk = std::visit(GetViewingKeyForPaymentAddress(pwalletMain), address.value());
if (vk) {
return keyIO.EncodeViewingKey(vk.value());
} else {

File diff suppressed because it is too large Load Diff

View File

@ -376,20 +376,25 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance)
LOCK2(cs_main, pwalletMain->cs_wallet);
UniValue retValue;
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
std::string taddr1 = retValue.get_str();
BOOST_CHECK_THROW(CallRPC("z_getbalance too many args"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_getbalance invalidaddress"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab"));
BOOST_CHECK_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab 0"));
// address does not belong to wallet
BOOST_CHECK_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(std::string("z_getbalance ") + taddr1));
// negative minconf not allowed
BOOST_CHECK_THROW(CallRPC(std::string("z_getbalance ") + taddr1 + " -1"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(std::string("z_getbalance ") + taddr1 + std::string(" 0")));
// don't have the spending key
BOOST_CHECK_THROW(CallRPC("z_getbalance tnRZ8bPq2pff3xBWhTJhNkVUkm2uhzksDeW5PvEa7aFKGT9Qi3YgTALZfjaY4jU3HLVKBtHdSXxoPoLA3naMPcHBcY88FcF 1"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_gettotalbalance too manyargs"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_gettotalbalance -1"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("z_gettotalbalance 0"));
BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress too many args"), runtime_error);
// negative minconf not allowed
BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error);
@ -616,15 +621,16 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet)
BOOST_CHECK(addrs.size()==1);
// check that we have the spending key for the address
auto address = keyIO.DecodePaymentAddress(testAddr);
BOOST_CHECK(IsValidPaymentAddress(address));
BOOST_ASSERT(std::get_if<libzcash::SproutPaymentAddress>(&address) != nullptr);
auto addr = std::get<libzcash::SproutPaymentAddress>(address);
BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(addr));
auto decoded = keyIO.DecodePaymentAddress(testAddr);
BOOST_CHECK(decoded.has_value());
libzcash::PaymentAddress address(decoded.value());
BOOST_ASSERT(std::holds_alternative<libzcash::SproutPaymentAddress>(address));
auto sprout_addr = std::get<libzcash::SproutPaymentAddress>(address);
BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(sprout_addr));
// Verify the spending key is the same as the test data
libzcash::SproutSpendingKey k;
BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k));
BOOST_CHECK(pwalletMain->GetSproutSpendingKey(sprout_addr, k));
BOOST_CHECK_EQUAL(testKey, keyIO.EncodeSpendingKey(k));
}
@ -790,10 +796,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
// Check if address is of given type and spendable from our wallet.
template <typename ADDR_TYPE>
void CheckHaveAddr(const libzcash::PaymentAddress& addr) {
BOOST_CHECK(IsValidPaymentAddress(addr));
auto addr_of_type = std::get_if<ADDR_TYPE>(&addr);
void CheckHaveAddr(const std::optional<libzcash::PaymentAddress>& addr) {
BOOST_CHECK(addr.has_value());
auto addr_of_type = std::get_if<ADDR_TYPE>(&(addr.value()));
BOOST_ASSERT(addr_of_type != nullptr);
HaveSpendingKeyForPaymentAddress test(pwalletMain);
@ -1184,41 +1189,12 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters)
// Mutable tx containing contextual information we need to build tx
UniValue retValue = CallRPC("getblockcount");
int nHeight = retValue.get_int();
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1);
if (mtx.nVersion == 1) {
mtx.nVersion = 2;
}
// Test constructor of AsyncRPCOperation_sendmany
try {
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::nullopt, mtx, "",{}, {}, -1));
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Minconf cannot be negative"));
}
TransactionBuilder builder(Params().GetConsensus(), nHeight + 1, pwalletMain);
try {
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::nullopt, mtx, "",{}, {}, 1));
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "From address parameter missing"));
}
try {
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ", {}, {}, 1) );
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "No recipients"));
}
try {
std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy", 1*COIN, "") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, "INVALID", recipients, {}, 1) );
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Invalid from address"));
}
// Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix.
try {
std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy", 1*COIN, "") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) );
libzcash::UnifiedAddress ua; //dummy
std::vector<SendManyRecipient> recipients = { SendManyRecipient(ua, 1*COIN, std::nullopt) };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, ua, recipients, 1));
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Invalid from address"));
}
@ -1226,10 +1202,13 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters)
// Note: The following will crash as a google test because AsyncRPCOperation_sendmany
// invokes a method on pwalletMain, which is undefined in the google test environment.
try {
std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy", 1*COIN, "") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) );
KeyIO keyIO(Params());
auto sender = keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value();
libzcash::UnifiedAddress ua; //dummy
std::vector<SendManyRecipient> recipients = { SendManyRecipient(ua, 1*COIN, std::nullopt) };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, sender, recipients, 1));
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "no spending key found for zaddr"));
BOOST_CHECK( find_error(objError, "no spending key found for address"));
}
}
@ -1250,6 +1229,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
{
SelectParams(CBaseChainParams::TESTNET);
const Consensus::Params& consensusParams = Params().GetConsensus();
KeyIO keyIO(Params());
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -1259,54 +1239,55 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
// We removed the ability to create pre-Sapling Sprout proofs, so we can
// only create Sapling-onwards transactions.
int nHeight = consensusParams.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight + 1);
if (mtx.nVersion == 1) {
mtx.nVersion = 2;
}
// add keys manually
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
std::string taddr1 = retValue.get_str();
auto pa = pwalletMain->GenerateNewSproutZKey();
KeyIO keyIO(Params());
std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
auto taddr1 = keyIO.DecodePaymentAddress(retValue.get_str()).value();
if (!pwalletMain->HaveMnemonicSeed()) {
pwalletMain->GenerateNewSeed();
}
auto zaddr1 = pwalletMain->GenerateNewLegacySaplingZKey();
// there are no utxos to spend
{
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, taddr1, {}, recipients, 1) );
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, taddr1, recipients, 1));
operation->main();
BOOST_CHECK(operation->isFailed());
std::string msg = operation->getErrorMessage();
BOOST_CHECK( msg.find("Insufficient transparent funds") != string::npos);
BOOST_CHECK( msg.find("Insufficient funds") != string::npos);
}
// minconf cannot be zero when sending from zaddr
{
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
try {
std::vector<SendManyRecipient> recipients = {SendManyRecipient(taddr1, 100*COIN, "DEADBEEF")};
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::nullopt, mtx, zaddr1, recipients, {}, 0));
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, zaddr1, recipients, 0));
BOOST_CHECK(false); // Fail test if an exception is not thrown
} catch (const UniValue& objError) {
BOOST_CHECK(find_error(objError, "Minconf cannot be zero when sending from zaddr"));
BOOST_CHECK(find_error(objError, "Minconf cannot be zero when sending from a shielded address"));
}
}
// there are no unspent notes to spend
{
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(taddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, zaddr1, recipients, {}, 1) );
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, zaddr1, recipients, 1));
operation->main();
BOOST_CHECK(operation->isFailed());
std::string msg = operation->getErrorMessage();
BOOST_CHECK( msg.find("Insufficient funds, no unspent notes") != string::npos);
BOOST_CHECK(msg.find("Insufficient funds: have 0.00") != string::npos);
}
// get_memo_from_hex_string())
{
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, zaddr1, recipients, {}, 1) );
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, zaddr1, recipients, 1));
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr);
@ -1352,106 +1333,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
BOOST_CHECK( find_error(objError, "hexadecimal format"));
}
}
// add_taddr_change_output_to_tx() will append a vout to a raw transaction
{
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, zaddr1, recipients, {}, 1) );
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr);
CTransaction tx = proxy.getTx();
BOOST_CHECK(tx.vout.size() == 0);
CReserveKey keyChange(pwalletMain);
CAmount amount = 12345600000;
proxy.add_taddr_change_output_to_tx(keyChange, amount);
tx = proxy.getTx();
BOOST_CHECK(tx.vout.size() == 1);
CTxOut out = tx.vout[0];
BOOST_CHECK_EQUAL(out.nValue, amount);
amount = 111100000;
proxy.add_taddr_change_output_to_tx(keyChange, amount);
tx = proxy.getTx();
BOOST_CHECK(tx.vout.size() == 2);
out = tx.vout[1];
BOOST_CHECK_EQUAL(out.nValue, amount);
}
// add_taddr_outputs_to_tx() will append many vouts to a raw transaction
{
std::vector<SendManyRecipient> recipients = {
SendManyRecipient("tmTGScYwiLMzHe4uGZtBYmuqoW4iEoYNMXt", 123000000, ""),
SendManyRecipient("tmUSbHz3vxnwLvRyNDXbwkZxjVyDodMJEhh", 456000000, ""),
SendManyRecipient("tmYZAXYPCP56Xa5JQWWPZuK7o7bfUQW6kkd", 789000000, ""),
};
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, zaddr1, recipients, {}, 1) );
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr);
proxy.add_taddr_outputs_to_tx();
CTransaction tx = proxy.getTx();
BOOST_CHECK(tx.vout.size() == 3);
BOOST_CHECK_EQUAL(tx.vout[0].nValue, 123000000);
BOOST_CHECK_EQUAL(tx.vout[1].nValue, 456000000);
BOOST_CHECK_EQUAL(tx.vout[2].nValue, 789000000);
}
// Test the perform_joinsplit methods.
{
// Dummy input so the operation object can be instantiated.
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 50000, "ABCD") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, zaddr1, {}, recipients, 1) );
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr);
// Enable test mode so tx is not sent and proofs are not generated
static_cast<AsyncRPCOperation_sendmany *>(operation.get())->testmode = true;
AsyncJoinSplitInfo info;
std::vector<std::optional < SproutWitness>> witnesses;
uint256 anchor;
try {
proxy.perform_joinsplit(info, witnesses, anchor);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos);
}
try {
std::vector<JSOutPoint> v;
proxy.perform_joinsplit(info, v);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos);
}
info.notes.push_back(SproutNote());
try {
proxy.perform_joinsplit(info);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("number of notes")!= string::npos);
}
info.notes.clear();
info.vjsin.push_back(JSInput());
info.vjsin.push_back(JSInput());
info.vjsin.push_back(JSInput());
try {
proxy.perform_joinsplit(info);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos);
}
info.vjsin.clear();
try {
proxy.perform_joinsplit(info);
} catch (const std::runtime_error & e) {
BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos);
}
}
}
@ -1471,8 +1352,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
// add keys manually
auto taddr = pwalletMain->GenerateNewKey().GetID();
std::string taddr1 = keyIO.EncodeDestination(taddr);
auto pa = DefaultSaplingAddress(pwalletMain);
std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
auto pa = pwalletMain->GenerateNewLegacySaplingZKey();
const Consensus::Params& consensusParams = Params().GetConsensus();
retValue = CallRPC("getblockcount");
@ -1505,8 +1385,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
auto builder = TransactionBuilder(consensusParams, nextBlockHeight, pwalletMain);
mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight);
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 1*COIN, "ABCD") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(builder, mtx, taddr1, {}, recipients, 0) );
std::vector<SendManyRecipient> recipients = { SendManyRecipient(pa, 1*COIN, "ABCD") };
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, taddr, recipients, 0));
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
// Enable test mode so tx is not sent
@ -1779,8 +1659,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters)
}
// Test constructor of AsyncRPCOperation_shieldcoinbase
std::string testnetzaddr = "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP";
std::string mainnetzaddr = "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U";
KeyIO keyIO(Params());
auto testnetzaddr = std::get<libzcash::SproutPaymentAddress>(keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value());
try {
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, {}, testnetzaddr, -1 ));
@ -1793,15 +1673,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters)
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Empty inputs"));
}
// Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix.
try {
std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, mainnetzaddr, 1) );
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Invalid to address"));
}
}
@ -1819,14 +1690,12 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight + 1);
// Add keys manually
KeyIO keyIO(Params());
auto pa = pwalletMain->GenerateNewSproutZKey();
std::string zaddr = keyIO.EncodePaymentAddress(pa);
// Insufficient funds
{
std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) );
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, pa) );
operation->main();
BOOST_CHECK(operation->isFailed());
std::string msg = operation->getErrorMessage();
@ -1837,7 +1706,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals)
{
// Dummy input so the operation object can be instantiated.
std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,100000} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) );
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, pa) );
std::shared_ptr<AsyncRPCOperation_shieldcoinbase> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_shieldcoinbase> (operation);
TEST_FRIEND_AsyncRPCOperation_shieldcoinbase proxy(ptr);
static_cast<AsyncRPCOperation_shieldcoinbase *>(operation.get())->testmode = true;
@ -1946,12 +1815,10 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1);
// Test constructor of AsyncRPCOperation_mergetoaddress
KeyIO keyIO(Params());
MergeToAddressRecipient testnetzaddr(
"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP",
keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value(),
"testnet memo");
MergeToAddressRecipient mainnetzaddr(
"zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U",
"mainnet memo");
try {
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, {}, {}, {}, testnetzaddr, -1 ));
@ -1969,14 +1836,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} };
try {
MergeToAddressRecipient badaddr("", "memo");
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, badaddr, 1));
BOOST_FAIL("Should have caused an error");
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Recipient parameter missing"));
}
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs =
{MergeToAddressInputSproutNote{JSOutPoint(), SproutNote(), 0, SproutSpendingKey()}};
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs =
@ -1996,15 +1855,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
} catch (const UniValue& objError) {
BOOST_CHECK(find_error(objError, "Sprout notes are not supported by the TransactionBuilder"));
}
// Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix.
try {
std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, mainnetzaddr, 1) );
BOOST_FAIL("Should have caused an error");
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Invalid recipient address"));
}
}
@ -2013,6 +1863,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
{
SelectParams(CBaseChainParams::TESTNET);
const Consensus::Params& consensusParams = Params().GetConsensus();
KeyIO keyIO(Params());
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -2026,10 +1877,9 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
// Add keys manually
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
MergeToAddressRecipient taddr1(retValue.get_str(), "");
MergeToAddressRecipient taddr1(keyIO.DecodePaymentAddress(retValue.get_str()).value(), "");
auto pa = pwalletMain->GenerateNewSproutZKey();
KeyIO keyIO(Params());
MergeToAddressRecipient zaddr1(keyIO.EncodePaymentAddress(pa), "DEADBEEF");
MergeToAddressRecipient zaddr1(pa, "DEADBEEF");
// Insufficient funds
{

View File

@ -25,6 +25,7 @@
#include "script/sign.h"
#include "timedata.h"
#include "utilmoneystr.h"
#include "util/match.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "crypter.h"
@ -1049,49 +1050,58 @@ void CWallet::SetBestChain(const CBlockLocator& loc)
SetBestChainINTERNAL(walletdb, loc);
}
std::set<std::pair<libzcash::RawAddress, uint256>> CWallet::GetNullifiersForAddresses(
const std::set<libzcash::RawAddress> & addresses)
{
std::set<std::pair<libzcash::RawAddress, uint256>> nullifierSet;
// Sapling ivk -> list of addrs map
// (There may be more than one diversified address for a given ivk.)
std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap;
for (const auto & addr : addresses) {
auto saplingAddr = std::get_if<libzcash::SaplingPaymentAddress>(&addr);
if (saplingAddr != nullptr) {
libzcash::SaplingIncomingViewingKey ivk;
this->GetSaplingIncomingViewingKey(*saplingAddr, ivk);
ivkMap[ivk].push_back(*saplingAddr);
}
}
for (const auto & txPair : mapWallet) {
// Sprout
for (const auto & noteDataPair : txPair.second.mapSproutNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & address = noteData.address;
if (nullifier && addresses.count(address)) {
nullifierSet.insert(std::make_pair(address, nullifier.value()));
}
}
// Sapling
for (const auto & noteDataPair : txPair.second.mapSaplingNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & ivk = noteData.ivk;
if (nullifier && ivkMap.count(ivk)) {
for (const auto & addr : ivkMap[ivk]) {
nullifierSet.insert(std::make_pair(addr, nullifier.value()));
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> CWallet::GetSproutNullifiers(
const std::set<libzcash::SproutPaymentAddress>& addresses) {
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> nullifierSet;
if (!addresses.empty()) {
for (const auto& txPair : mapWallet) {
for (const auto & noteDataPair : txPair.second.mapSproutNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & address = noteData.address;
if (nullifier && addresses.count(address) > 0) {
nullifierSet.insert(std::make_pair(address, nullifier.value()));
}
}
}
}
return nullifierSet;
}
std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> CWallet::GetSaplingNullifiers(
const std::set<libzcash::SaplingPaymentAddress>& addresses) {
std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> nullifierSet;
if (!addresses.empty()) {
// Sapling ivk -> list of addrs map
// (There may be more than one diversified address for a given ivk.)
std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap;
for (const auto & addr : addresses) {
libzcash::SaplingIncomingViewingKey ivk;
this->GetSaplingIncomingViewingKey(addr, ivk);
ivkMap[ivk].push_back(addr);
}
for (const auto& txPair : mapWallet) {
for (const auto& noteDataPair : txPair.second.mapSaplingNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & ivk = noteData.ivk;
if (nullifier && ivkMap.count(ivk) > 0) {
for (const auto & addr : ivkMap[ivk]) {
nullifierSet.insert(std::make_pair(addr, nullifier.value()));
}
}
}
}
}
return nullifierSet;
}
bool CWallet::IsNoteSproutChange(
const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet,
const libzcash::RawAddress & address,
const std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> & nullifierSet,
const libzcash::SproutPaymentAddress& address,
const JSOutPoint & jsop)
{
// A Note is marked as "change" if the address that received it
@ -1113,8 +1123,9 @@ bool CWallet::IsNoteSproutChange(
return false;
}
bool CWallet::IsNoteSaplingChange(const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet,
const libzcash::RawAddress & address,
bool CWallet::IsNoteSaplingChange(
const std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> & nullifierSet,
const libzcash::SaplingPaymentAddress& address,
const SaplingOutPoint & op)
{
// A Note is marked as "change" if the address that received it
@ -1125,6 +1136,8 @@ bool CWallet::IsNoteSaplingChange(const std::set<std::pair<libzcash::RawAddress,
// - Notes created by consolidation transactions (e.g. using
// z_mergetoaddress).
// - Notes sent from one address to itself.
// FIXME: This also needs to check against the wallet's change address
// for the associated unified account when we add UA support
for (const SpendDescription &spend : mapWallet[op.hash].vShieldedSpend) {
if (nullifierSet.count(std::make_pair(address, spend.nullifier))) {
return true;
@ -2386,7 +2399,7 @@ bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const
return false;
}
void CWallet::GetSproutNoteWitnesses(std::vector<JSOutPoint> notes,
void CWallet::GetSproutNoteWitnesses(const std::vector<JSOutPoint>& notes,
std::vector<std::optional<SproutWitness>>& witnesses,
uint256 &final_anchor)
{
@ -2413,7 +2426,7 @@ void CWallet::GetSproutNoteWitnesses(std::vector<JSOutPoint> notes,
}
}
void CWallet::GetSaplingNoteWitnesses(std::vector<SaplingOutPoint> notes,
void CWallet::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
std::vector<std::optional<SaplingWitness>>& witnesses,
uint256 &final_anchor)
{
@ -5322,8 +5335,8 @@ bool CWallet::ParameterInteraction(const CChainParams& params)
// Check Sapling migration address if set and is a valid Sapling address
if (mapArgs.count("-migrationdestaddress")) {
std::string migrationDestAddress = mapArgs["-migrationdestaddress"];
libzcash::PaymentAddress address = keyIO.DecodePaymentAddress(migrationDestAddress);
if (std::get_if<libzcash::SaplingPaymentAddress>(&address) == nullptr) {
std::optional<libzcash::PaymentAddress> address = keyIO.DecodePaymentAddress(migrationDestAddress);
if (!address.has_value() || std::get_if<libzcash::SaplingPaymentAddress>(&address.value()) == nullptr) {
return UIError(_("-migrationdestaddress must be a valid Sapling address."));
}
}
@ -5420,46 +5433,73 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
return ::AcceptToMemoryPool(Params(), mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee);
}
/**
* Find notes in the wallet filtered by payment address, min depth and ability to spend.
* These notes are decrypted and added to the output parameter vector, outEntries.
*/
void CWallet::GetFilteredNotes(
std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::string address,
int minDepth,
bool ignoreSpent,
bool requireSpendingKey)
{
std::set<libzcash::RawAddress> filterAddresses;
AddrSet AddrSet::ForPaymentAddresses(const std::vector<libzcash::PaymentAddress>& paymentAddrs) {
AddrSet addrs;
for (const auto& addr: paymentAddrs) {
std::visit(match {
[&](const CKeyID& keyId) { },
[&](const CScriptID& scriptId) { },
[&](const libzcash::SproutPaymentAddress& addr) {
addrs.sproutAddresses.insert(addr);
},
[&](const libzcash::SaplingPaymentAddress& addr) {
addrs.saplingAddresses.insert(addr);
},
[&](const libzcash::UnifiedAddress& uaddr) {
for (auto& receiver : uaddr) {
std::visit(match {
[&](const libzcash::SaplingPaymentAddress& addr) {
addrs.saplingAddresses.insert(addr);
},
[&](const auto& other) { }
}, receiver);
}
},
}, addr);
}
return addrs;
}
KeyIO keyIO(Params());
if (address.length() > 0) {
auto addr = keyIO.DecodePaymentAddress(address);
for (const auto ra : std::visit(GetRawAddresses(), addr)) {
filterAddresses.insert(ra);
bool CWallet::HasSpendingKeys(const AddrSet& addrSet) const {
for (const auto& zaddr : addrSet.GetSproutAddresses()) {
if (!HaveSproutSpendingKey(zaddr)) {
return false;
}
}
GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey);
for (const auto& zaddr : addrSet.GetSaplingAddresses()) {
if (!HaveSaplingSpendingKeyForAddress(zaddr)) {
return false;
}
}
return true;
}
/**
* Find notes in the wallet filtered by payment addresses, min depth, max depth,
* if the note is spent, if a spending key is required, and if the notes are locked.
* These notes are decrypted and added to the output parameter vector, outEntries.
*
* For the `noteFilter` argument, `std::nullopt` will return every address; if a
* value is provided, all returned notes will correspond to the addresses in
* that address set. If the empty address set is provided, this function will
* return early and the return arguments `sproutEntries` and `saplingEntries`
* will be unmodified.
*/
void CWallet::GetFilteredNotes(
std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::set<libzcash::RawAddress>& filterAddresses,
const std::optional<AddrSet>& noteFilter,
int minDepth,
int maxDepth,
bool ignoreSpent,
bool requireSpendingKey,
bool ignoreLocked)
{
// Don't bother to do anything if the note filter would reject all notes
if (noteFilter.has_value() && noteFilter.value().IsEmpty())
return;
LOCK2(cs_main, cs_wallet);
KeyIO keyIO(Params());
@ -5483,8 +5523,8 @@ void CWallet::GetFilteredNotes(
SproutNoteData nd = pair.second;
SproutPaymentAddress pa = nd.address;
// skip notes which belong to a different payment address in the wallet
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
// skip notes which do not conform to the filter, if supplied
if (noteFilter.has_value() && !noteFilter.value().HasSproutAddress(pa)) {
continue;
}
@ -5553,8 +5593,8 @@ void CWallet::GetFilteredNotes(
assert(static_cast<bool>(maybe_pa));
auto pa = maybe_pa.value();
// skip notes which belong to a different payment address in the wallet
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
// skip notes which do not conform to the filter, if supplied
if (noteFilter.has_value() && !noteFilter.value().HasSaplingAddress(pa)) {
continue;
}
@ -5563,7 +5603,7 @@ void CWallet::GetFilteredNotes(
}
// skip notes which cannot be spent
if (requireSpendingKey && !HaveSpendingKeyForPaymentAddress(this)(pa)) {
if (requireSpendingKey && !HaveSaplingSpendingKeyForAddress(pa)) {
continue;
}
@ -5585,42 +5625,57 @@ std::optional<UnifiedAddress> CWallet::GetUnifiedForReceiver(const Receiver& rec
}
//
// Shielded key and address generalizations
// Payment address operations
//
// PaymentAddressBelongsToWallet
bool PaymentAddressBelongsToWallet::operator()(const CKeyID &addr) const
{
CScript script = GetScriptForDestination(addr);
return m_wallet->HaveKey(addr) || m_wallet->HaveWatchOnly(script);
}
bool PaymentAddressBelongsToWallet::operator()(const CScriptID &addr) const
{
CScript script = GetScriptForDestination(addr);
return m_wallet->HaveCScript(addr) || m_wallet->HaveWatchOnly(script);
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr);
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
// If we have a SaplingExtendedSpendingKey in the wallet, then we will
// also have the corresponding SaplingExtendedFullViewingKey.
return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
return
m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
m_wallet->HaveSaplingFullViewingKey(ivk);
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return false;
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const
// GetSourceForPaymentAddress
PaymentAddressSource GetSourceForPaymentAddress::operator()(const CKeyID &zaddr) const
{
return false;
// TODO
return AddressNotFound;
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const CScriptID &zaddr) const
{
// TODO
return AddressNotFound;
}
///
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return Random;
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
@ -5633,7 +5688,7 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl
if (m_wallet->mapSaplingZKeyMetadata.count(ivk) > 0 &&
m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath != "") {
return LegacyHDSeed;
} else if (HaveSpendingKeyForPaymentAddress(m_wallet)(zaddr)) {
} else if (m_wallet->HaveSaplingSpendingKeyForAddress(zaddr)) {
return Imported;
} else {
return ImportedWatchOnly;
@ -5645,21 +5700,24 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl
return AddressNotFound;
}
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return AddressNotFound;
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const
// GetViewingKeyForPaymentAddress
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return AddressNotFound;
return std::nullopt;
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
///
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
@ -5673,7 +5731,6 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
}
return libzcash::ViewingKey(vk);
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
@ -5688,26 +5745,27 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
return std::nullopt;
}
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return libzcash::ViewingKey();
return std::nullopt;
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::InvalidEncoding& no) const
// HaveSpendingKeyForPaymentAddress
bool HaveSpendingKeyForPaymentAddress::operator()(const CKeyID &addr) const
{
// Defaults to InvalidEncoding
return libzcash::ViewingKey();
return m_wallet->HaveKey(addr);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const CScriptID &addr) const
{
return m_wallet->HaveCScript(addr);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return m_wallet->HaveSproutSpendingKey(zaddr);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
@ -5717,53 +5775,88 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPayment
m_wallet->GetSaplingFullViewingKey(ivk, extfvk) &&
m_wallet->HaveSaplingSpendingKey(extfvk);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return false;
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const
{
return false;
}
// GetSproutKeyForPaymentAddress
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
libzcash::SproutSpendingKey k;
if (m_wallet->GetSproutSpendingKey(zaddr, k)) {
return libzcash::SpendingKey(k);
return k;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
// GetSaplingKeyForPaymentAddress
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) {
return libzcash::SpendingKey(extsk);
return extsk;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return libzcash::SpendingKey();
for (const libzcash::Receiver& receiver: uaddr) {
auto saplingAddr = std::get_if<SaplingPaymentAddress>(&receiver);
if (saplingAddr != nullptr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(*saplingAddr, extsk)) {
return extsk;
}
}
}
return std::nullopt;
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
const libzcash::InvalidEncoding& no) const
{
// Defaults to InvalidEncoding
return libzcash::SpendingKey();
}
// AddViewingKeyToWallet
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey &vkey) const {
auto addr = vkey.address();
@ -5778,7 +5871,6 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey
return KeyNotAdded;
}
}
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFullViewingKey &extfvk) const {
if (m_wallet->HaveSaplingSpendingKey(extfvk)) {
return SpendingKeyExists;
@ -5795,9 +5887,7 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::UnifiedFullViewin
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unified full viewing key import is not yet supported.");
}
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
}
// AddSpendingKeyToWallet
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const {
auto addr = sk.address();
@ -5814,7 +5904,6 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKe
return KeyNotAdded;
}
}
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const {
auto extfvk = sk.ToXFVK();
auto ivk = extfvk.fvk.in_viewing_key();
@ -5851,10 +5940,6 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
}
}
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
}
std::optional<libzcash::UnifiedAddress> LookupUnifiedAddress::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
auto ufvkPair = wallet.GetUFVKMetadataForReceiver(saplingAddr);
if (ufvkPair.has_value()) {

View File

@ -639,6 +639,37 @@ public:
std::set<uint256> GetConflicts() const;
};
class AddrSet {
private:
std::set<libzcash::SproutPaymentAddress> sproutAddresses;
std::set<libzcash::SaplingPaymentAddress> saplingAddresses;
AddrSet() {}
public:
static AddrSet Empty() { return AddrSet(); }
static AddrSet ForPaymentAddresses(const std::vector<libzcash::PaymentAddress>& addrs);
const std::set<libzcash::SproutPaymentAddress>& GetSproutAddresses() const {
return sproutAddresses;
}
const std::set<libzcash::SaplingPaymentAddress>& GetSaplingAddresses() const {
return saplingAddresses;
}
bool IsEmpty() const {
return sproutAddresses.empty() && saplingAddresses.empty();
}
bool HasSproutAddress(libzcash::SproutPaymentAddress addr) const {
return sproutAddresses.count(addr) > 0;
}
bool HasSaplingAddress(libzcash::SaplingPaymentAddress addr) const {
return saplingAddresses.count(addr) > 0;
}
};
class COutput
{
public:
@ -1313,11 +1344,11 @@ public:
bool IsSaplingNullifierFromMe(const uint256& nullifier) const;
void GetSproutNoteWitnesses(
std::vector<JSOutPoint> notes,
const std::vector<JSOutPoint>& notes,
std::vector<std::optional<SproutWitness>>& witnesses,
uint256 &final_anchor);
void GetSaplingNoteWitnesses(
std::vector<SaplingOutPoint> notes,
const std::vector<SaplingOutPoint>& notes,
std::vector<std::optional<SaplingWitness>>& witnesses,
uint256 &final_anchor);
@ -1341,9 +1372,20 @@ public:
void AddPendingSaplingMigrationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */
void SetBestChain(const CBlockLocator& loc);
std::set<std::pair<libzcash::RawAddress, uint256>> GetNullifiersForAddresses(const std::set<libzcash::RawAddress> & addresses);
bool IsNoteSproutChange(const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet, const libzcash::RawAddress & address, const JSOutPoint & entry);
bool IsNoteSaplingChange(const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet, const libzcash::RawAddress & address, const SaplingOutPoint & entry);
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> GetSproutNullifiers(
const std::set<libzcash::SproutPaymentAddress>& addresses);
bool IsNoteSproutChange(
const std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> & nullifierSet,
const libzcash::SproutPaymentAddress& address,
const JSOutPoint & entry);
std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> GetSaplingNullifiers(
const std::set<libzcash::SaplingPaymentAddress>& addresses);
bool IsNoteSaplingChange(
const std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> & nullifierSet,
const libzcash::SaplingPaymentAddress& address,
const SaplingOutPoint & entry);
DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
@ -1462,19 +1504,17 @@ public:
bool CheckNetworkInfo(std::pair<std::string, std::string> networkInfo);
uint32_t BIP44CoinType();
/* Find notes filtered by payment address, min depth, ability to spend */
void GetFilteredNotes(std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::string address,
int minDepth=1,
bool ignoreSpent=true,
bool requireSpendingKey=true);
/**
* Check whether the wallet contains spending keys for all the addresses
* contained in the given address set.
*/
bool HasSpendingKeys(const AddrSet& noteFilter) const;
/* Find notes filtered by payment addresses, min depth, max depth, if they are spent,
if a spending key is required, and if they are locked */
void GetFilteredNotes(std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::set<libzcash::RawAddress>& filterAddresses,
const std::optional<AddrSet>& noteFilter,
int minDepth=1,
int maxDepth=INT_MAX,
bool ignoreSpent=true,
@ -1527,10 +1567,11 @@ private:
public:
PaymentAddressBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const CKeyID &zaddr) const;
bool operator()(const CScriptID &zaddr) const;
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
class GetViewingKeyForPaymentAddress
@ -1540,10 +1581,11 @@ private:
public:
GetViewingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::ViewingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
std::optional<libzcash::ViewingKey> operator()(const libzcash::InvalidEncoding& no) const;
};
class HaveSpendingKeyForPaymentAddress
@ -1553,23 +1595,39 @@ private:
public:
HaveSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const CKeyID &addr) const;
bool operator()(const CScriptID &addr) const;
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
class GetSpendingKeyForPaymentAddress
class GetSproutKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
GetSproutKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::InvalidEncoding& no) const;
std::optional<libzcash::SproutSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
class GetSaplingKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSaplingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
enum PaymentAddressSource {
@ -1588,10 +1646,11 @@ private:
public:
GetSourceForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
PaymentAddressSource operator()(const CKeyID &zaddr) const;
PaymentAddressSource operator()(const CScriptID &zaddr) const;
PaymentAddressSource operator()(const libzcash::SproutPaymentAddress &zaddr) const;
PaymentAddressSource operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
PaymentAddressSource operator()(const libzcash::UnifiedAddress &uaddr) const;
PaymentAddressSource operator()(const libzcash::InvalidEncoding& no) const;
};
enum KeyAddResult {
@ -1611,7 +1670,6 @@ public:
KeyAddResult operator()(const libzcash::SproutViewingKey &sk) const;
KeyAddResult operator()(const libzcash::SaplingExtendedFullViewingKey &sk) const;
KeyAddResult operator()(const libzcash::UnifiedFullViewingKey &sk) const;
KeyAddResult operator()(const libzcash::InvalidEncoding& no) const;
};
class AddSpendingKeyToWallet
@ -1638,7 +1696,6 @@ public:
KeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const;
KeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
KeyAddResult operator()(const libzcash::InvalidEncoding& no) const;
};
class LookupUnifiedAddress {

View File

@ -80,9 +80,6 @@ std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(co
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const {
return std::make_pair("sapling", sk.ToXFVK().DefaultAddress());
}
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const {
throw std::invalid_argument("Cannot derive default address from invalid spending key");
}
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const SproutViewingKey &sk) const {
return std::make_pair("sprout", sk.address());
@ -99,132 +96,30 @@ std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(con
.first
);
}
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const InvalidEncoding&) const {
throw std::invalid_argument("Cannot derive default address from invalid viewing key");
}
}
bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr) {
return !std::holds_alternative<libzcash::InvalidEncoding>(zaddr);
}
bool IsValidViewingKey(const libzcash::ViewingKey& vk) {
return !std::holds_alternative<libzcash::InvalidEncoding>(vk);
}
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) {
return !std::holds_alternative<libzcash::InvalidEncoding>(zkey);
}
} // namespace libzcash
uint32_t TypecodeForReceiver::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return ZCASH_UA_TYPECODE_SAPLING;
}
uint32_t TypecodeForReceiver::operator()(
const CScriptID &p2sh) const
{
return ZCASH_UA_TYPECODE_P2SH;
}
uint32_t TypecodeForReceiver::operator()(
const CKeyID &p2sh) const
{
return ZCASH_UA_TYPECODE_P2PKH;
}
uint32_t TypecodeForReceiver::operator()(
const libzcash::UnknownReceiver &unknown) const
{
return unknown.typecode;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return zaddr;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const CScriptID &p2sh) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const CKeyID &p2sh) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const libzcash::UnknownReceiver &p2sh) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::InvalidEncoding& no) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
return zaddr;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return zaddr;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
for (auto& receiver : uaddr) {
// Return the first one.
return std::visit(ReceiverToRawAddress(), receiver);
}
return std::nullopt;
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::InvalidEncoding& no) const
{
return {};
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
return {zaddr};
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return {zaddr};
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
std::set<libzcash::RawAddress> ret;
for (auto& receiver : uaddr) {
auto ra = std::visit(ReceiverToRawAddress(), receiver);
if (ra) {
ret.insert(*ra);
}
}
return ret;
}
//
// Unified full viewing keys
//

View File

@ -6,6 +6,8 @@
#include "key_constants.h"
#include "script/script.h"
#include "uint256.h"
#include "pubkey.h"
#include "script/script.h"
#include "util/match.h"
#include "zcash/address/orchard.hpp"
#include "zcash/address/sapling.hpp"
@ -21,15 +23,6 @@ const unsigned char ZCASH_UFVK_ID_PERSONAL[BLAKE2bPersonalBytes] =
namespace libzcash {
/** Protocol addresses that can receive funds in a transaction. */
typedef std::variant<SproutPaymentAddress, SaplingPaymentAddress> RawAddress;
class InvalidEncoding {
public:
friend bool operator==(const InvalidEncoding &a, const InvalidEncoding &b) { return true; }
friend bool operator<(const InvalidEncoding &a, const InvalidEncoding &b) { return true; }
};
class UnknownReceiver {
public:
uint32_t typecode;
@ -240,27 +233,36 @@ public:
/** Addresses that can appear in a string encoding. */
typedef std::variant<
InvalidEncoding,
CKeyID,
CScriptID,
SproutPaymentAddress,
SaplingPaymentAddress,
UnifiedAddress> PaymentAddress;
/** Viewing keys that can have a string encoding. */
typedef std::variant<
InvalidEncoding,
SproutViewingKey,
SaplingExtendedFullViewingKey,
UnifiedFullViewingKey> ViewingKey;
/** Spending keys that can have a string encoding. */
typedef std::variant<
InvalidEncoding,
SproutSpendingKey,
SaplingExtendedSpendingKey> SpendingKey;
class HasShieldedRecipient {
public:
bool operator()(const CKeyID& p2pkh) { return false; }
bool operator()(const CScriptID& p2sh) { return false; }
bool operator()(const SproutPaymentAddress& addr) { return true; }
bool operator()(const SaplingPaymentAddress& addr) { return true; }
// unified addresses must contain a shielded receiver, so we
// consider this to be safe by construction
bool operator()(const UnifiedAddress& addr) { return true; }
};
class AddressInfoFromSpendingKey {
public:
std::pair<std::string, PaymentAddress> operator()(const SproutSpendingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const struct SaplingExtendedSpendingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const InvalidEncoding&) const;
};
class AddressInfoFromViewingKey {
@ -272,20 +274,10 @@ public:
std::pair<std::string, PaymentAddress> operator()(const SproutViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const struct SaplingExtendedFullViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const UnifiedFullViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const InvalidEncoding&) const;
};
} //namespace libzcash
/** Check whether a PaymentAddress is not an InvalidEncoding. */
bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr);
/** Check whether a ViewingKey is not an InvalidEncoding. */
bool IsValidViewingKey(const libzcash::ViewingKey& vk);
/** Check whether a SpendingKey is not an InvalidEncoding. */
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey);
/**
* Gets the typecode for the given UA receiver.
*/
@ -299,43 +291,4 @@ public:
uint32_t operator()(const libzcash::UnknownReceiver &p2pkh) const;
};
/**
* Converts the given UA receiver to a protocol address, if it is a shielded receiver.
*/
class ReceiverToRawAddress {
public:
ReceiverToRawAddress() {}
std::optional<libzcash::RawAddress> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::RawAddress> operator()(const CScriptID &p2sh) const;
std::optional<libzcash::RawAddress> operator()(const CKeyID &p2pkh) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::UnknownReceiver &p2pkh) const;
};
/**
* Returns the protocol address that should be used in transaction outputs.
*/
class RecipientForPaymentAddress {
public:
RecipientForPaymentAddress() {}
std::optional<libzcash::RawAddress> operator()(const libzcash::InvalidEncoding& no) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
/**
* Returns all protocol addresses contained within the given payment address.
*/
class GetRawAddresses {
public:
GetRawAddresses() {}
std::set<libzcash::RawAddress> operator()(const libzcash::InvalidEncoding& no) const;
std::set<libzcash::RawAddress> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::set<libzcash::RawAddress> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::set<libzcash::RawAddress> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
#endif // ZC_ADDRESS_H_