Add RPC test for the Orchard commitment tree bug on first NU5 testnet
This commit is contained in:
parent
68d6c3ddc3
commit
9a60cdeed9
|
@ -60,6 +60,7 @@ BASE_SCRIPTS= [
|
||||||
'wallet_persistence.py',
|
'wallet_persistence.py',
|
||||||
'wallet_listnotes.py',
|
'wallet_listnotes.py',
|
||||||
# vv Tests less than 60s vv
|
# vv Tests less than 60s vv
|
||||||
|
'orchard_reorg.py',
|
||||||
'fundrawtransaction.py',
|
'fundrawtransaction.py',
|
||||||
'reorg_limit.py',
|
'reorg_limit.py',
|
||||||
'mempool_limit.py',
|
'mempool_limit.py',
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
#!/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,
|
||||||
|
connect_nodes_bi,
|
||||||
|
get_coinbase_address,
|
||||||
|
nuparams,
|
||||||
|
start_nodes,
|
||||||
|
stop_nodes,
|
||||||
|
sync_blocks,
|
||||||
|
wait_and_assert_operationid_status,
|
||||||
|
wait_bitcoinds,
|
||||||
|
)
|
||||||
|
|
||||||
|
from finalsaplingroot import ORCHARD_TREE_EMPTY_ROOT
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
class OrchardReorgTest(BitcoinTestFramework):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.num_nodes = 4
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
|
||||||
|
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',
|
||||||
|
'-experimentalfeatures',
|
||||||
|
'-orchardwallet',
|
||||||
|
# '-debug',
|
||||||
|
]] * 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['pools']), set(['orchard']))
|
||||||
|
ua = addr['unifiedaddress']
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
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)
|
||||||
|
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")
|
||||||
|
# We can't use `self.join_network()` because the coinbase-spending second Orchard
|
||||||
|
# transaction doesn't propagate from node 1's mempool to node 2 on restart. Inline
|
||||||
|
# the block-syncing parts here.
|
||||||
|
assert self.is_network_split
|
||||||
|
stop_nodes(self.nodes)
|
||||||
|
wait_bitcoinds()
|
||||||
|
self.nodes = self.setup_nodes()
|
||||||
|
connect_nodes_bi(self.nodes, 1, 2)
|
||||||
|
sync_blocks(self.nodes[1:3])
|
||||||
|
connect_nodes_bi(self.nodes, 0, 1)
|
||||||
|
connect_nodes_bi(self.nodes, 2, 3)
|
||||||
|
self.is_network_split = False
|
||||||
|
sync_blocks(self.nodes)
|
||||||
|
|
||||||
|
# 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()
|
Loading…
Reference in New Issue