aboutsummaryrefslogtreecommitdiff
path: root/test/functional/p2p-acceptblock.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/p2p-acceptblock.py')
-rwxr-xr-xtest/functional/p2p-acceptblock.py323
1 files changed, 0 insertions, 323 deletions
diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py
deleted file mode 100755
index 672626f15b..0000000000
--- a/test/functional/p2p-acceptblock.py
+++ /dev/null
@@ -1,323 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2015-2017 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 processing of unrequested blocks.
-
-Setup: two nodes, node0+node1, not connected to each other. Node1 will have
-nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks.
-
-We have one P2PInterface connection to node0 called test_node, and one to node1
-called min_work_node.
-
-The test:
-1. Generate one block on each node, to leave IBD.
-
-2. Mine a new block on each tip, and deliver to each node from node's peer.
- The tip should advance for node0, but node1 should skip processing due to
- nMinimumChainWork.
-
-Node1 is unused in tests 3-7:
-
-3. Mine a block that forks from the genesis block, and deliver to test_node.
- Node0 should not process this block (just accept the header), because it
- is unrequested and doesn't have more or equal work to the tip.
-
-4a,b. Send another two blocks that build on the forking block.
- Node0 should process the second block but be stuck on the shorter chain,
- because it's missing an intermediate block.
-
-4c.Send 288 more blocks on the longer chain (the number of blocks ahead
- we currently store).
- Node0 should process all but the last block (too far ahead in height).
-
-5. Send a duplicate of the block in #3 to Node0.
- Node0 should not process the block because it is unrequested, and stay on
- the shorter chain.
-
-6. Send Node0 an inv for the height 3 block produced in #4 above.
- Node0 should figure out that Node0 has the missing height 2 block and send a
- getdata.
-
-7. Send Node0 the missing block again.
- Node0 should process and the tip should advance.
-
-8. Create a fork which is invalid at a height longer than the current chain
- (ie to which the node will try to reorg) but which has headers built on top
- of the invalid block. Check that we get disconnected if we send more headers
- on the chain the node now knows to be invalid.
-
-9. Test Node1 is able to sync when connected to node0 (which should have sufficient
- work on its chain).
-"""
-
-from test_framework.mininode import *
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-import time
-from test_framework.blocktools import create_block, create_coinbase, create_transaction
-
-class AcceptBlockTest(BitcoinTestFramework):
- def add_options(self, parser):
- parser.add_option("--testbinary", dest="testbinary",
- default=os.getenv("BITCOIND", "bitcoind"),
- help="bitcoind binary to test")
-
- def set_test_params(self):
- self.setup_clean_chain = True
- self.num_nodes = 2
- self.extra_args = [[], ["-minimumchainwork=0x10"]]
-
- def setup_network(self):
- # Node0 will be used to test behavior of processing unrequested blocks
- # from peers which are not whitelisted, while Node1 will be used for
- # the whitelisted case.
- # Node2 will be used for non-whitelisted peers to test the interaction
- # with nMinimumChainWork.
- self.setup_nodes()
-
- def run_test(self):
- # Setup the p2p connections and start up the network thread.
- # test_node connects to node0 (not whitelisted)
- test_node = self.nodes[0].add_p2p_connection(P2PInterface())
- # min_work_node connects to node1 (whitelisted)
- min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())
-
- network_thread_start()
-
- # Test logic begins here
- test_node.wait_for_verack()
- min_work_node.wait_for_verack()
-
- # 1. Have nodes mine a block (leave IBD)
- [ n.generate(1) for n in self.nodes ]
- tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
-
- # 2. Send one block that builds on each tip.
- # This should be accepted by node0
- blocks_h2 = [] # the height 2 blocks on each node's chain
- block_time = int(time.time()) + 1
- for i in range(2):
- blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time))
- blocks_h2[i].solve()
- block_time += 1
- test_node.send_message(msg_block(blocks_h2[0]))
- min_work_node.send_message(msg_block(blocks_h2[1]))
-
- for x in [test_node, min_work_node]:
- x.sync_with_ping()
- assert_equal(self.nodes[0].getblockcount(), 2)
- assert_equal(self.nodes[1].getblockcount(), 1)
- self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
-
- # 3. Send another block that builds on genesis.
- block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time)
- block_time += 1
- block_h1f.solve()
- test_node.send_message(msg_block(block_h1f))
-
- test_node.sync_with_ping()
- tip_entry_found = False
- for x in self.nodes[0].getchaintips():
- if x['hash'] == block_h1f.hash:
- assert_equal(x['status'], "headers-only")
- tip_entry_found = True
- assert(tip_entry_found)
- assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
-
- # 4. Send another two block that build on the fork.
- block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time)
- block_time += 1
- block_h2f.solve()
- test_node.send_message(msg_block(block_h2f))
-
- test_node.sync_with_ping()
- # Since the earlier block was not processed by node, the new block
- # can't be fully validated.
- tip_entry_found = False
- for x in self.nodes[0].getchaintips():
- if x['hash'] == block_h2f.hash:
- assert_equal(x['status'], "headers-only")
- tip_entry_found = True
- assert(tip_entry_found)
-
- # But this block should be accepted by node since it has equal work.
- self.nodes[0].getblock(block_h2f.hash)
- self.log.info("Second height 2 block accepted, but not reorg'ed to")
-
- # 4b. Now send another block that builds on the forking chain.
- block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime+1)
- block_h3.solve()
- test_node.send_message(msg_block(block_h3))
-
- test_node.sync_with_ping()
- # Since the earlier block was not processed by node, the new block
- # can't be fully validated.
- tip_entry_found = False
- for x in self.nodes[0].getchaintips():
- if x['hash'] == block_h3.hash:
- assert_equal(x['status'], "headers-only")
- tip_entry_found = True
- assert(tip_entry_found)
- self.nodes[0].getblock(block_h3.hash)
-
- # But this block should be accepted by node since it has more work.
- self.nodes[0].getblock(block_h3.hash)
- self.log.info("Unrequested more-work block accepted")
-
- # 4c. Now mine 288 more blocks and deliver; all should be processed but
- # the last (height-too-high) on node (as long as its not missing any headers)
- tip = block_h3
- all_blocks = []
- for i in range(288):
- next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime+1)
- next_block.solve()
- all_blocks.append(next_block)
- tip = next_block
-
- # Now send the block at height 5 and check that it wasn't accepted (missing header)
- test_node.send_message(msg_block(all_blocks[1]))
- test_node.sync_with_ping()
- assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash)
- assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash)
-
- # The block at height 5 should be accepted if we provide the missing header, though
- headers_message = msg_headers()
- headers_message.headers.append(CBlockHeader(all_blocks[0]))
- test_node.send_message(headers_message)
- test_node.send_message(msg_block(all_blocks[1]))
- test_node.sync_with_ping()
- self.nodes[0].getblock(all_blocks[1].hash)
-
- # Now send the blocks in all_blocks
- for i in range(288):
- test_node.send_message(msg_block(all_blocks[i]))
- test_node.sync_with_ping()
-
- # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
- for x in all_blocks[:-1]:
- self.nodes[0].getblock(x.hash)
- assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
-
- # 5. Test handling of unrequested block on the node that didn't process
- # Should still not be processed (even though it has a child that has more
- # work).
-
- # The node should have requested the blocks at some point, so
- # disconnect/reconnect first
-
- self.nodes[0].disconnect_p2ps()
- self.nodes[1].disconnect_p2ps()
- network_thread_join()
-
- test_node = self.nodes[0].add_p2p_connection(P2PInterface())
- network_thread_start()
- test_node.wait_for_verack()
-
- test_node.send_message(msg_block(block_h1f))
-
- test_node.sync_with_ping()
- assert_equal(self.nodes[0].getblockcount(), 2)
- self.log.info("Unrequested block that would complete more-work chain was ignored")
-
- # 6. Try to get node to request the missing block.
- # Poke the node with an inv for block at height 3 and see if that
- # triggers a getdata on block 2 (it should if block 2 is missing).
- with mininode_lock:
- # Clear state so we can check the getdata request
- test_node.last_message.pop("getdata", None)
- test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))
-
- test_node.sync_with_ping()
- with mininode_lock:
- getdata = test_node.last_message["getdata"]
-
- # Check that the getdata includes the right block
- assert_equal(getdata.inv[0].hash, block_h1f.sha256)
- self.log.info("Inv at tip triggered getdata for unprocessed block")
-
- # 7. Send the missing block for the third time (now it is requested)
- test_node.send_message(msg_block(block_h1f))
-
- test_node.sync_with_ping()
- assert_equal(self.nodes[0].getblockcount(), 290)
- self.nodes[0].getblock(all_blocks[286].hash)
- assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
- assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[287].hash)
- self.log.info("Successfully reorged to longer chain from non-whitelisted peer")
-
- # 8. Create a chain which is invalid at a height longer than the
- # current chain, but which has more blocks on top of that
- block_289f = create_block(all_blocks[284].sha256, create_coinbase(289), all_blocks[284].nTime+1)
- block_289f.solve()
- block_290f = create_block(block_289f.sha256, create_coinbase(290), block_289f.nTime+1)
- block_290f.solve()
- block_291 = create_block(block_290f.sha256, create_coinbase(291), block_290f.nTime+1)
- # block_291 spends a coinbase below maturity!
- block_291.vtx.append(create_transaction(block_290f.vtx[0], 0, b"42", 1))
- block_291.hashMerkleRoot = block_291.calc_merkle_root()
- block_291.solve()
- block_292 = create_block(block_291.sha256, create_coinbase(292), block_291.nTime+1)
- block_292.solve()
-
- # Now send all the headers on the chain and enough blocks to trigger reorg
- headers_message = msg_headers()
- headers_message.headers.append(CBlockHeader(block_289f))
- headers_message.headers.append(CBlockHeader(block_290f))
- headers_message.headers.append(CBlockHeader(block_291))
- headers_message.headers.append(CBlockHeader(block_292))
- test_node.send_message(headers_message)
-
- test_node.sync_with_ping()
- tip_entry_found = False
- for x in self.nodes[0].getchaintips():
- if x['hash'] == block_292.hash:
- assert_equal(x['status'], "headers-only")
- tip_entry_found = True
- assert(tip_entry_found)
- assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_292.hash)
-
- test_node.send_message(msg_block(block_289f))
- test_node.send_message(msg_block(block_290f))
-
- test_node.sync_with_ping()
- self.nodes[0].getblock(block_289f.hash)
- self.nodes[0].getblock(block_290f.hash)
-
- test_node.send_message(msg_block(block_291))
-
- # At this point we've sent an obviously-bogus block, wait for full processing
- # without assuming whether we will be disconnected or not
- try:
- # Only wait a short while so the test doesn't take forever if we do get
- # disconnected
- test_node.sync_with_ping(timeout=1)
- except AssertionError:
- test_node.wait_for_disconnect()
-
- self.nodes[0].disconnect_p2ps()
- test_node = self.nodes[0].add_p2p_connection(P2PInterface())
-
- network_thread_start()
- test_node.wait_for_verack()
-
- # We should have failed reorg and switched back to 290 (but have block 291)
- assert_equal(self.nodes[0].getblockcount(), 290)
- assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
- assert_equal(self.nodes[0].getblock(block_291.hash)["confirmations"], -1)
-
- # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected
- block_293 = create_block(block_292.sha256, create_coinbase(293), block_292.nTime+1)
- block_293.solve()
- headers_message = msg_headers()
- headers_message.headers.append(CBlockHeader(block_293))
- test_node.send_message(headers_message)
- test_node.wait_for_disconnect()
-
- # 9. Connect node1 to node0 and ensure it is able to sync
- connect_nodes(self.nodes[0], 1)
- sync_blocks([self.nodes[0], self.nodes[1]])
- self.log.info("Successfully synced nodes 1 and 0")
-
-if __name__ == '__main__':
- AcceptBlockTest().main()