def run_test(self):
        print "Mining blocks..."
        self.nodes[0].generate(101)
        self.sync_all()

        # Send some ZEC to a Sprout address
        tAddr = get_coinbase_address(self.nodes[0])
        sproutAddr = self.nodes[0].z_getnewaddress('sprout')
        saplingAddr = self.nodes[0].z_getnewaddress('sapling')

        opid = self.nodes[0].z_sendmany(tAddr, [{
            "address": sproutAddr,
            "amount": Decimal('10')
        }], 1, 0)
        wait_and_assert_operationid_status(self.nodes[0], opid)
        self.nodes[0].generate(1)
        self.sync_all()

        assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('10'))
        assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0'))

        # Migrate
        self.nodes[0].z_setmigration(True)
        print "Mining to block 494..."
        self.nodes[0].generate(392)  # 102 -> 494
        self.sync_all()

        # At 494 we should have no async operations
        assert_equal(0, len(self.nodes[0].z_getoperationstatus()),
                     "num async operations at 494")

        self.nodes[0].generate(1)
        self.sync_all()

        # At 495 we should have an async operation
        operationstatus = self.nodes[0].z_getoperationstatus()
        print "migration operation: {}".format(operationstatus)
        assert_equal(1, len(operationstatus), "num async operations at 495")
        assert_equal('saplingmigration', operationstatus[0]['method'])
        assert_equal(500, operationstatus[0]['target_height'])

        result = wait_and_assert_operationid_status_result(
            self.nodes[0], operationstatus[0]['id'])
        print "result: {}".format(result)
        assert_equal('saplingmigration', result['method'])
        assert_equal(500, result['target_height'])
        assert_equal(1, result['result']['num_tx_created'])

        assert_equal(0, len(self.nodes[0].getrawmempool()),
                     "mempool size at 495")

        self.nodes[0].generate(3)
        self.sync_all()

        # At 498 the mempool will be empty and no funds will have moved
        assert_equal(0, len(self.nodes[0].getrawmempool()),
                     "mempool size at 498")
        assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('10'))
        assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0'))

        self.nodes[0].generate(1)
        self.sync_all()

        # At 499 there will be a transaction in the mempool and the note will be locked
        assert_equal(1, len(self.nodes[0].getrawmempool()),
                     "mempool size at 499")
        assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('0'))
        assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0'))
        assert_true(self.nodes[0].z_getbalance(saplingAddr, 0) > Decimal('0'),
                    "Unconfirmed sapling")

        self.nodes[0].generate(1)
        self.sync_all()

        # At 500 funds will have moved
        sprout_balance = self.nodes[0].z_getbalance(sproutAddr)
        sapling_balance = self.nodes[0].z_getbalance(saplingAddr)
        print "sprout balance: {}, sapling balance: {}".format(
            sprout_balance, sapling_balance)
        assert_true(sprout_balance < Decimal('10'),
                    "Should have less Sprout funds")
        assert_true(sapling_balance > Decimal('0'),
                    "Should have more Sapling funds")
        assert_true(sprout_balance + sapling_balance, Decimal('9.9999'))
    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[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(), 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)

        # Send will fail because we are enforcing the consensus rule that
        # coinbase utxos can only be sent to a zaddr.
        errorString = ""
        try:
            self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1)
        except JSONRPCException as e:
            errorString = e.error['message']
        assert_equal("Coinbase funds can only be sent to a zaddr" in errorString, True)

        # Prepare to send taddr->zaddr
        mytaddr = get_coinbase_address(self.nodes[0])
        myzaddr = self.nodes[0].z_getnewaddress('sprout')

        # 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)

        wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient 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.
        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.76533211 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)

        # Test that the returned status object contains a params field with the operation's input parameters
        assert_equal(error_result["method"], "z_sendmany")
        params = error_result["params"]
        assert_equal(params["fee"], Decimal('0.0001')) # default
        assert_equal(params["minconf"], Decimal('1')) # default
        assert_equal(params["fromaddress"], mytaddr)
        assert_equal(params["amounts"][0]["address"], myzaddr)
        assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))

        # Add viewing key for myzaddr to Node 3
        myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
        self.nodes[3].z_importviewingkey(myviewingkey, "no")

        # This send will succeed.  We send two coinbase utxos totalling 20.0 less a fee of 0.00010000, with no change.
        shieldvalue = Decimal('20.0') - Decimal('0.0001')
        recipients = []
        recipients.append({"address":myzaddr, "amount": shieldvalue})
        myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
        self.sync_all()

        # Verify that z_listunspent can return a note that has zero confirmations
        results = self.nodes[0].z_listunspent()
        assert(len(results) == 0)
        results = self.nodes[0].z_listunspent(0) # set minconf to zero
        assert(len(results) == 1)
        assert_equal(results[0]["address"], myzaddr)
        assert_equal(results[0]["amount"], shieldvalue)
        assert_equal(results[0]["confirmations"], 0)

        # Mine the tx
        self.nodes[1].generate(1)
        self.sync_all()

        # Verify that z_listunspent returns one note which has been confirmed
        results = self.nodes[0].z_listunspent()
        assert(len(results) == 1)
        assert_equal(results[0]["address"], myzaddr)
        assert_equal(results[0]["amount"], shieldvalue)
        assert_equal(results[0]["confirmations"], 1)
        assert_equal(results[0]["spendable"], True)

        # Verify that z_listunspent returns note for watchonly address on node 3.
        results = self.nodes[3].z_listunspent(1, 999, True)
        assert(len(results) == 1)
        assert_equal(results[0]["address"], myzaddr)
        assert_equal(results[0]["amount"], shieldvalue)
        assert_equal(results[0]["confirmations"], 1)
        assert_equal(results[0]["spendable"], False)

        # Verify that z_listunspent returns error when address spending key from node 0 is not available in wallet of node 1.
        try:
            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)

        # 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)
        finished_line = check_node_log(self, 0, myopid + ": z_sendmany finished", False)
        assert(initialized_line < finished_line)

        # check balances (the z_sendmany consumes 3 coinbase utxos)
        resp = self.nodes[0].z_gettotalbalance()
        assert_equal(Decimal(resp["transparent"]), Decimal('20.0'))
        assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
        assert_equal(Decimal(resp["total"]), Decimal('39.9999'))

        # The Sprout value pool should reflect the send
        sproutvalue = shieldvalue
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # A custom fee of 0 is okay.  Here the node will send the note value back to itself.
        recipients = []
        recipients.append({"address":myzaddr, "amount": Decimal('19.9999')})
        myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0'))
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()
        resp = self.nodes[0].z_gettotalbalance()
        assert_equal(Decimal(resp["transparent"]), Decimal('20.0'))
        assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
        assert_equal(Decimal(resp["total"]), Decimal('39.9999'))

        # The Sprout value pool should be unchanged
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # convert note to transparent funds
        unshieldvalue = Decimal('10.0')
        recipients = []
        recipients.append({"address":mytaddr, "amount": unshieldvalue})
        myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
        assert(mytxid is not None)
        self.sync_all()

        # check that priority of the tx sending from a zaddr is not 0
        mempool = self.nodes[0].getrawmempool(True)
        assert(Decimal(mempool[mytxid]['startingpriority']) >= Decimal('1000000000000'))

        self.nodes[1].generate(1)
        self.sync_all()

        # check balances
        sproutvalue -= unshieldvalue + Decimal('0.0001')
        resp = self.nodes[0].z_gettotalbalance()
        assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
        assert_equal(Decimal(resp["private"]), Decimal('9.9998'))
        assert_equal(Decimal(resp["total"]), Decimal('39.9998'))
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # 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.
        # At this point in time, unspent notes all have a value of 10.0 and standard z_sendmany fee is 0.0001.
        recipients = []
        amount = Decimal('10.0') - Decimal('0.00010000') - 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)")

        # Send will fail because send amount is too big, even when including coinbase utxos
        errorString = ""
        try:
            self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 99999)
        except JSONRPCException as e:
            errorString = e.error['message']
        assert_equal("Insufficient funds" in errorString, True)

        # z_sendmany will fail because of insufficient funds
        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.0001")
        myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
        wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient shielded funds, have 9.9998, need 10000.0001")

        # Send will fail because of insufficient funds unless sender uses coinbase utxos
        try:
            self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 21)
        except JSONRPCException as e:
            errorString = e.error['message']
        assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True)

        # Verify that mempools accept tx with joinsplits which have at least the default z_sendmany fee.
        # If this test passes, it confirms that issue #1851 has been resolved, where sending from
        # a zaddr to 1385 taddr recipients fails because the default fee was considered too low
        # given the tx size, resulting in mempool rejection.
        errorString = ''
        recipients = []
        num_t_recipients = 2500
        amount_per_recipient = Decimal('0.00000546') # dust threshold
        # Note that regtest chainparams does not require standard tx, so setting the amount to be
        # less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection.
        start_time = timeit.default_timer()
        for i in range(0,num_t_recipients):
            newtaddr = self.nodes[2].getnewaddress()
            recipients.append({"address":newtaddr, "amount":amount_per_recipient})
        elapsed = timeit.default_timer() - start_time
        print("...invoked getnewaddress() {} times in {} seconds".format(num_t_recipients, elapsed))

        # Issue #2263 Workaround START
        # HTTP connection to node 0 may fall into a state, during the few minutes it takes to process
        # loop above to create new addresses, that when z_sendmany is called with a large amount of
        # rpc data in recipients, the connection fails with a 'broken pipe' error.  Making a RPC call
        # to node 0 before calling z_sendmany appears to fix this issue, perhaps putting the HTTP
        # connection into a good state to handle a large amount of data in recipients.
        self.nodes[0].getinfo()
        # Issue #2263 Workaround END

        myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
        try:
            wait_and_assert_operationid_status(self.nodes[0], myopid)
        except JSONRPCException as e:
            print("JSONRPC error: "+e.error['message'])
            assert(False)
        except Exception as e:
            print("Unexpected exception caught during testing: ", e.error['message'], str(sys.exc_info()[0]))
            assert(False)

        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        # check balance
        node2balance = amount_per_recipient * num_t_recipients
        sproutvalue -= node2balance + Decimal('0.0001')
        assert_equal(self.nodes[2].getbalance(), node2balance)
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # Send will fail because fee is negative
        try:
            self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1)
        except JSONRPCException as e:
            errorString = e.error['message']
        assert_equal("Amount out of range" in errorString, True)

        # Send will fail because fee is larger than MAX_MONEY
        try:
            self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('21000000.00000001'))
        except JSONRPCException as e:
            errorString = e.error['message']
        assert_equal("Amount out of range" in errorString, True)

        # Send will fail because fee is larger than sum of outputs
        try:
            self.nodes[0].z_sendmany(myzaddr, recipients, 1, (amount_per_recipient * num_t_recipients) + Decimal('0.00000001'))
        except JSONRPCException as e:
            errorString = e.error['message']
        assert_equal("is greater than the sum of outputs" in errorString, True)

        # Send will succeed because the balance of non-coinbase utxos is 10.0
        try:
            self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 9)
        except JSONRPCException:
            assert(False)

        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        # check balance
        node2balance = node2balance + 9
        assert_equal(self.nodes[2].getbalance(), node2balance)

        # Check that chained joinsplits in a single tx are created successfully.
        recipients = []
        num_recipients = 3
        amount_per_recipient = Decimal('0.002')
        minconf = 1
        send_amount = num_recipients * amount_per_recipient
        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')
            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)
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        # check balances and unspent notes
        resp = self.nodes[2].z_gettotalbalance()
        assert_equal(Decimal(resp["private"]), send_amount)

        notes = self.nodes[2].z_listunspent()
        sum_of_notes = sum([note["amount"] for note in notes])
        assert_equal(Decimal(resp["private"]), sum_of_notes)

        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)

        notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
        sum_of_notes = sum([note["amount"] for note in notes])
        assert_equal(Decimal(resp), sum_of_notes)
class WalletProtectCoinbaseTest(BitcoinTestFramework):
    def setup_chain(self):
        print("Initializing test directory " + self.options.tmpdir)
        initialize_chain_clean(self.options.tmpdir, 4)

    # Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true.
    def setup_network(self, split=False):
        self.nodes = start_nodes(
            4,
            self.options.tmpdir,
            extra_args=[['-regtestprotectcoinbase', '-debug=zrpcunsafe']] * 4)
        connect_nodes_bi(self.nodes, 0, 1)
        connect_nodes_bi(self.nodes, 1, 2)
        connect_nodes_bi(self.nodes, 0, 2)
        connect_nodes_bi(self.nodes, 0, 3)
        self.is_network_split = False
        self.sync_all()

    def run_test(self):
        print "Mining blocks..."

        self.nodes[0].generate(4)

        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], 40)
        assert_equal(walletinfo['balance'], 0)

        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(), 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)

        # Send will fail because we are enforcing the consensus rule that
        # coinbase utxos can only be sent to a zaddr.
        errorString = ""
        try:
            self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1)
        except JSONRPCException, e:
            errorString = e.error['message']
        assert_equal(
            "Coinbase funds can only be sent to a zaddr" in errorString, True)

        # Prepare to send taddr->zaddr
        mytaddr = get_coinbase_address(self.nodes[0])
        myzaddr = self.nodes[0].z_getnewaddress('sprout')

        # 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)

        wait_and_assert_operationid_status(
            self.nodes[3], myopid, "failed",
            "Insufficient funds, no UTXOs found for taddr from address.", 10)

        # This send will fail because our wallet does not allow any change when protecting a coinbase utxo,
        # as it's currently not possible to specify a change address in z_sendmany.
        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.76533211 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)

        # Test that the returned status object contains a params field with the operation's input parameters
        assert_equal(error_result["method"], "z_sendmany")
        params = error_result["params"]
        assert_equal(params["fee"], Decimal('0.0001'))  # default
        assert_equal(params["minconf"], Decimal('1'))  # default
        assert_equal(params["fromaddress"], mytaddr)
        assert_equal(params["amounts"][0]["address"], myzaddr)
        assert_equal(params["amounts"][0]["amount"], Decimal('1.23456789'))

        # Add viewing key for myzaddr to Node 3
        myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
        self.nodes[3].z_importviewingkey(myviewingkey, "no")

        # This send will succeed.  We send two coinbase utxos totalling 20.0 less a fee of 0.00010000, with no change.
        shieldvalue = Decimal('20.0') - Decimal('0.0001')
        recipients = []
        recipients.append({"address": myzaddr, "amount": shieldvalue})
        myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
        self.sync_all()

        # Verify that z_listunspent can return a note that has zero confirmations
        results = self.nodes[0].z_listunspent()
        assert (len(results) == 0)
        results = self.nodes[0].z_listunspent(0)  # set minconf to zero
        assert (len(results) == 1)
        assert_equal(results[0]["address"], myzaddr)
        assert_equal(results[0]["amount"], shieldvalue)
        assert_equal(results[0]["confirmations"], 0)

        # Mine the tx
        self.nodes[1].generate(1)
        self.sync_all()

        # Verify that z_listunspent returns one note which has been confirmed
        results = self.nodes[0].z_listunspent()
        assert (len(results) == 1)
        assert_equal(results[0]["address"], myzaddr)
        assert_equal(results[0]["amount"], shieldvalue)
        assert_equal(results[0]["confirmations"], 1)
        assert_equal(results[0]["spendable"], True)

        # Verify that z_listunspent returns note for watchonly address on node 3.
        results = self.nodes[3].z_listunspent(1, 999, True)
        assert (len(results) == 1)
        assert_equal(results[0]["address"], myzaddr)
        assert_equal(results[0]["amount"], shieldvalue)
        assert_equal(results[0]["confirmations"], 1)
        assert_equal(results[0]["spendable"], False)

        # Verify that z_listunspent returns error when address spending key from node 0 is not available in wallet of node 1.
        try:
            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)

        # Verify that debug=zrpcunsafe logs params, and that full txid is associated with opid
        logpath = self.options.tmpdir + "/node0/regtest/debug.log"
        logcounter = 0
        with open(logpath, "r") as myfile:
            logdata = myfile.readlines()
        for logline in logdata:
            if myopid + ": z_sendmany initialized" in logline and mytaddr in logline and myzaddr in logline:
                assert_equal(logcounter, 0)  # verify order of log messages
                logcounter = logcounter + 1
            if myopid + ": z_sendmany finished" in logline and mytxid in logline:
                assert_equal(logcounter, 1)
                logcounter = logcounter + 1
        assert_equal(logcounter, 2)

        # check balances (the z_sendmany consumes 3 coinbase utxos)
        resp = self.nodes[0].z_gettotalbalance()
        assert_equal(Decimal(resp["transparent"]), Decimal('20.0'))
        assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
        assert_equal(Decimal(resp["total"]), Decimal('39.9999'))

        # The Sprout value pool should reflect the send
        sproutvalue = shieldvalue
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # A custom fee of 0 is okay.  Here the node will send the note value back to itself.
        recipients = []
        recipients.append({"address": myzaddr, "amount": Decimal('19.9999')})
        myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1,
                                          Decimal('0.0'))
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()
        resp = self.nodes[0].z_gettotalbalance()
        assert_equal(Decimal(resp["transparent"]), Decimal('20.0'))
        assert_equal(Decimal(resp["private"]), Decimal('19.9999'))
        assert_equal(Decimal(resp["total"]), Decimal('39.9999'))

        # The Sprout value pool should be unchanged
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # convert note to transparent funds
        unshieldvalue = Decimal('10.0')
        recipients = []
        recipients.append({"address": mytaddr, "amount": unshieldvalue})
        myopid = self.nodes[0].z_sendmany(myzaddr, recipients)
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)
        assert (mytxid is not None)
        self.sync_all()

        # check that priority of the tx sending from a zaddr is not 0
        mempool = self.nodes[0].getrawmempool(True)
        assert (Decimal(mempool[mytxid]['startingpriority']) >=
                Decimal('1000000000000'))

        self.nodes[1].generate(1)
        self.sync_all()

        # check balances
        sproutvalue -= unshieldvalue + Decimal('0.0001')
        resp = self.nodes[0].z_gettotalbalance()
        assert_equal(Decimal(resp["transparent"]), Decimal('30.0'))
        assert_equal(Decimal(resp["private"]), Decimal('9.9998'))
        assert_equal(Decimal(resp["total"]), Decimal('39.9998'))
        check_value_pool(self.nodes[0], 'sprout', sproutvalue)

        # 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.
        # At this point in time, unspent notes all have a value of 10.0 and standard z_sendmany fee is 0.0001.
        recipients = []
        amount = Decimal('10.0') - Decimal('0.00010000') - 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)"
        )

        # Send will fail because send amount is too big, even when including coinbase utxos
        errorString = ""
        try:
            self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 99999)
        except JSONRPCException, e:
            errorString = e.error['message']
    def run_migration_test(self, node, sproutAddr, saplingAddr, target_height):
        # Make sure we are in a good state to run the test
        assert_equal(102,
                     node.getblockcount() % 500,
                     "Should be at block 102 % 500")
        assert_equal(node.z_getbalance(sproutAddr), Decimal('10'))
        assert_equal(node.z_getbalance(saplingAddr), Decimal('0'))
        check_migration_status(node, saplingAddr, DISABLED_BEFORE_MIGRATION)

        # Migrate
        node.z_setmigration(True)
        print("Mining to block 494 % 500...")
        node.generate(392)  # 102 % 500 -> 494 % 500
        self.sync_all()

        # At 494 % 500 we should have no async operations
        assert_equal(0, len(node.z_getoperationstatus()),
                     "num async operations at 494 % 500")
        check_migration_status(node, saplingAddr, ENABLED_BEFORE_MIGRATION)

        node.generate(1)
        self.sync_all()

        # At 495 % 500 we should have an async operation
        operationstatus = node.z_getoperationstatus()
        print("migration operation: {}".format(operationstatus))
        assert_equal(1, len(operationstatus),
                     "num async operations at 495  % 500")
        assert_equal('saplingmigration', operationstatus[0]['method'])
        assert_equal(target_height, operationstatus[0]['target_height'])

        result = wait_and_assert_operationid_status_result(
            node, operationstatus[0]['id'])
        print("result: {}".format(result))
        assert_equal('saplingmigration', result['method'])
        assert_equal(target_height, result['target_height'])
        assert_equal(1, result['result']['num_tx_created'])
        assert_equal(1, len(result['result']['migration_txids']))
        assert_true(
            Decimal(result['result']['amount_migrated']) > Decimal('0'))

        assert_equal(0, len(node.getrawmempool()), "mempool size at 495 % 500")

        node.generate(3)
        self.sync_all()

        # At 498 % 500 the mempool will be empty and no funds will have moved
        assert_equal(0, len(node.getrawmempool()), "mempool size at 498 % 500")
        assert_equal(node.z_getbalance(sproutAddr), Decimal('10'))
        assert_equal(node.z_getbalance(saplingAddr), Decimal('0'))

        node.generate(1)
        self.sync_all()

        # At 499 % 500 there will be a transaction in the mempool and the note will be locked
        mempool = node.getrawmempool()
        print("mempool: {}".format(mempool))
        assert_equal(1, len(mempool), "mempool size at 499 % 500")
        assert_equal(node.z_getbalance(sproutAddr), Decimal('0'))
        assert_equal(node.z_getbalance(saplingAddr), Decimal('0'))
        assert_true(
            node.z_getbalance(saplingAddr, 0) > Decimal('0'),
            "Unconfirmed sapling balance at 499 % 500")
        # Check that unmigrated amount + unfinalized = starting balance - fee
        status = node.z_getmigrationstatus()
        print("status: {}".format(status))
        assert_equal(
            Decimal('9.9999'),
            Decimal(status['unmigrated_amount']) +
            Decimal(status['unfinalized_migrated_amount']))

        # The transaction in the mempool should be the one listed in migration_txids,
        # and it should expire at the next 450 % 500.
        assert_equal(1, len(status['migration_txids']))
        txid = status['migration_txids'][0]
        assert_equal(txid, mempool[0])
        tx = node.getrawtransaction(txid, 1)
        assert_equal(target_height + 450, tx['expiryheight'])

        node.generate(1)
        self.sync_all()

        # At 0 % 500 funds will have moved
        sprout_balance = node.z_getbalance(sproutAddr)
        sapling_balance = node.z_getbalance(saplingAddr)
        print("sprout balance: {}, sapling balance: {}".format(
            sprout_balance, sapling_balance))
        assert_true(sprout_balance < Decimal('10'),
                    "Should have less Sprout funds")
        assert_true(sapling_balance > Decimal('0'),
                    "Should have more Sapling funds")
        assert_true(sprout_balance + sapling_balance, Decimal('9.9999'))

        check_migration_status(node, saplingAddr, DURING_MIGRATION)
        # At 10 % 500 the transactions will be considered 'finalized'
        node.generate(10)
        self.sync_all()
        check_migration_status(node, saplingAddr, AFTER_MIGRATION)
        # Check exact migration status amounts to make sure we account for fee
        status = node.z_getmigrationstatus()
        assert_equal(sprout_balance, Decimal(status['unmigrated_amount']))
        assert_equal(sapling_balance,
                     Decimal(status['finalized_migrated_amount']))
    def run_migration_test(self, node, sproutAddr, saplingAddr, target_height):
        # Make sure we are in a good state to run the test
        assert_equal(102, node.getblockcount() % 500, "Should be at block 102 % 500")
        assert_equal(node.z_getbalance(sproutAddr), Decimal('10'))
        assert_equal(node.z_getbalance(saplingAddr), Decimal('0'))
        check_migration_status(node, False, saplingAddr, True, False, False, 0, 0)

        # Migrate
        node.z_setmigration(True)
        print("Mining to block 494 % 500...")
        node.generate(392)  # 102 % 500 -> 494 % 500
        self.sync_all()

        # At 494 % 500 we should have no async operations
        assert_equal(0, len(node.z_getoperationstatus()), "num async operations at 494 % 500")
        check_migration_status(node, True, saplingAddr, True, False, False, 0, 0)

        node.generate(1)
        self.sync_all()

        # At 495 % 500 we should have an async operation
        operationstatus = node.z_getoperationstatus()
        print("migration operation: {}".format(operationstatus))
        assert_equal(1, len(operationstatus), "num async operations at 495  % 500")
        assert_equal('saplingmigration', operationstatus[0]['method'])
        assert_equal(target_height, operationstatus[0]['target_height'])

        result = wait_and_assert_operationid_status_result(node, operationstatus[0]['id'])
        print("result: {}".format(result))
        assert_equal('saplingmigration', result['method'])
        assert_equal(target_height, result['target_height'])
        assert_equal(1, result['result']['num_tx_created'])

        assert_equal(0, len(node.getrawmempool()), "mempool size at 495 % 500")

        node.generate(3)
        self.sync_all()

        # At 498 % 500 the mempool will be empty and no funds will have moved
        assert_equal(0, len(node.getrawmempool()), "mempool size at 498 % 500")
        assert_equal(node.z_getbalance(sproutAddr), Decimal('10'))
        assert_equal(node.z_getbalance(saplingAddr), Decimal('0'))

        node.generate(1)
        self.sync_all()

        # At 499 % 500 there will be a transaction in the mempool and the note will be locked
        assert_equal(1, len(node.getrawmempool()), "mempool size at 499 % 500")
        assert_equal(node.z_getbalance(sproutAddr), Decimal('0'))
        assert_equal(node.z_getbalance(saplingAddr), Decimal('0'))
        assert_true(node.z_getbalance(saplingAddr, 0) > Decimal('0'), "Unconfirmed sapling balance at 499 % 500")
        # Check that unmigrated amount + unfinalized = starting balance - fee
        status = node.z_getmigrationstatus()
        assert_equal(Decimal('9.9999'), Decimal(status['unmigrated_amount']) + Decimal(status['unfinalized_migrated_amount']))

        node.generate(1)
        self.sync_all()

        # At 0 % 500 funds will have moved
        sprout_balance = node.z_getbalance(sproutAddr)
        sapling_balance = node.z_getbalance(saplingAddr)
        print("sprout balance: {}, sapling balance: {}".format(sprout_balance, sapling_balance))
        assert_true(sprout_balance < Decimal('10'), "Should have less Sprout funds")
        assert_true(sapling_balance > Decimal('0'), "Should have more Sapling funds")
        assert_true(sprout_balance + sapling_balance, Decimal('9.9999'))

        check_migration_status(node, True, saplingAddr, True, True, False, 0, 1)
        # At 10 % 500 the transactions will be considered 'finalized'
        node.generate(10)
        self.sync_all()
        check_migration_status(node, True, saplingAddr, True, False, True, 1, 1)
        # Check exact migration status amounts to make sure we account for fee
        status = node.z_getmigrationstatus()
        assert_equal(sprout_balance, Decimal(status['unmigrated_amount']))
        assert_equal(sapling_balance, Decimal(status['finalized_migrated_amount']))