/ test / functional / rpc_preciousblock.py
rpc_preciousblock.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2015-2021 The Bitcoin Core developers
  3  # Distributed under the MIT software license, see the accompanying
  4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5  """Test the preciousblock RPC."""
  6  
  7  from test_framework.test_framework import BitcoinTestFramework
  8  from test_framework.util import (
  9      assert_equal,
 10  )
 11  
 12  def unidirectional_node_sync_via_rpc(node_src, node_dest):
 13      blocks_to_copy = []
 14      blockhash = node_src.getbestblockhash()
 15      while True:
 16          try:
 17              assert len(node_dest.getblock(blockhash, False)) > 0
 18              break
 19          except Exception:
 20              blocks_to_copy.append(blockhash)
 21              blockhash = node_src.getblockheader(blockhash, True)['previousblockhash']
 22      blocks_to_copy.reverse()
 23      for blockhash in blocks_to_copy:
 24          blockdata = node_src.getblock(blockhash, False)
 25          assert node_dest.submitblock(blockdata) in (None, 'inconclusive')
 26  
 27  def node_sync_via_rpc(nodes):
 28      for node_src in nodes:
 29          for node_dest in nodes:
 30              if node_src is node_dest:
 31                  continue
 32              unidirectional_node_sync_via_rpc(node_src, node_dest)
 33  
 34  class PreciousTest(BitcoinTestFramework):
 35      def set_test_params(self):
 36          self.setup_clean_chain = True
 37          self.num_nodes = 3
 38          self.supports_cli = False
 39  
 40      def setup_network(self):
 41          self.setup_nodes()
 42  
 43      def run_test(self):
 44          self.log.info("Ensure submitblock can in principle reorg to a competing chain")
 45          self.generate(self.nodes[0], 1, sync_fun=self.no_op)
 46          assert_equal(self.nodes[0].getblockcount(), 1)
 47          hashZ = self.generate(self.nodes[1], 2, sync_fun=self.no_op)[-1]
 48          assert_equal(self.nodes[1].getblockcount(), 2)
 49          node_sync_via_rpc(self.nodes[0:3])
 50          assert_equal(self.nodes[0].getbestblockhash(), hashZ)
 51  
 52          self.log.info("Mine blocks A-B-C on Node 0")
 53          hashC = self.generate(self.nodes[0], 3, sync_fun=self.no_op)[-1]
 54          assert_equal(self.nodes[0].getblockcount(), 5)
 55          self.log.info("Mine competing blocks E-F-G on Node 1")
 56          hashG = self.generate(self.nodes[1], 3, sync_fun=self.no_op)[-1]
 57          assert_equal(self.nodes[1].getblockcount(), 5)
 58          assert hashC != hashG
 59          self.log.info("Connect nodes and check no reorg occurs")
 60          # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
 61          node_sync_via_rpc(self.nodes[0:2])
 62          self.connect_nodes(0, 1)
 63          assert_equal(self.nodes[0].getbestblockhash(), hashC)
 64          assert_equal(self.nodes[1].getbestblockhash(), hashG)
 65          self.log.info("Make Node0 prefer block G")
 66          self.nodes[0].preciousblock(hashG)
 67          assert_equal(self.nodes[0].getbestblockhash(), hashG)
 68          self.log.info("Make Node0 prefer block C again")
 69          self.nodes[0].preciousblock(hashC)
 70          assert_equal(self.nodes[0].getbestblockhash(), hashC)
 71          self.log.info("Make Node1 prefer block C")
 72          self.nodes[1].preciousblock(hashC)
 73          self.sync_blocks(self.nodes[0:2])  # wait because node 1 may not have downloaded hashC
 74          assert_equal(self.nodes[1].getbestblockhash(), hashC)
 75          self.log.info("Make Node1 prefer block G again")
 76          self.nodes[1].preciousblock(hashG)
 77          assert_equal(self.nodes[1].getbestblockhash(), hashG)
 78          self.log.info("Make Node0 prefer block G again")
 79          self.nodes[0].preciousblock(hashG)
 80          assert_equal(self.nodes[0].getbestblockhash(), hashG)
 81          self.log.info("Make Node1 prefer block C again")
 82          self.nodes[1].preciousblock(hashC)
 83          assert_equal(self.nodes[1].getbestblockhash(), hashC)
 84          self.log.info("Mine another block (E-F-G-)H on Node 0 and reorg Node 1")
 85          self.generate(self.nodes[0], 1, sync_fun=self.no_op)
 86          assert_equal(self.nodes[0].getblockcount(), 6)
 87          self.sync_blocks(self.nodes[0:2])
 88          hashH = self.nodes[0].getbestblockhash()
 89          assert_equal(self.nodes[1].getbestblockhash(), hashH)
 90          self.log.info("Node1 should not be able to prefer block C anymore")
 91          self.nodes[1].preciousblock(hashC)
 92          assert_equal(self.nodes[1].getbestblockhash(), hashH)
 93          self.log.info("Mine competing blocks I-J-K-L on Node 2")
 94          self.generate(self.nodes[2], 4, sync_fun=self.no_op)
 95          assert_equal(self.nodes[2].getblockcount(), 6)
 96          hashL = self.nodes[2].getbestblockhash()
 97          self.log.info("Connect nodes and check no reorg occurs")
 98          node_sync_via_rpc(self.nodes[1:3])
 99          self.connect_nodes(1, 2)
100          self.connect_nodes(0, 2)
101          assert_equal(self.nodes[0].getbestblockhash(), hashH)
102          assert_equal(self.nodes[1].getbestblockhash(), hashH)
103          assert_equal(self.nodes[2].getbestblockhash(), hashL)
104          self.log.info("Make Node1 prefer block L")
105          self.nodes[1].preciousblock(hashL)
106          assert_equal(self.nodes[1].getbestblockhash(), hashL)
107          self.log.info("Make Node2 prefer block H")
108          self.nodes[2].preciousblock(hashH)
109          assert_equal(self.nodes[2].getbestblockhash(), hashH)
110  
111  if __name__ == '__main__':
112      PreciousTest().main()