zcashd/qa/rpc-tests/sapling_rewind_check.py

110 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright (c) 2014 The Bitcoin Core developers
# Copyright (c) 2020 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
"""
Exercise the chain rewind code at the Blossom boundary.
Test case is:
3 nodes are initialized, two of which are aware of the Blossom network upgrade,
and one of which is not. On each node, the chain is advanced to just prior to
the Blossom activation; height then, the network is split and each branch of
the network produces blocks into the range of the upgraded protocol.
The node that is not aware of Blossom activation is advanced beyond the maximum
reorg length of 99 blocks, then that node is shut down. When the node is
restarted with knowledge of the network activation height the checks on startup
identify a need to reorg to come into agreement with the rest of the network.
However, since the rollback required is greater than the maximum reorg length,
the node shuts down with an error as a precautionary measure. It was noticed in
#4119 that the error message indicated an incorrect computation of the rollback
length. This test reproduces that error, and the associated change in rollback
length computation (40b5d5e3ea4b602c34c4efaba0b9f6171dddfef5) corrects the issue.
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (assert_equal, assert_true,
assert_start_raises_init_error,
start_nodes, start_node, connect_nodes_bi,
bitcoind_processes,
nuparams, OVERWINTER_BRANCH_ID, SAPLING_BRANCH_ID)
import logging
import sys
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, stream=sys.stdout)
HAS_SAPLING = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_ID, 15)]
NO_SAPLING = [nuparams(OVERWINTER_BRANCH_ID, 10), nuparams(SAPLING_BRANCH_ID, 150)]
class SaplingRewindTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 3
self.cache_behavior = 'clean'
# This mirrors how the network was setup in the bash test
def setup_network(self, split=False):
logging.info("Initializing the network in "+self.options.tmpdir)
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[
HAS_SAPLING, # The first two nodes have a correct view of the network,
HAS_SAPLING, # the third will rewind after upgrading.
NO_SAPLING
])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
self.is_network_split=False
self.sync_all()
def run_test(self):
# Generate shared state up to the network split
logging.info("Generating initial blocks.")
self.nodes[0].generate(13)
block14 = self.nodes[0].generate(1)[0]
logging.info("Syncing network after initial generation...")
self.sync_all() # Everyone is still on overwinter
logging.info("Checking overwinter block propagation.")
assert_equal(self.nodes[0].getbestblockhash(), block14)
assert_equal(self.nodes[1].getbestblockhash(), block14)
assert_equal(self.nodes[2].getbestblockhash(), block14)
logging.info("All nodes are on overwinter.")
logging.info("Generating network split...")
self.is_network_split=True
# generate past the boundary into sapling; this will become the "canonical" branch
self.nodes[0].generate(50)
expected = self.nodes[0].getbestblockhash()
# generate blocks into sapling beyond the maximum rewind length (99 blocks)
self.nodes[2].generate(120)
self.sync_all()
assert_true(expected != self.nodes[2].getbestblockhash(), "Split chains have not diverged!")
# Stop the overwinter node to ensure state is flushed to disk.
logging.info("Shutting down lagging node...")
self.nodes[2].stop()
bitcoind_processes[2].wait()
# Restart the nodes, reconnect, and sync the network. This succeeds if "-reindex" is passed.
logging.info("Reconnecting the network...")
# expect an exception; the node will refuse to fully start because its last point of
# agreement with the rest of the network was prior to the network upgrade activation
assert_start_raises_init_error(2, self.options.tmpdir, HAS_SAPLING, "roll back 120")
# restart the node with -reindex to allow the test to complete gracefully,
# otherwise the node shutdown call in test cleanup will throw an error since
# it can't connect
self.nodes[2] = start_node(2, self.options.tmpdir, extra_args=NO_SAPLING + ["-reindex"])
if __name__ == '__main__':
SaplingRewindTest().main()