1#!/usr/bin/env python3 2# Copyright (c) 2015-2016 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 6from test_framework.test_framework import BitcoinTestFramework 7from test_framework.util import * 8from test_framework.script import * 9from test_framework.mininode import * 10from test_framework.messages import * 11from test_framework.qtum import * 12import time 13import io 14 15 16class QtumHeaderSpamTest(BitcoinTestFramework): 17 def set_test_params(self): 18 self.setup_clean_chain = True 19 self.num_nodes = 2 20 21 def skip_test_if_missing_module(self): 22 self.skip_if_no_wallet() 23 24 def start_p2p_connection(self): 25 self.p2p_node = self.node.add_p2p_connection(P2PInterface()) 26 27 def _remove_from_staking_prevouts(self, block): 28 for j in range(len(self.staking_prevouts)): 29 prevout = self.staking_prevouts[j] 30 if prevout[0].serialize() == block.prevoutStake.serialize(): 31 self.staking_prevouts.pop(j) 32 break 33 34 def sign_block_with_standard_private_key(self, block): 35 block_sig_key = ECKey() 36 block_sig_key.set(hash256(struct.pack('<I', 0)), False) 37 block.sign_block(block_sig_key) 38 39 def create_pos_block(self, staking_prevouts, parent_block, parent_block_stake_modifier, block_height, block_reward=2000400000000): 40 coinbase = create_coinbase(block_height+1) 41 coinbase.vout[0].nValue = 0 42 coinbase.vout[0].scriptPubKey = b"" 43 coinbase.rehash() 44 block = create_block(parent_block.sha256, coinbase, (parent_block.nTime + 0x10) & 0xfffffff0) 45 if not block.solve_stake(parent_block_stake_modifier, staking_prevouts): 46 return None 47 48 # create a new private key used for block signing. 49 block_sig_key = ECKey() 50 block_sig_key.set(hash256(struct.pack('<I', 0)), False) 51 pubkey = block_sig_key.get_pubkey().get_bytes() 52 scriptPubKey = CScript([pubkey, OP_CHECKSIG]) 53 stake_tx_unsigned = CTransaction() 54 55 stake_tx_unsigned.vin.append(CTxIn(block.prevoutStake)) 56 stake_tx_unsigned.vout.append(CTxOut()) 57 stake_tx_unsigned.vout.append(CTxOut(2000400000000, scriptPubKey)) 58 59 stake_tx_signed_raw_hex = self.node.signrawtransactionwithwallet(bytes_to_hex_str(stake_tx_unsigned.serialize()))['hex'] 60 f = io.BytesIO(hex_str_to_bytes(stake_tx_signed_raw_hex)) 61 stake_tx_signed = CTransaction() 62 stake_tx_signed.deserialize(f) 63 block.vtx.append(stake_tx_signed) 64 block.hashMerkleRoot = block.calc_merkle_root() 65 block.sign_block(block_sig_key) 66 return block 67 68 def calculate_stake_modifier(self, parent_block_modifier, current_block): 69 data = b"" 70 data += ser_uint256(current_block.sha256 if current_block.prevoutStake.serialize() == COutPoint(0, 0xffffffff).serialize() else current_block.prevoutStake.hash) 71 data += ser_uint256(parent_block_modifier) 72 return uint256_from_str(hash256(data)) 73 74 def run_test(self): 75 self.node = self.nodes[0] 76 self.alt_node = self.nodes[1] 77 privkey = byte_to_base58(hash256(struct.pack('<I', 0)), 239) 78 self.node.importprivkey(privkey) 79 self.start_p2p_connection() 80 self.node.setmocktime(int(time.time())-10000) 81 generatesynchronized(self.node, 100+COINBASE_MATURITY, "qSrM9K6FMhZ29Vkp8Rdk8Jp66bbfpjFETq", self.nodes) 82 self.sync_all() 83 disconnect_nodes(self.node, 1) 84 85 self.node.setmocktime(0) 86 self.staking_prevouts = collect_prevouts(self.node) 87 tip = self.node.getblock(self.node.getbestblockhash()) 88 stake_modifier = int(tip['modifier'], 16) 89 block_height = tip['height'] 90 block_raw_hex = self.node.getblock(self.node.getbestblockhash(), False) 91 f = io.BytesIO(hex_str_to_bytes(block_raw_hex)) 92 block = CBlock() 93 block.deserialize(f) 94 block.rehash() 95 96 # Make sure that first sending a header and then announcing its block succeeds 97 block = self.create_pos_block(self.staking_prevouts, block, stake_modifier, block_height) 98 self._remove_from_staking_prevouts(block) 99 block.rehash() 100 self._remove_from_staking_prevouts(block) 101 self.p2p_node.send_message(msg_headers([CBlockHeader(block)])) 102 time.sleep(0.05) 103 assert(self.node.getblockheader(block.hash)) 104 assert_raises_rpc_error(-1, "Block not found on disk", self.node.getblock, block.hash) 105 self.p2p_node.send_message(msg_block(block)) 106 time.sleep(0.05) 107 assert(self.node.getblockheader(block.hash)) 108 assert(self.node.getblock(block.hash)) 109 110 # Make sure that the identical block with only a modified nonce (i.e. the same prevoutStake but different block hash is not accepted) 111 block.nNonce += 1 112 self.sign_block_with_standard_private_key(block) 113 self.p2p_node.send_message(msg_headers([CBlockHeader(block)])) 114 time.sleep(0.05) 115 block.rehash() 116 assert_raises_rpc_error(-5, "Block not found", self.node.getblockheader, block.hash) 117 assert_raises_rpc_error(-5, "Block not found", self.node.getblock, block.hash) 118 119 # Make sure that the chain is still reorgable if presented with a longer chain 120 blocks = [block] 121 child_stake_modifier = self.calculate_stake_modifier(stake_modifier, block) 122 child_block = self.create_pos_block(self.staking_prevouts, block, child_stake_modifier, block_height+1) 123 self._remove_from_staking_prevouts(child_block) 124 child_block.rehash() 125 print(self.alt_node.submitblock(bytes_to_hex_str(block.serialize()))) 126 print(self.alt_node.submitblock(bytes_to_hex_str(child_block.serialize()))) 127 self.node.setmocktime(child_block.nTime-16) 128 connect_nodes(self.node, 1) 129 time.sleep(1) 130 self.node.setmocktime(0) 131 self.alt_node.generate(1) 132 self.sync_all() 133 134 135if __name__ == '__main__': 136 QtumHeaderSpamTest().main() 137