Changeset View
Changeset View
Standalone View
Standalone View
test/functional/sendheaders-selfish-mining.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
File Mode | null | 100755 |
#!/usr/bin/env python3 | |||||
# Copyright (c) 2018 The Bitcoin ABC developers | |||||
# Distributed under the MIT software license, see the accompanying | |||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | |||||
# TODO DOCSTRING | |||||
"""An example functional test | |||||
The module-level docstring should include a high-level description of | |||||
what the test is doing. It's the first thing people see when they open | |||||
the file and should give the reader information about *what* the test | |||||
is testing and *how* it's being tested | |||||
""" | |||||
# Imports should be in PEP8 ordering (std library first, then third party | |||||
# libraries then local imports). | |||||
from collections import defaultdict | |||||
import time | |||||
# Avoid wildcard * imports if possible | |||||
from test_framework.blocktools import (create_block, create_coinbase) | |||||
from test_framework.mininode import ( | |||||
NetworkThread, | |||||
NodeConn, | |||||
NodeConnCB, | |||||
msg_block, | |||||
) | |||||
from test_framework.test_framework import BitcoinTestFramework | |||||
from test_framework.util import ( | |||||
assert_equal, | |||||
p2p_port, | |||||
wait_until, | |||||
) | |||||
class DissuadeSelfishMiningTests(BitcoinTestFramework): | |||||
def set_test_params(self): | |||||
self.setup_clean_chain = True | |||||
self.num_nodes = 2 | |||||
def get_chaintip_hashes(self, node): | |||||
hashes = [] | |||||
for tip in node.getchaintips(): | |||||
hashes.append(tip['hash']) | |||||
return hashes | |||||
def run_test(self): | |||||
node = NodeConnCB() | |||||
connections = [] | |||||
connections.append( | |||||
NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node)) | |||||
node.add_connection(connections[0]) | |||||
# Start up network handling in another thread. This needs to be called | |||||
# after the P2P connections have been created. | |||||
NetworkThread().start() | |||||
# wait_for_verack ensures that the P2P connection is fully up. | |||||
node.wait_for_verack() | |||||
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)] | |||||
self.sync_all([self.nodes[0:1]]) | |||||
# A selfish miner sending out headers in response to an honest block | |||||
# will not get the block relayed, as the honest block was seen first. | |||||
common_block = int(self.nodes[0].getbestblockhash(), 16) | |||||
block_time = self.nodes[0].getblock( | |||||
self.nodes[0].getbestblockhash())['time'] + 1 | |||||
height = self.nodes[0].getblockcount() + 1 | |||||
selfish_block1 = create_block( | |||||
common_block, create_coinbase(height), block_time) | |||||
selfish_block1.solve() | |||||
block_time += 1 | |||||
honest_block1 = create_block( | |||||
common_block, create_coinbase(height), block_time) | |||||
honest_block1.solve() | |||||
# Selfishly mined block gets broadcasted immediately after the honest block | |||||
node.send_message(msg_block(honest_block1)) | |||||
node.send_and_ping(msg_block(selfish_block1)) | |||||
actual_tip = int(self.nodes[0].getbestblockhash(), 16) | |||||
# Node0 should have both blocks as chaintips, with the honest block as the active tip | |||||
assert(honest_block1.hash in self.get_chaintip_hashes(self.nodes[0])) | |||||
assert(selfish_block1.hash in self.get_chaintip_hashes(self.nodes[0])) | |||||
assert_equal(actual_tip, honest_block1.sha256) | |||||
common_block = actual_tip | |||||
block_time += 1 | |||||
height += 1 | |||||
# A selfish miner withholding a block for too long will allow it to be replaced | |||||
# as the active chain tip by an honestly-mined block that was received at a more | |||||
# accurate time compared to the block timestamp. | |||||
selfish_block2 = create_block( | |||||
common_block, create_coinbase(height), block_time) | |||||
selfish_block2.solve() | |||||
block_time += 20 | |||||
honest_block2 = create_block( | |||||
common_block, create_coinbase(height), block_time) | |||||
honest_block2.solve() | |||||
# Wait for enough time to pass so that the selfish block is identified (>10s) | |||||
time.sleep(20) | |||||
node.send_and_ping(msg_block(selfish_block2)) | |||||
node.send_and_ping(msg_block(honest_block2)) | |||||
actual_tip = int(self.nodes[1].getbestblockhash(), 16) | |||||
# selfish block is no longer the active tip | |||||
assert_equal(actual_tip, honest_block2.sha256) | |||||
if __name__ == '__main__': | |||||
DissuadeSelfishMiningTests().main() |