From 5805ac836c5847bc54cbef3e71154d022ca18eda Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 26 Aug 2016 23:05:26 +0200 Subject: [PATCH] Add preciousblock tests Rebased, improved and extended by Luke-Jr. --- qa/pull-tester/rpc-tests.py | 1 + qa/rpc-tests/preciousblock.py | 116 ++++++++++++++++++++++++++++ qa/rpc-tests/test_framework/util.py | 10 +++ 3 files changed, 127 insertions(+) create mode 100755 qa/rpc-tests/preciousblock.py diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 92cce6aad..bd5c5f8e1 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -140,6 +140,7 @@ testScripts = [ 'invalidtxrequest.py', 'abandonconflict.py', 'p2p-versionbits-warning.py', + 'preciousblock.py', 'importprunedfunds.py', 'signmessages.py', 'p2p-compactblocks.py', diff --git a/qa/rpc-tests/preciousblock.py b/qa/rpc-tests/preciousblock.py new file mode 100755 index 000000000..854dcc725 --- /dev/null +++ b/qa/rpc-tests/preciousblock.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test PreciousBlock code +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +def unidirectional_node_sync_via_rpc(node_src, node_dest): + blocks_to_copy = [] + blockhash = node_src.getbestblockhash() + while True: + try: + assert(len(node_dest.getblock(blockhash, False)) > 0) + break + except: + blocks_to_copy.append(blockhash) + blockhash = node_src.getblockheader(blockhash, True)['previousblockhash'] + blocks_to_copy.reverse() + for blockhash in blocks_to_copy: + blockdata = node_src.getblock(blockhash, False) + assert(node_dest.submitblock(blockdata) in (None, 'inconclusive')) + +def node_sync_via_rpc(nodes): + for node_src in nodes: + for node_dest in nodes: + if node_src is node_dest: + continue + unidirectional_node_sync_via_rpc(node_src, node_dest) + +class PreciousTest(BitcoinTestFramework): + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 3) + + def setup_network(self): + self.nodes = [] + self.is_network_split = False + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug"])) + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"])) + + def run_test(self): + print("Ensure submitblock can in principle reorg to a competing chain") + self.nodes[0].generate(1) + assert(self.nodes[0].getblockcount() == 1) + (hashY, hashZ) = self.nodes[1].generate(2) + assert(self.nodes[1].getblockcount() == 2) + node_sync_via_rpc(self.nodes[0:3]) + assert(self.nodes[0].getbestblockhash() == hashZ) + + print("Mine blocks A-B-C on Node 0") + (hashA, hashB, hashC) = self.nodes[0].generate(3) + assert(self.nodes[0].getblockcount() == 5) + print("Mine competing blocks E-F-G on Node 1") + (hashE, hashF, hashG) = self.nodes[1].generate(3) + assert(self.nodes[1].getblockcount() == 5) + assert(hashC != hashG) + print("Connect nodes and check no reorg occurs") + # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync) + node_sync_via_rpc(self.nodes[0:2]) + connect_nodes_bi(self.nodes,0,1) + assert(self.nodes[0].getbestblockhash() == hashC) + assert(self.nodes[1].getbestblockhash() == hashG) + print("Make Node0 prefer block G") + self.nodes[0].preciousblock(hashG) + assert(self.nodes[0].getbestblockhash() == hashG) + print("Make Node0 prefer block C again") + self.nodes[0].preciousblock(hashC) + assert(self.nodes[0].getbestblockhash() == hashC) + print("Make Node1 prefer block C") + self.nodes[1].preciousblock(hashC) + sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC + assert(self.nodes[1].getbestblockhash() == hashC) + print("Make Node1 prefer block G again") + self.nodes[1].preciousblock(hashG) + assert(self.nodes[1].getbestblockhash() == hashG) + print("Make Node0 prefer block G again") + self.nodes[0].preciousblock(hashG) + assert(self.nodes[0].getbestblockhash() == hashG) + print("Make Node1 prefer block C again") + self.nodes[1].preciousblock(hashC) + assert(self.nodes[1].getbestblockhash() == hashC) + print("Mine another block (E-F-G-)H on Node 0 and reorg Node 1") + self.nodes[0].generate(1) + assert(self.nodes[0].getblockcount() == 6) + sync_blocks(self.nodes[0:2]) + hashH = self.nodes[0].getbestblockhash() + assert(self.nodes[1].getbestblockhash() == hashH) + print("Node1 should not be able to prefer block C anymore") + self.nodes[1].preciousblock(hashC) + assert(self.nodes[1].getbestblockhash() == hashH) + print("Mine competing blocks I-J-K-L on Node 2") + self.nodes[2].generate(4) + assert(self.nodes[2].getblockcount() == 6) + hashL = self.nodes[2].getbestblockhash() + print("Connect nodes and check no reorg occurs") + node_sync_via_rpc(self.nodes[0:3]) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + assert(self.nodes[0].getbestblockhash() == hashH) + assert(self.nodes[1].getbestblockhash() == hashH) + assert(self.nodes[2].getbestblockhash() == hashL) + print("Make Node1 prefer block L") + self.nodes[1].preciousblock(hashL) + assert(self.nodes[1].getbestblockhash() == hashL) + print("Make Node2 prefer block H") + self.nodes[2].preciousblock(hashH) + assert(self.nodes[2].getbestblockhash() == hashH) + +if __name__ == '__main__': + PreciousTest().main() diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 190fa7f66..bc0b801ff 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -133,6 +133,16 @@ def sync_blocks(rpc_connections, wait=1, timeout=60): timeout -= wait raise AssertionError("Block sync failed") +def sync_chain(rpc_connections, wait=1): + """ + Wait until everybody has the same best block + """ + while True: + counts = [ x.getbestblockhash() for x in rpc_connections ] + if counts == [ counts[0] ]*len(counts): + break + time.sleep(wait) + def sync_mempools(rpc_connections, wait=1, timeout=60): """ Wait until everybody has the same transactions in their memory