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:
commit
6118432712
|
@ -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.
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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()
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__':
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
28
src/init.cpp
28
src/init.cpp
|
@ -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"));
|
||||
|
|
117
src/key_io.cpp
117
src/key_io.cpp
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
46
src/miner.h
46
src/miner.h
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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_
|
||||
|
|
Loading…
Reference in New Issue