def mine_large_blocks(node, n): # Make a large scriptPubKey for the coinbase transaction. This is OP_RETURN # followed by 950k of OP_NOP. This would be non-standard in a non-coinbase # transaction but is consensus valid. # Set the nTime if this is the first time this function has been called. # A static variable ensures that time is monotonicly increasing and is therefore # different for each block created => blockhash is unique. if "nTimes" not in mine_large_blocks.__dict__: mine_large_blocks.nTime = 0 # Get the block parameters for the first block big_script = CScript([OP_RETURN] + [OP_NOP] * 950000) best_block = node.getblock(node.getbestblockhash()) height = int(best_block["height"]) + 1 mine_large_blocks.nTime = max(mine_large_blocks.nTime, int(best_block["time"])) + 1 previousblockhash = int(best_block["hash"], 16) for _ in range(n): # Build the coinbase transaction (with large scriptPubKey) coinbase_tx = create_coinbase(height) coinbase_tx.vin[0].nSequence = 2**32 - 1 coinbase_tx.vout[0].scriptPubKey = big_script coinbase_tx.rehash() # Build the block block = CBlock() block.nHeaderVersion = best_block["version"] block.hashPrevBlock = previousblockhash block.nTime = mine_large_blocks.nTime block.nBits = int('207fffff', 16) block.nNonce = 0 block.vtx = [coinbase_tx] block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Submit to the node node.submitblock(ToHex(block)) previousblockhash = block.sha256 height += 1 mine_large_blocks.nTime += 1
def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) # OP_TRUE in P2SH address = node.decodescript('51')['p2sh'] # burn script p2sh_script = CScript([OP_HASH160, bytes(20), OP_EQUAL]) prevblockhash = node.getbestblockhash() coinbase = create_coinbase(201) coinbase.vout[1].scriptPubKey = p2sh_script coinbase.rehash() sample_block = CBlock() sample_block.vtx = [coinbase] sample_block.hashPrevBlock = int(prevblockhash, 16) sample_block.nBits = 0x207fffff sample_block.nTime = 1600000036 sample_block.nReserved = 0 sample_block.nHeaderVersion = 1 sample_block.nHeight = 201 sample_block.hashEpochBlock = 0 sample_block.hashMerkleRoot = sample_block.calc_merkle_root() sample_block.hashExtendedMetadata = hash256_int(b'\0') sample_block.update_size() # Using legacy hashing algo block = copy.deepcopy(sample_block) target = uint256_from_compact(block.nBits) block.rehash() while hash256_int( block.serialize()) > target or block.sha256 <= target: block.nNonce += 1 block.rehash() self.fail_block(block, force_send=True, reject_reason='high-hash') del block # Claimed size already excessive (before doing any other checks) block = copy.deepcopy(sample_block) block.nSize = 32_000_001 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-size') del block # Incorrect nBits block = copy.deepcopy(sample_block) block.nBits = 0x207ffffe block.solve() self.fail_block(block, force_send=True, reject_reason='bad-diffbits') del block # Block too old block = copy.deepcopy(sample_block) block.nTime = 1600000035 block.solve() self.fail_block(block, force_send=True, reject_reason='time-too-old') del block # nReserved must be 0 block = copy.deepcopy(sample_block) block.nReserved = 0x0100 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-reserved') del block # nHeaderVersion must be 1 block = copy.deepcopy(sample_block) block.nHeaderVersion = 0 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-version') block.nHeaderVersion = 2 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-version') del block # Incorrect claimed height block = copy.deepcopy(sample_block) block.nHeight = 200 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-height') block.nHeight = 202 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-height') del block # Invalid epoch block block = copy.deepcopy(sample_block) block.hashEpochBlock = 1 block.solve() self.fail_block(block, force_send=True, reject_reason='bad-blk-epoch') del block # Time too far into the future block = copy.deepcopy(sample_block) block.nTime = int(time.time()) + 2 * 60 * 60 + 1 block.solve() self.fail_block(block, force_send=True, reject_reason='time-too-new') del block # Invalid merkle root block = copy.deepcopy(sample_block) block.hashMerkleRoot = 0 block.solve() self.fail_block(block, reject_reason='bad-txnmrklroot') del block # Invalid metadata hash block = copy.deepcopy(sample_block) block.hashExtendedMetadata = 0 block.solve() self.fail_block(block, reject_reason='bad-metadata-hash') del block # Non-empty metadata block = copy.deepcopy(sample_block) block.vMetadata.append(CBlockMetadataField(0, b'')) block.rehash_extended_metadata() block.solve() self.fail_block(block, reject_reason='bad-metadata') del block # Claimed nSize doesn't match actual size block = copy.deepcopy(sample_block) block.nSize = 1 block.solve() self.fail_block(block, reject_reason='blk-size-mismatch') del block block_template = node.getblocktemplate() assert_equal(block_template.pop('capabilities'), ['proposal']) assert_equal(block_template.pop('version'), 1) assert_equal(block_template.pop('previousblockhash'), prevblockhash) assert_equal( block_template.pop('epochblockhash'), '0000000000000000000000000000000000000000000000000000000000000000') assert_equal( block_template.pop('extendedmetadatahash'), '9a538906e6466ebd2617d321f71bc94e56056ce213d366773699e28158e00614') assert_equal(block_template.pop('transactions'), []) assert_equal(block_template.pop('coinbaseaux'), {}) assert_equal(block_template.pop('coinbasevalue'), int(SUBSIDY * COIN)) assert_equal(block_template.pop('coinbasetxn'), {'minerfund': { 'outputs': [] }}) block_template.pop('longpollid') assert_equal( block_template.pop('target'), '7fffff0000000000000000000000000000000000000000000000000000000000') assert_equal(block_template.pop('mintime'), 1600000036) assert_equal(block_template.pop('mutable'), ['time', 'transactions', 'prevblock']) assert_equal(block_template.pop('noncerange'), '00000000ffffffff') assert_equal(block_template.pop('sigoplimit'), 226950) assert_equal(block_template.pop('sizelimit'), 32000000) block_template.pop('curtime') assert_equal(block_template.pop('bits'), '207fffff') assert_equal(block_template.pop('height'), 201) assert_equal(block_template, {}) # Check epoch hash is 0 for the first 20 blocks for height in range(201, 221): block_template = node.getblocktemplate() assert_equal(block_template['epochblockhash'], '00' * 32) block = self.block_from_template(block_template) block.hashEpochBlock = 0 prepare_block(block) node.p2p.send_blocks_and_test([block], node) del block # Move to end of epoch node.generatetoaddress(4819, address) assert_equal(node.getblockcount(), 5039) epochblockhash = node.getbestblockhash() epochblock = node.getblock(epochblockhash) assert_equal(epochblock['epochblockhash'], '00' * 32) # getblocktemplate gives us current tip as epoch block hash block_template = node.getblocktemplate() assert_equal(block_template['epochblockhash'], epochblockhash) assert_equal(block_template['previousblockhash'], epochblockhash) # Using 0 as epoch block hash is now invalid block = self.block_from_template(block_template) block.hashEpochBlock = 0 prepare_block(block) self.fail_block(block, force_send=True, reject_reason='bad-blk-epoch') # Setting current tip as epoch hash makes the block valid block.hashEpochBlock = int(epochblockhash, 16) prepare_block(block) node.p2p.send_blocks_and_test([block], node) del block # getblocktemplate still gives us the same epoch block hash block_template = node.getblocktemplate() assert_equal(block_template['epochblockhash'], epochblockhash) assert_equal(block_template['previousblockhash'], node.getbestblockhash()) # Block after that still requires epoch block hash block = self.block_from_template(block_template) block.hashEpochBlock = int(epochblockhash, 16) prepare_block(block) node.p2p.send_blocks_and_test([block], node) del block # Test 48-bit nTime node.setmocktime( 2**32) # smallest number that does not fit in 32-bit number block_template = node.getblocktemplate() assert_equal(block_template['curtime'], 2**32) block = self.block_from_template(block_template) block.nTime = 2**32 prepare_block(block) node.p2p.send_blocks_and_test([block], node) del block node.setmocktime(2**48 - 1) # biggest possible 48-bit number block_template = node.getblocktemplate() assert_equal(block_template['curtime'], 2**48 - 1) block = self.block_from_template(block_template) block.nTime = 2**48 - 1 prepare_block(block) node.p2p.send_blocks_and_test([block], node) del block
def run_test(self): self.mine_chain() node = self.nodes[0] def assert_submitblock(block, result_str_1, result_str_2=None): block.solve() result_str_2 = result_str_2 or 'duplicate-invalid' assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex())) assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex())) self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], self.chain) assert 'currentblocktx' not in mining_info assert 'currentblocksize' not in mining_info assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download node.generatetoaddress(1, node.get_deterministic_priv_key().address) tmpl = node.getblocktemplate() self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] next_height = int(tmpl["height"]) coinbase_tx = create_coinbase(height=next_height) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2**32 - 2 coinbase_tx.rehash() block = CBlock() block.nHeaderVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.nHeight = next_height block.vtx = [coinbase_tx] prepare_block(block) self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex()) self.log.info( "getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, bad_block.serialize().hex()) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': block.serialize()[:-1].hex(), 'mode': 'proposal' }) self.log.info( "getblocktemplate: Test duplicate transaction, results in 'bad-tx-coinbase'" ) bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) prepare_block(bad_block) assert_template(node, bad_block, 'bad-tx-coinbase') assert_submitblock(bad_block, 'bad-tx-coinbase', 'bad-tx-coinbase') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) prepare_block(bad_block) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2**32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') assert_submitblock(bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header bad_block_sn = bytearray(block.serialize()) # Check metadata is empty (for now) assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 0) # Check num txs = 1, which is behind the header and metadata assert_equal(bad_block_sn[BLOCK_HEADER_SIZE + 1], 1) # Increase num txs by 1 artificially bad_block_sn[BLOCK_HEADER_SIZE + 1] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': bad_block_sn.hex(), 'mode': 'proposal' }) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2**31 - 1 assert_template(node, bad_block, 'time-too-new') assert_submitblock(bad_block, 'time-too-new', 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') assert_submitblock(bad_block, 'time-too-old', 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk') assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found') self.log.info('submitheader tests') assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE)) assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE - 2))) assert_raises_rpc_error( -25, 'Must submit previous header', lambda: node.submitheader( hexdata=super(CBlock, bad_block).serialize().hex())) block.nTime += 1 block.solve() def chain_tip(b_hash, *, status='headers-only', branchlen=1): return { 'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status } assert chain_tip(block.hash) not in node.getchaintips() node.submitheader(hexdata=block.serialize().hex()) assert chain_tip(block.hash) in node.getchaintips() # Noop node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) assert chain_tip(block.hash) in node.getchaintips() bad_block_root = copy.deepcopy(block) bad_block_root.hashMerkleRoot += 2 bad_block_root.solve() assert chain_tip(bad_block_root.hash) not in node.getchaintips() node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() # Should still reject invalid blocks, even if we have the header: assert_equal( node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert_equal( node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert chain_tip(bad_block_root.hash) in node.getchaintips() # We know the header for this invalid block, so should just return # early without error: node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() bad_block_lock = copy.deepcopy(block) bad_block_lock.vtx[0].nLockTime = 2**32 - 1 bad_block_lock.vtx[0].rehash() bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() bad_block_lock.solve() assert_equal( node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal') assert_equal( node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid') # Build a "good" block on top of the submitted bad block bad_block2 = copy.deepcopy(block) bad_block2.hashPrevBlock = bad_block_lock.sha256 bad_block2.solve() assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader( bad_block2).serialize().hex())) # Should reject invalid header right away bad_block_time = copy.deepcopy(block) bad_block_time.nTime = 1 bad_block_time.solve() assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader( hexdata=CBlockHeader(bad_block_time).serialize().hex())) # Should ask for the block from a p2p node, if they announce the header # as well: node.add_p2p_connection(P2PDataStore()) # Drop the first getheaders node.p2p.wait_for_getheaders(timeout=5) node.p2p.send_blocks_and_test(blocks=[block], node=node) # Must be active now: assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() # Building a few blocks should give the same results node.generatetoaddress(10, node.get_deterministic_priv_key().address) assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader( hexdata=CBlockHeader(bad_block_time).serialize().hex())) assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader( bad_block2).serialize().hex())) node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) # valid assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # Sanity check that maxtries supports large integers node.generatetoaddress(1, node.get_deterministic_priv_key().address, pow(2, 32))