Exemplo n.º 1
0
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
Exemplo n.º 3
0
    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))