コード例 #1
0
    def run_test (self):

        # Test the check_node_log utility function
        string_to_find = "Pastel version"
        check_node_log(self, 1, string_to_find)

        # Node 1 was stopped to check the logs, need to be restarted
        self.nodes[1] = self.start_node_with(1, [])
        connect_nodes(self.nodes[1], 0)

        assert_raises(AssertionError, check_node_log, self, 1, "Will not be found")

        # Need to start node 1 before leaving the test
        self.nodes[1] = self.start_node_with(1, [])
        connect_nodes(self.nodes[1], 0)
コード例 #2
0
    def run_test(self):
        # Sanity-check the test harness
        self.nodes[0].generate(101)
        assert_equal(self.nodes[0].getblockcount(), 101)
        self.sync_all()

        # 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)
        wait_and_assert_operationid_status(self.nodes[0], myopid)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(self.nodes[0].z_getbalance(dest_addr), Decimal('10'))

        # Verify size of shielded pool
        self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(),
                                 Decimal('10'))
        self.assert_pool_balance(self.nodes[1], POOL_NAME.lower(),
                                 Decimal('10'))
        self.assert_pool_balance(self.nodes[2], POOL_NAME.lower(),
                                 Decimal('10'))

        # Relaunch node 0 with in-memory size of value pools set to zero.
        self.restart_and_sync_node(0, TURNSTILE_ARGS)

        # Verify size of shielded pool
        self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(),
                                 Decimal('0'))
        self.assert_pool_balance(self.nodes[1], POOL_NAME.lower(),
                                 Decimal('10'))
        self.assert_pool_balance(self.nodes[2], POOL_NAME.lower(),
                                 Decimal('10'))

        # Node 0 creates an unshielding transaction
        recipients = []
        recipients.append({"address": taddr0, "amount": Decimal('1')})
        myopid = self.nodes[0].z_sendmany(dest_addr, recipients, 1, 0)
        mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid)

        # Verify transaction appears in mempool of nodes
        self.sync_all()
        assert (mytxid in self.nodes[0].getrawmempool())
        assert (mytxid in self.nodes[1].getrawmempool())
        assert (mytxid in self.nodes[2].getrawmempool())

        # Node 0 mines a block
        count = self.nodes[0].getblockcount()
        self.nodes[0].generate(1)
        self.sync_all()

        # Verify the mined block does not contain the unshielding transaction
        block = self.nodes[0].getblock(self.nodes[0].getbestblockhash())
        assert_equal(len(block["tx"]), 1)
        assert_equal(block["height"], count + 1)

        # Stop node 0 and check logs to verify the miner excluded the transaction from the block
        string_to_find = "CreateNewBlock(): tx " + mytxid + " appears to violate " + POOL_NAME.capitalize(
        ) + " turnstile"
        check_node_log(self, 0, string_to_find)

        # Launch node 0 with in-memory size of value pools set to zero.
        self.start_and_sync_node(0, TURNSTILE_ARGS)

        # Node 1 mines a block
        oldhash = self.nodes[0].getbestblockhash()
        self.nodes[1].generate(1)
        newhash = self.nodes[1].getbestblockhash()

        # Verify block contains the unshielding transaction
        assert (mytxid in self.nodes[1].getblock(newhash)["tx"])

        # Verify nodes 1 and 2 have accepted the block as valid
        sync_blocks(self.nodes[1:3])
        sync_mempools(self.nodes[1:3])
        assert_equal(len(self.nodes[1].getrawmempool()), 0)
        assert_equal(len(self.nodes[2].getrawmempool()), 0)

        # Verify node 0 has not accepted the block
        assert_equal(oldhash, self.nodes[0].getbestblockhash())
        assert (mytxid in self.nodes[0].getrawmempool())
        self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(),
                                 Decimal('0'))

        # Verify size of shielded pool
        self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(),
                                 Decimal('0'))
        self.assert_pool_balance(self.nodes[1], POOL_NAME.lower(),
                                 Decimal('9'))
        self.assert_pool_balance(self.nodes[2], POOL_NAME.lower(),
                                 Decimal('9'))

        # Stop node 0 and check logs to verify the block was rejected as a turnstile violation
        string_to_find1 = "ConnectBlock(): turnstile violation in " + POOL_NAME.capitalize(
        ) + " shielded value pool"
        string_to_find2 = "InvalidChainFound: invalid block="
        string_to_find3 = "ConnectTip(): ConnectBlock " + newhash + " failed"
        check_node_log(self, 0, string_to_find1, True)
        check_node_log(self, 0, string_to_find2, False)
        check_node_log(self, 0, string_to_find3, False)
        self.start_and_sync_node(0)

        assert_equal(newhash, self.nodes[0].getbestblockhash())
コード例 #3
0
    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)
コード例 #4
0
    def run_test(self):
        # Generate a Sapling address for node 1
        node1_zaddr = self.nodes[1].z_getnewaddress('sapling')

        self.nodes[1].stop()
        bitcoind_processes[1].wait()
        self.nodes[1] = self.start_node_with(1, [
            "-mineraddress=%s" % node1_zaddr,
        ])
        connect_nodes(self.nodes[1], 0)

        # Node 0 can mine blocks, because it is targeting a transparent address
        print("Mining block with node 0")
        self.nodes[0].generate(1)
        self.sync_all()
        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], 3802400)
        assert_equal(walletinfo['balance'], 0)

        # Node 1 cannot mine blocks, because it is targeting a Sapling address
        # but Heartwood is not yet active
        print("Attempting to mine block with node 1")
        assert_raises(JSONRPCException, self.nodes[1].generate, 1)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 0)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 0)

        # Stop node 1 and check logs to verify the block was rejected correctly
        string_to_find = "CheckTransaction(): coinbase has output descriptions"
        check_node_log(self, 1, string_to_find)

        # Restart node 1
        self.nodes[1] = self.start_node_with(
            1, ["-mineraddress=%s" % node1_zaddr])
        connect_nodes(self.nodes[1], 0)

        # Activate Heartwood
        print("Activating Heartwood")
        self.nodes[0].generate(8)
        self.sync_all()

        # Node 1 can now mine blocks!
        print("Mining block with node 1")
        self.nodes[1].generate(1)
        self.sync_all()

        # Transparent coinbase outputs are subject to coinbase maturity
        assert_equal(self.nodes[0].getbalance(), Decimal('0'))
        assert_equal(self.nodes[0].z_gettotalbalance()['transparent'], '0.00')
        assert_equal(self.nodes[0].z_gettotalbalance()['private'], '0.00')
        assert_equal(self.nodes[0].z_gettotalbalance()['total'], '0.00')

        # Shielded coinbase outputs are not subject to coinbase maturity
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 97)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 97)
        assert_equal(self.nodes[1].z_gettotalbalance()['private'], '97.00')
        assert_equal(self.nodes[1].z_gettotalbalance()['total'], '97.00')

        # Send from Sapling coinbase to Sapling address and transparent address
        # (to check that a non-empty vout is allowed when spending shielded
        # coinbase)
        print("Sending Sapling coinbase to Sapling address")
        node0_zaddr = self.nodes[0].z_getnewaddress('sapling')
        node0_taddr = self.nodes[0].getnewaddress()
        recipients = []
        recipients.append({"address": node0_zaddr, "amount": Decimal('2')})
        recipients.append({"address": node0_taddr, "amount": Decimal('2')})
        myopid = self.nodes[1].z_sendmany(node1_zaddr, recipients, 1, 0)
        wait_and_assert_operationid_status(self.nodes[1], myopid)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        assert_equal(self.nodes[0].z_getbalance(node0_zaddr), 2)
        assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 93)
コード例 #5
0
    def run_test(self):
        # Generate a Sapling address for node 1
        node1_zaddr = self.nodes[1].z_getnewaddress('sapling')

        self.nodes[1].stop()
        bitcoind_processes[1].wait()
        self.nodes[1] = self.start_node_with(1, [
            "-mineraddress=%s" % node1_zaddr,
        ])
        connect_nodes(self.nodes[1], 0)

        # Node 0 can mine blocks, because it is targeting a transparent address
        print("Mining block with node 0")
        self.nodes[0].generate(1)
        self.sync_all()
        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], 5)
        assert_equal(walletinfo['balance'], 0)

        # Node 1 cannot mine blocks, because it is targeting a Sapling address
        # but Heartwood is not yet active
        print("Attempting to mine block with node 1")
        assert_raises(JSONRPCException, self.nodes[1].generate, 1)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 0)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 0)

        # Stop node 1 and check logs to verify the block was rejected correctly
        string_to_find = "CheckTransaction(): coinbase has output descriptions"
        check_node_log(self, 1, string_to_find)

        # Restart node 1
        self.nodes[1] = self.start_node_with(
            1, ["-mineraddress=%s" % node1_zaddr])
        connect_nodes(self.nodes[1], 0)

        # Activate Heartwood
        print("Activating Heartwood")
        self.nodes[0].generate(8)
        self.sync_all()

        # Node 1 can now mine blocks!
        print("Mining block with node 1")
        self.nodes[1].generate(1)
        self.sync_all()

        # Transparent coinbase outputs are subject to coinbase maturity
        assert_equal(self.nodes[0].getbalance(), Decimal('0'))
        assert_equal(self.nodes[0].z_gettotalbalance()['transparent'], '0.00')
        assert_equal(self.nodes[0].z_gettotalbalance()['private'], '0.00')
        assert_equal(self.nodes[0].z_gettotalbalance()['total'], '0.00')

        # Shielded coinbase outputs are not subject to coinbase maturity
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 5)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 5)
        assert_equal(self.nodes[1].z_gettotalbalance()['private'], '5.00')
        assert_equal(self.nodes[1].z_gettotalbalance()['total'], '5.00')

        # Send from Sapling coinbase to Sapling address and transparent address
        # (to check that a non-empty vout is allowed when spending shielded
        # coinbase)
        print("Sending Sapling coinbase to Sapling address")
        node0_zaddr = self.nodes[0].z_getnewaddress('sapling')
        node0_taddr = self.nodes[0].getnewaddress()
        recipients = []
        recipients.append({"address": node0_zaddr, "amount": Decimal('2')})
        recipients.append({"address": node0_taddr, "amount": Decimal('2')})
        myopid = self.nodes[1].z_sendmany(node1_zaddr, recipients, 1, 0)
        wait_and_assert_operationid_status(self.nodes[1], myopid)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        assert_equal(self.nodes[0].z_getbalance(node0_zaddr), 2)
        assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2)
        assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 1)

        # Generate a Unified Address for node 1
        self.nodes[1].z_getnewaccount()
        node1_addr0 = self.nodes[1].z_getaddressforaccount(0)
        assert_equal(node1_addr0['account'], 0)
        assert_equal(set(node1_addr0['receiver_types']),
                     set(['p2pkh', 'sapling', 'orchard']))
        node1_ua = node1_addr0['address']

        # Set node 1's miner address to the UA
        self.nodes[1].stop()
        bitcoind_processes[1].wait()
        self.nodes[1] = self.start_node_with(1, [
            "-mineraddress=%s" % node1_ua,
        ])
        connect_nodes(self.nodes[1], 0)

        # The UA starts with zero balance.
        assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], {})

        # Node 1 can mine blocks because the miner selects the Sapling receiver
        # of its UA.
        print("Mining block with node 1")
        self.nodes[1].generate(1)
        self.sync_all()

        # The UA balance should show that Sapling funds were received.
        assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], {
            'sapling': {
                'valueZat': 5 * COIN
            },
        })

        # Activate NU5
        print("Activating NU5")
        self.nodes[0].generate(7)
        self.sync_all()

        # Now any block mined by node 1 should use the Orchard receiver of its UA.
        print("Mining block with node 1")
        self.nodes[1].generate(1)
        self.sync_all()
        assert_equal(
            self.nodes[1].z_getbalanceforaccount(0)['pools'],
            {
                'sapling': {
                    'valueZat': 5 * COIN
                },
                # 6.25 ZEC because the FR always ends when Canopy activates, and
                # regtest has no defined funding streams.
                'orchard': {
                    'valueZat': 6.25 * COIN
                },
            })