#!/usr/bin/env python3 # Copyright (c) 2022 The Zcash developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . # # Test the effect of reorgs on the Orchard commitment tree. # from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( BLOSSOM_BRANCH_ID, HEARTWOOD_BRANCH_ID, CANOPY_BRANCH_ID, NU5_BRANCH_ID, assert_equal, get_coinbase_address, nuparams, start_nodes, wait_and_assert_operationid_status, ) from finalsaplingroot import ORCHARD_TREE_EMPTY_ROOT from decimal import Decimal class OrchardReorgTest(BitcoinTestFramework): def __init__(self): super().__init__() self.num_nodes = 4 self.cache_behavior = 'clean' def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ nuparams(BLOSSOM_BRANCH_ID, 1), nuparams(HEARTWOOD_BRANCH_ID, 5), nuparams(CANOPY_BRANCH_ID, 5), nuparams(NU5_BRANCH_ID, 10), '-nurejectoldversions=false', ]] * self.num_nodes) def run_test(self): # Activate NU5 so we can test Orchard. self.nodes[0].generate(10) self.sync_all() # Generate a UA with only an Orchard receiver. account = self.nodes[0].z_getnewaccount()['account'] addr = self.nodes[0].z_getaddressforaccount(account, ['orchard']) assert_equal(addr['account'], account) assert_equal(set(addr['receiver_types']), set(['orchard'])) ua = addr['address'] # Before mining any Orchard notes, finalorchardroot should be the empty Orchard root. assert_equal( ORCHARD_TREE_EMPTY_ROOT, self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'], ) # finalorchardroot should not change if we mine additional blocks without Orchard notes. self.nodes[0].generate(100) self.sync_all() assert_equal( ORCHARD_TREE_EMPTY_ROOT, self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'], ) # Create an Orchard note. recipients = [{'address': ua, 'amount': Decimal('12.5')}] opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders') wait_and_assert_operationid_status(self.nodes[0], opid) # After mining a block, finalorchardroot should have changed. self.sync_all() self.nodes[0].generate(1) self.sync_all() orchardroot_oneleaf = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'] print("Root of Orchard commitment tree with one leaf:", orchardroot_oneleaf) assert(orchardroot_oneleaf != ORCHARD_TREE_EMPTY_ROOT) # finalorchardroot should not change if we mine additional blocks without Orchard notes. self.nodes[0].generate(4) self.sync_all() assert_equal( orchardroot_oneleaf, self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'], ) # Split the network so we can test the effect of a reorg. print("Splitting the network") self.split_network() # Create another Orchard note on node 0. recipients = [{'address': ua, 'amount': Decimal('12.5')}] opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders') wait_and_assert_operationid_status(self.nodes[0], opid) # Mine two blocks on node 0. print("Mining 2 blocks on node 0") self.nodes[0].generate(2) self.sync_all() orchardroot_twoleaf = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'] print("Root of Orchard commitment tree with two leaves:", orchardroot_twoleaf) assert(orchardroot_twoleaf != ORCHARD_TREE_EMPTY_ROOT) assert(orchardroot_twoleaf != orchardroot_oneleaf) # Generate 10 blocks on node 2. print("Mining alternate chain on node 2") self.nodes[2].generate(10) self.sync_all() assert_equal( orchardroot_oneleaf, self.nodes[2].getblock(self.nodes[2].getbestblockhash())['finalorchardroot'], ) # Reconnect the nodes; node 0 will re-org to node 2's chain. print("Re-joining the network so that node 0 reorgs") self.join_network() # Verify that node 0's latest Orchard root matches what we expect. orchardroot_postreorg = self.nodes[0].getblock(self.nodes[2].getbestblockhash())['finalorchardroot'] print("Root of Orchard commitment tree after reorg:", orchardroot_postreorg) assert_equal(orchardroot_postreorg, orchardroot_oneleaf) if __name__ == '__main__': OrchardReorgTest().main()