diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index f53e0448c..8a2f9cbb1 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -21,7 +21,6 @@ testScripts=( 'wallet_shieldcoinbase_sprout.py' 'wallet_shieldcoinbase_sapling.py' 'wallet_listreceived.py' - 'wallet_mergetoaddress.py' 'wallet.py' 'wallet_overwintertx.py' 'wallet_persistence.py' @@ -30,6 +29,8 @@ testScripts=( 'wallet_addresses.py' 'wallet_sapling.py' 'wallet_listnotes.py' + 'mergetoaddress_sprout.py' + 'mergetoaddress_sapling.py' 'listtransactions.py' 'mempool_resurrect_test.py' 'txn_doublespend.py' diff --git a/qa/rpc-tests/mergetoaddress_helper.py b/qa/rpc-tests/mergetoaddress_helper.py new file mode 100755 index 000000000..011054b30 --- /dev/null +++ b/qa/rpc-tests/mergetoaddress_helper.py @@ -0,0 +1,348 @@ +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Common code for testing z_mergetoaddress before and after sapling activation +# + +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, connect_nodes_bi, fail, \ + initialize_chain_clean, start_node, sync_blocks, sync_mempools, \ + wait_and_assert_operationid_status + +from decimal import Decimal + + +def assert_mergetoaddress_exception(expected_error_msg, merge_to_address_lambda): + try: + merge_to_address_lambda() + fail("Expected exception: %s" % expected_error_msg) + except JSONRPCException as e: + assert_equal(expected_error_msg, e.error['message']) + except Exception as e: + fail("Expected JSONRPCException. Found %s" % repr(e)) + + +class MergeToAddressHelper: + + def setup_chain(self, test): + print("Initializing test directory "+test.options.tmpdir) + initialize_chain_clean(test.options.tmpdir, 4) + + def setup_network(self, test, additional_args=[]): + args = ['-debug=zrpcunsafe', '-experimentalfeatures', '-zmergetoaddress'] + args += additional_args + test.nodes = [] + test.nodes.append(start_node(0, test.options.tmpdir, args)) + test.nodes.append(start_node(1, test.options.tmpdir, args)) + args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-zmergetoaddress', '-mempooltxinputlimit=7'] + args2 += additional_args + test.nodes.append(start_node(2, test.options.tmpdir, args2)) + connect_nodes_bi(test.nodes, 0, 1) + connect_nodes_bi(test.nodes, 1, 2) + connect_nodes_bi(test.nodes, 0, 2) + test.is_network_split = False + test.sync_all() + + def run_test(self, test, addr_type): + print "Mining blocks..." + + test.nodes[0].generate(1) + do_not_shield_taddr = test.nodes[0].getnewaddress() + + test.nodes[0].generate(4) + walletinfo = test.nodes[0].getwalletinfo() + assert_equal(walletinfo['immature_balance'], 50) + assert_equal(walletinfo['balance'], 0) + test.sync_all() + test.nodes[2].generate(1) + test.nodes[2].getnewaddress() + test.nodes[2].generate(1) + test.nodes[2].getnewaddress() + test.nodes[2].generate(1) + test.sync_all() + test.nodes[1].generate(101) + test.sync_all() + assert_equal(test.nodes[0].getbalance(), 50) + assert_equal(test.nodes[1].getbalance(), 10) + assert_equal(test.nodes[2].getbalance(), 30) + + # Shield the coinbase + myzaddr = test.nodes[0].z_getnewaddress(addr_type) + result = test.nodes[0].z_shieldcoinbase("*", myzaddr, 0) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + # Prepare some UTXOs and notes for merging + mytaddr = test.nodes[0].getnewaddress() + mytaddr2 = test.nodes[0].getnewaddress() + mytaddr3 = test.nodes[0].getnewaddress() + result = test.nodes[0].z_sendmany(myzaddr, [ + {'address': do_not_shield_taddr, 'amount': 10}, + {'address': mytaddr, 'amount': 10}, + {'address': mytaddr2, 'amount': 10}, + {'address': mytaddr3, 'amount': 10}, + ], 1, 0) + wait_and_assert_operationid_status(test.nodes[0], result) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + # Merging will fail because from arguments need to be in an array + assert_mergetoaddress_exception( + "JSON value is not an array as expected", + lambda: test.nodes[0].z_mergetoaddress("*", myzaddr)) + + # Merging will fail when trying to spend from watch-only address + test.nodes[2].importaddress(mytaddr) + assert_mergetoaddress_exception( + "Could not find any funds to merge.", + lambda: test.nodes[2].z_mergetoaddress([mytaddr], myzaddr)) + + # Merging will fail because fee is negative + assert_mergetoaddress_exception( + "Amount out of range", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, -1)) + + # Merging will fail because fee is larger than MAX_MONEY + assert_mergetoaddress_exception( + "Amount out of range", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001'))) + + # Merging will fail because fee is larger than sum of UTXOs + assert_mergetoaddress_exception( + "Insufficient funds, have 50.00, which is less than miners fee 999.00", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, 999)) + + # Merging will fail because transparent limit parameter must be at least 0 + assert_mergetoaddress_exception( + "Limit on maximum number of UTXOs cannot be negative", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1)) + + # Merging will fail because transparent limit parameter is absurdly large + assert_mergetoaddress_exception( + "JSON integer out of range", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999)) + + # Merging will fail because shielded limit parameter must be at least 0 + assert_mergetoaddress_exception( + "Limit on maximum number of notes cannot be negative", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1)) + + # Merging will fail because shielded limit parameter is absurdly large + assert_mergetoaddress_exception( + "JSON integer out of range", + lambda: test.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999)) + + # Merging will fail for this specific case where it would spend a fee and do nothing + assert_mergetoaddress_exception( + "Destination address is also the only source address, and all its funds are already merged.", + lambda: test.nodes[0].z_mergetoaddress([mytaddr], mytaddr)) + + # Merge UTXOs from node 0 of value 30, standard fee of 0.00010000 + result = test.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone + assert_equal(test.nodes[0].getbalance(), 10) + assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('39.99990000')) + assert_equal(test.nodes[1].getbalance(), 40) + assert_equal(test.nodes[2].getbalance(), 30) + + # Shield all notes to another z-addr + myzaddr2 = test.nodes[0].z_getnewaddress(addr_type) + result = test.nodes[0].z_mergetoaddress(["ANY_ZADDR"], myzaddr2, 0) + assert_equal(result["mergingUTXOs"], Decimal('0')) + assert_equal(result["remainingUTXOs"], Decimal('0')) + assert_equal(result["mergingNotes"], Decimal('2')) + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + test.sync_all() + blockhash = test.nodes[1].generate(1) + test.sync_all() + + assert_equal(len(test.nodes[0].getblock(blockhash[0])['tx']), 2) + assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) + + # Shield coinbase UTXOs from any node 2 taddr, and set fee to 0 + result = test.nodes[2].z_shieldcoinbase("*", myzaddr, 0) + wait_and_assert_operationid_status(test.nodes[2], result['opid']) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + assert_equal(test.nodes[0].getbalance(), 10) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('30')) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) + assert_equal(test.nodes[1].getbalance(), 60) + assert_equal(test.nodes[2].getbalance(), 0) + + # Merge all notes from node 0 into a node 0 taddr, and set fee to 0 + result = test.nodes[0].z_mergetoaddress(["ANY_ZADDR"], mytaddr, 0) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + assert_equal(test.nodes[0].getbalance(), Decimal('79.99990000')) + assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) + assert_equal(test.nodes[0].z_getbalance(mytaddr), Decimal('69.99990000')) + assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), 0) + assert_equal(test.nodes[1].getbalance(), 70) + assert_equal(test.nodes[2].getbalance(), 0) + + # Merge all node 0 UTXOs together into a node 1 taddr, and set fee to 0 + test.nodes[1].getnewaddress() # Ensure we have an empty address + n1taddr = test.nodes[1].getnewaddress() + result = test.nodes[0].z_mergetoaddress(["ANY_TADDR"], n1taddr, 0) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + assert_equal(test.nodes[0].getbalance(), 0) + assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), 0) + assert_equal(test.nodes[0].z_getbalance(mytaddr), 0) + assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) + assert_equal(test.nodes[1].getbalance(), Decimal('159.99990000')) + assert_equal(test.nodes[1].z_getbalance(n1taddr), Decimal('79.99990000')) + assert_equal(test.nodes[2].getbalance(), 0) + + # Generate 800 regular UTXOs on node 0, and 20 regular UTXOs on node 2 + mytaddr = test.nodes[0].getnewaddress() + n2taddr = test.nodes[2].getnewaddress() + test.nodes[1].generate(1000) + test.sync_all() + for i in range(800): + test.nodes[1].sendtoaddress(mytaddr, 1) + for i in range(20): + test.nodes[1].sendtoaddress(n2taddr, 1) + test.nodes[1].generate(1) + test.sync_all() + + # Merging the 800 UTXOs will occur over two transactions, since max tx size is 100,000 bytes. + # We don't verify mergingTransparentValue as UTXOs are not selected in any specific order, so value can change on each test run. + # We set an unrealistically high limit parameter of 99999, to verify that max tx size will constrain the number of UTXOs. + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 99999) + assert_equal(result["mergingUTXOs"], Decimal('662')) + assert_equal(result["remainingUTXOs"], Decimal('138')) + assert_equal(result["mergingNotes"], Decimal('0')) + assert_equal(result["mergingShieldedValue"], Decimal('0')) + assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["remainingShieldedValue"], Decimal('0')) + remainingTransparentValue = result["remainingTransparentValue"] + opid1 = result['opid'] + + # Verify that UTXOs are locked (not available for selection) by queuing up another merging operation + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 0) + assert_equal(result["mergingUTXOs"], Decimal('138')) + assert_equal(result["mergingTransparentValue"], Decimal(remainingTransparentValue)) + assert_equal(result["remainingUTXOs"], Decimal('0')) + assert_equal(result["remainingTransparentValue"], Decimal('0')) + assert_equal(result["mergingNotes"], Decimal('0')) + assert_equal(result["mergingShieldedValue"], Decimal('0')) + assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["remainingShieldedValue"], Decimal('0')) + opid2 = result['opid'] + + # wait for both aysnc operations to complete + wait_and_assert_operationid_status(test.nodes[0], opid1) + wait_and_assert_operationid_status(test.nodes[0], opid2) + + # sync_all() invokes sync_mempool() but node 2's mempool limit will cause tx1 and tx2 to be rejected. + # So instead, we sync on blocks and mempool for node 0 and node 1, and after a new block is generated + # which mines tx1 and tx2, all nodes will have an empty mempool which can then be synced. + sync_blocks(test.nodes[:2]) + sync_mempools(test.nodes[:2]) + # Generate enough blocks to ensure all transactions are mined + while test.nodes[1].getmempoolinfo()['size'] > 0: + test.nodes[1].generate(1) + test.sync_all() + + # Verify maximum number of UTXOs which node 2 can shield is limited by option -mempooltxinputlimit + # This option is used when the limit parameter is set to 0. + result = test.nodes[2].z_mergetoaddress([n2taddr], myzaddr, Decimal('0.0001'), 0) + assert_equal(result["mergingUTXOs"], Decimal('7')) + assert_equal(result["remainingUTXOs"], Decimal('13')) + assert_equal(result["mergingNotes"], Decimal('0')) + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(test.nodes[2], result['opid']) + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + # Verify maximum number of UTXOs which node 0 can shield is set by default limit parameter of 50 + mytaddr = test.nodes[0].getnewaddress() + for i in range(100): + test.nodes[1].sendtoaddress(mytaddr, 1) + test.nodes[1].generate(1) + test.sync_all() + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001')) + assert_equal(result["mergingUTXOs"], Decimal('50')) + assert_equal(result["remainingUTXOs"], Decimal('50')) + assert_equal(result["mergingNotes"], Decimal('0')) + # Remaining notes are only counted if we are trying to merge any notes + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + + # Verify maximum number of UTXOs which node 0 can shield can be set by the limit parameter + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001'), 33) + assert_equal(result["mergingUTXOs"], Decimal('33')) + assert_equal(result["remainingUTXOs"], Decimal('17')) + assert_equal(result["mergingNotes"], Decimal('0')) + # Remaining notes are only counted if we are trying to merge any notes + assert_equal(result["remainingNotes"], Decimal('0')) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit + sync_blocks(test.nodes[:2]) + sync_mempools(test.nodes[:2]) + test.nodes[1].generate(1) + test.sync_all() + + # Verify maximum number of notes which node 0 can shield can be set by the limit parameter + # Also check that we can set off a second merge before the first one is complete + + # myzaddr has 5 notes at this point + result1 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) + result2 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) + + # First merge should select from all notes + assert_equal(result1["mergingUTXOs"], Decimal('0')) + # Remaining UTXOs are only counted if we are trying to merge any UTXOs + assert_equal(result1["remainingUTXOs"], Decimal('0')) + assert_equal(result1["mergingNotes"], Decimal('2')) + assert_equal(result1["remainingNotes"], Decimal('3')) + + # Second merge should ignore locked notes + assert_equal(result2["mergingUTXOs"], Decimal('0')) + assert_equal(result2["remainingUTXOs"], Decimal('0')) + assert_equal(result2["mergingNotes"], Decimal('2')) + assert_equal(result2["remainingNotes"], Decimal('1')) + wait_and_assert_operationid_status(test.nodes[0], result1['opid']) + wait_and_assert_operationid_status(test.nodes[0], result2['opid']) + + test.sync_all() + test.nodes[1].generate(1) + test.sync_all() + + # Shield both UTXOs and notes to a z-addr + result = test.nodes[0].z_mergetoaddress(["*"], myzaddr, 0, 10, 2) + assert_equal(result["mergingUTXOs"], Decimal('10')) + assert_equal(result["remainingUTXOs"], Decimal('7')) + assert_equal(result["mergingNotes"], Decimal('2')) + assert_equal(result["remainingNotes"], Decimal('1')) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit + sync_blocks(test.nodes[:2]) + sync_mempools(test.nodes[:2]) + test.nodes[1].generate(1) + test.sync_all() diff --git a/qa/rpc-tests/mergetoaddress_sapling.py b/qa/rpc-tests/mergetoaddress_sapling.py new file mode 100755 index 000000000..dc6c50c17 --- /dev/null +++ b/qa/rpc-tests/mergetoaddress_sapling.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from mergetoaddress_helper import MergeToAddressHelper + + +class MergeToAddressSapling (BitcoinTestFramework): + helper = MergeToAddressHelper() + + def setup_chain(self): + self.helper.setup_chain(self) + + def setup_network(self, split=False): + self.helper.setup_network(self, [ + '-nuparams=5ba81b19:100', # Overwinter + '-nuparams=76b809bb:100', # Sapling + ]) + + def run_test(self): + self.helper.run_test(self, 'sapling') + + +if __name__ == '__main__': + MergeToAddressSapling().main() diff --git a/qa/rpc-tests/mergetoaddress_sprout.py b/qa/rpc-tests/mergetoaddress_sprout.py new file mode 100755 index 000000000..00b42c3ff --- /dev/null +++ b/qa/rpc-tests/mergetoaddress_sprout.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from mergetoaddress_helper import MergeToAddressHelper + + +class MergeToAddressSprout (BitcoinTestFramework): + helper = MergeToAddressHelper() + + def setup_chain(self): + self.helper.setup_chain(self) + + def setup_network(self, split=False): + self.helper.setup_network(self) + + def run_test(self): + self.helper.run_test(self, 'sprout') + + +if __name__ == '__main__': + MergeToAddressSprout().main()