def get_tests(self):
        if self.tip is None:
            self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
        self.block_time = int(time.time())+1

        '''
        Create a new block with an anyone-can-spend coinbase
        '''
        height = 1
        block = create_block(self.tip, create_coinbase(height), self.block_time)
        self.block_time += 1
        block.solve()
        # Save the coinbase for later
        self.block1 = block
        self.tip = block.sha256
        height += 1
        yield TestInstance([[block, True]])

        '''
        Now we need that block to mature so we can spend the coinbase.
        '''
        test = TestInstance(sync_every_block=False)
        for i in range(100):
            block = create_block(self.tip, create_coinbase(height), self.block_time)
            block.solve()
            self.tip = block.sha256
            self.block_time += 1
            test.blocks_and_transactions.append([block, True])
            height += 1
        yield test

        # b'\x64' is OP_NOTIF
        # Transaction will be rejected with code 16 (REJECT_INVALID)
        tx1 = create_transaction(self.block1.vtx[0], 0, b'\x64', 50 * COIN - 12000)
        yield TestInstance([[tx1, RejectResult(16, b'mandatory-script-verify-flag-failed')]])
Пример #2
0
    def get_tests(self):
        # shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        # Create a new block
        block(0)
        self.chain.save_spendable_output()
        yield self.accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(101):
            block(5000 + i)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(self.chain.get_spendable_output())

        assert_equal(node.getblock(node.getbestblockhash())['height'], 102)

        # create block with height 103, where sig_pushonly is not yet mandatory
        block(1, spend=out[0])
        transaction_op_add_accepted = create_transaction(
            out[1].tx, out[1].n, CScript([1, 1, OP_ADD]), 100000,
            CScript([OP_TRUE]))
        blk_accepted = self.chain.update_block(1,
                                               [transaction_op_add_accepted])
        yield self.accepted()
        assert_equal(node.getblock(node.getbestblockhash())['height'], 103)

        # create block with height 104, where sig_pushonly is mandatory
        block(2, spend=out[2])
        transaction_op_add_rejected = create_transaction(
            out[3].tx, out[3].n, CScript([1, 1, OP_ADD]), 100000,
            CScript([OP_TRUE]))
        self.chain.update_block(2, [transaction_op_add_rejected])
        yield self.rejected(RejectResult(16, b'blk-bad-inputs'))
        assert_equal(node.getblock(node.getbestblockhash())['height'], 103)

        assert_equal(node.getbestblockhash(), blk_accepted.hash)

        # invalidate block with height 103
        node.invalidateblock(format(blk_accepted.sha256, 'x'))

        # tip is now on height 102
        assert_equal(node.getblock(node.getbestblockhash())['height'], 102)

        # transaction_op_add_accepted should not be in mempool (individual transactions are always checked against pushonly)
        assert (transaction_op_add_accepted.hash
                not in set(node.getrawmempool()))
Пример #3
0
    def test_ltor_infringement_detection(self, sync_height):
        txns = self.create_chained_transactions()
        block = self.get_empty_block(sync_height=sync_height)
        # We ensure that the transactions are NOT sorted in the correct order
        block.vtx.extend(sorted(txns, key=lambda _tx: _tx.hash, reverse=True))

        block.compute_merkle_trees()
        block.solve()

        yield TestInstance(
            [[block, RejectResult(16, b'bad-tx-ordering')]],
            test_name='test_ltor_infringement_detection')
Пример #4
0
    def get_tests(self):
        # shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(105):
            block(5000 + i)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(105):
            out.append(self.chain.get_spendable_output())

        assert_equal(node.getblock(node.getbestblockhash())['height'], 105)

        block(1)

        redeem_script = CScript([OP_TRUE, OP_RETURN, b"a" * 5000])

        spend_tx1 = CTransaction()
        spend_tx1.vin.append(
            CTxIn(COutPoint(out[2].tx.sha256, out[2].n), CScript(),
                  0xffffffff))
        spend_tx1.vout.append(CTxOut(500, redeem_script))
        spend_tx1.vout.append(CTxOut(500, redeem_script))
        spend_tx1.calc_sha256()
        self.log.info(spend_tx1.hash)

        self.chain.update_block(1, [spend_tx1])
        yield self.accepted()

        tx1 = CTransaction()
        tx1.vout = [CTxOut(499, CScript([OP_TRUE]))]
        tx1.vin.append(
            CTxIn(COutPoint(spend_tx1.sha256, 0), CScript(), 0xfffffff))
        tx1.vin.append(
            CTxIn(COutPoint(spend_tx1.sha256, 1), CScript(), 0xfffffff))
        tx1.calc_sha256()
        self.log.info(tx1.hash)
        yield TestInstance(
            [[tx1, RejectResult(16, b'bad-txns-inputs-too-large')]])
Пример #5
0
    def get_tests(self):
        # shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        block(0)
        yield self.accepted()

        test, out, _ = prepare_init_chain(self.chain, 101, 100)

        yield test

        assert_equal(node.getblock(node.getbestblockhash())['height'], 102)

        # create block with height 103, where sig_pushonly is not yet mandatory
        block(1, spend=out[0])
        transaction_op_add_accepted = create_transaction(
            out[1].tx, out[1].n, CScript([1, 1, OP_ADD]), 100000,
            CScript([OP_TRUE]))
        blk_accepted = self.chain.update_block(1,
                                               [transaction_op_add_accepted])
        yield self.accepted()
        assert_equal(node.getblock(node.getbestblockhash())['height'], 103)

        # create block with height 104, where sig_pushonly is mandatory
        block(2, spend=out[2])
        transaction_op_add_rejected = create_transaction(
            out[3].tx, out[3].n, CScript([1, 1, OP_ADD]), 100000,
            CScript([OP_TRUE]))
        self.chain.update_block(2, [transaction_op_add_rejected])
        yield self.rejected(RejectResult(16, b'blk-bad-inputs'))
        assert_equal(node.getblock(node.getbestblockhash())['height'], 103)

        assert_equal(node.getbestblockhash(), blk_accepted.hash)

        # invalidate block with height 103
        node.invalidateblock(hashToHex(blk_accepted.sha256))

        # tip is now on height 102
        assert_equal(node.getblock(node.getbestblockhash())['height'], 102)

        # transaction_op_add_accepted should not be in mempool (individual transactions are always checked against pushonly)
        assert (transaction_op_add_accepted.hash
                not in set(node.getrawmempool()))
    def get_tests(self):
        # shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        test, out, _ = prepare_init_chain(self.chain, 105, 105, block_0=False)

        yield test

        assert_equal(node.getblock(node.getbestblockhash())['height'], 105)

        block(1)

        redeem_script = CScript([OP_TRUE, OP_RETURN, b"a" * 5000])

        spend_tx1 = CTransaction()
        spend_tx1.vin.append(
            CTxIn(COutPoint(out[2].tx.sha256, out[2].n), CScript(),
                  0xffffffff))
        spend_tx1.vout.append(CTxOut(500, redeem_script))
        spend_tx1.vout.append(CTxOut(500, redeem_script))
        spend_tx1.calc_sha256()
        self.log.info(spend_tx1.hash)

        self.chain.update_block(1, [spend_tx1])
        yield self.accepted()

        tx1 = CTransaction()
        tx1.vout = [CTxOut(499, CScript([OP_TRUE]))]
        tx1.vin.append(
            CTxIn(COutPoint(spend_tx1.sha256, 0), CScript(), 0xfffffff))
        tx1.vin.append(
            CTxIn(COutPoint(spend_tx1.sha256, 1), CScript(), 0xfffffff))
        tx1.calc_sha256()
        self.log.info(tx1.hash)
        yield TestInstance(
            [[tx1, RejectResult(16, b'bad-txns-inputs-too-large')]])
Пример #7
0
    def get_tests(self):
        node = self.nodes[0]
        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(15):
            n = i + 1
            block(n, spend=out[i], block_size=n * ONE_MEGABYTE // 2)
            yield accepted()

        # Start moving MTP forward
        bfork = block(5555, out[15], block_size=8 * ONE_MEGABYTE)
        bfork.nTime = MONOLITH_START_TIME - 1
        update_block(5555, [])
        yield accepted()

        # Get to one block of the May 15, 2018 HF activation
        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])
        yield test

        # Check that the MTP is just before the configured fork point.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MONOLITH_START_TIME - 1)

        # Before we acivate the May 15, 2018 HF, 8MB is the limit.
        block(4444, spend=out[16], block_size=8 * ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block.
        tip(5104)

        # Actiavte the May 15, 2018 HF
        block(5556)
        yield accepted()

        # Now MTP is exactly the fork time. Bigger blocks are now accepted.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MONOLITH_START_TIME)

        # block of maximal size
        block(17, spend=out[16], block_size=self.excessive_block_size)
        yield accepted()

        # Reject oversized blocks with bad-blk-length error
        block(18, spend=out[17], block_size=self.excessive_block_size + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block.
        tip(17)

        # Accept many sigops
        lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB)
        block(19,
              spend=out[17],
              script=lots_of_checksigs,
              block_size=ONE_MEGABYTE)
        yield accepted()

        block(20,
              spend=out[18],
              script=lots_of_checksigs,
              block_size=ONE_MEGABYTE,
              extra_sigops=1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(19)

        # Accept 40k sigops per block > 1MB and <= 2MB
        block(21,
              spend=out[18],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB,
              block_size=ONE_MEGABYTE + 1)
        yield accepted()

        # Accept 40k sigops per block > 1MB and <= 2MB
        block(22,
              spend=out[19],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB,
              block_size=2 * ONE_MEGABYTE)
        yield accepted()

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(23,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(22)

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(24,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=2 * ONE_MEGABYTE)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(22)

        # Accept 60k sigops per block > 2MB and <= 3MB
        block(25,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB,
              block_size=2 * ONE_MEGABYTE + 1)
        yield accepted()

        # Accept 60k sigops per block > 2MB and <= 3MB
        block(26,
              spend=out[21],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB,
              block_size=3 * ONE_MEGABYTE)
        yield accepted()

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(27,
              spend=out[22],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=2 * ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(26)

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(28,
              spend=out[22],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=3 * ONE_MEGABYTE)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(26)

        # Too many sigops in one txn
        too_many_tx_checksigs = CScript([OP_CHECKSIG] *
                                        (MAX_BLOCK_SIGOPS_PER_MB + 1))
        block(29,
              spend=out[22],
              script=too_many_tx_checksigs,
              block_size=ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-txn-sigops'))

        # Rewind bad block
        tip(26)

        # Generate a key pair to test P2SH sigops count
        private_key = CECKey()
        private_key.set_secretbytes(b"fatstacks")
        public_key = private_key.get_pubkey()

        # P2SH
        # Build the redeem script, hash it, use hash to create the p2sh script
        redeem_script = CScript([public_key] +
                                [OP_2DUP, OP_CHECKSIGVERIFY] * 5 +
                                [OP_CHECKSIG])
        redeem_script_hash = hash160(redeem_script)
        p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])

        # Create a p2sh transaction
        p2sh_tx = self.create_tx(out[22], 1, p2sh_script)

        # Add the transaction to the block
        block(30)
        update_block(30, [p2sh_tx])
        yield accepted()

        # Creates a new transaction using the p2sh transaction included in the
        # last block
        def spend_p2sh_tx(output_script=CScript([OP_TRUE])):
            # Create the transaction
            spent_p2sh_tx = CTransaction()
            spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b''))
            spent_p2sh_tx.vout.append(CTxOut(1, output_script))
            # Sign the transaction using the redeem script
            sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0,
                                          SIGHASH_ALL | SIGHASH_FORKID,
                                          p2sh_tx.vout[0].nValue)
            sig = private_key.sign(sighash) + \
                bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID]))
            spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script])
            spent_p2sh_tx.rehash()
            return spent_p2sh_tx

        # Sigops p2sh limit
        p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \
            redeem_script.GetSigOpCount(True)
        # Too many sigops in one p2sh txn
        too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1))
        block(31, spend=out[23], block_size=ONE_MEGABYTE + 1)
        update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)])
        yield rejected(RejectResult(16, b'bad-txn-sigops'))

        # Rewind bad block
        tip(30)

        # Max sigops in one p2sh txn
        max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit))
        block(32, spend=out[23], block_size=ONE_MEGABYTE + 1)
        update_block(32, [spend_p2sh_tx(max_p2sh_sigops)])
        yield accepted()
    def get_tests(self):
        node = self.nodes[0]

        # First, we generate some coins to spend.
        node.generate(125)

        # Create various outputs using the OP_CHECKDATASIG
        # to check for activation.
        tx_hex = self.create_checkdatasig_tx(25)
        txid = node.sendrawtransaction(tx_hex)
        assert (txid in set(node.getrawmempool()))

        node.generate(1)
        assert (txid not in set(node.getrawmempool()))

        # register the spendable outputs.
        tx = FromHex(CTransaction(), tx_hex)
        tx.rehash()
        spendable_checkdatasigs = [
            PreviousSpendableOutput(tx, i) for i in range(len(tx.vout))
        ]

        def spend_checkdatasig():
            outpoint = spendable_checkdatasigs.pop()
            out = outpoint.tx.vout[outpoint.n]
            tx = CTransaction()
            tx.vin = [CTxIn(COutPoint(outpoint.tx.sha256, outpoint.n))]
            tx.vout = [
                CTxOut(out.nValue, CScript([])),
                CTxOut(0, CScript([random.getrandbits(800), OP_RETURN]))
            ]
            tx.vout[0].nValue -= min(tx.vout[0].nValue / 100,
                                     1000)  # node.calculate_fee(tx)
            tx.rehash()
            return tx

        # Check that transactions using checkdatasig are not accepted yet.
        logging.info("Try to use the checkdatasig opcodes before activation")

        tx0 = spend_checkdatasig()
        tx0_hex = ToHex(tx0)
        assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR,
                                node.sendrawtransaction, tx0_hex)

        # Push MTP forward just before activation.
        logging.info("Pushing MTP just before the activation and check again")
        node.setmocktime(NOV152018_START_TIME)

        # returns a test case that asserts that the current tip was accepted
        def accepted(tip):
            return TestInstance([[tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(tip, reject=None):
            if reject is None:
                return TestInstance([[tip, False]])
            else:
                return TestInstance([[tip, reject]])

        def next_block(block_time):
            # get block height
            blockchaininfo = node.getblockchaininfo()
            height = int(blockchaininfo['blocks'])

            # create the block
            coinbase = create_coinbase(height)
            coinbase.rehash()
            block = create_block(int(node.getbestblockhash(), 16), coinbase,
                                 block_time)

            # Do PoW, which is cheap on regnet
            block.solve()
            return block

        for i in range(6):
            b = next_block(NOV152018_START_TIME + i - 1)
            yield accepted(b)

        # Check again just before the activation time
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            NOV152018_START_TIME - 1)
        assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR,
                                node.sendrawtransaction, tx0_hex)

        def add_tx(block, tx):
            block.vtx.append(tx)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()

        b = next_block(NOV152018_START_TIME + 6)
        add_tx(b, tx0)
        yield rejected(b, RejectResult(16, b'bad-blk-signatures'))

        logging.info("Activates checkdatasig")
        fork_block = next_block(NOV152018_START_TIME + 6)
        yield accepted(fork_block)

        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            NOV152018_START_TIME)

        tx0id = node.sendrawtransaction(tx0_hex)
        assert (tx0id in set(node.getrawmempool()))

        # Transactions can also be included in blocks.
        nov152018forkblock = next_block(NOV152018_START_TIME + 7)
        add_tx(nov152018forkblock, tx0)
        yield accepted(nov152018forkblock)

        logging.info("Cause a reorg that deactivate the checkdatasig opcodes")

        # Invalidate the checkdatasig block, ensure tx0 gets back to the mempool.
        assert (tx0id not in set(node.getrawmempool()))

        node.invalidateblock(format(nov152018forkblock.sha256, 'x'))
        waitFor(3, lambda: tx0id in set(node.getrawmempool()),
                "Transaction shoud be included in the mempool")

        node.invalidateblock(format(fork_block.sha256, 'x'))
        waitFor(3, lambda: tx0id not in set(node.getrawmempool()),
                "Transaction should not be included in the memopool")
    def get_tests(self):
        node = self.nodes[0]
        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions=[]):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(15):
            n = i + 1
            block(n)
            yield accepted()

        # Start moving MTP forward
        bfork = block(5555)
        bfork.nTime = MAGNETIC_ANOMALY_START_TIME - 1
        update_block(5555)
        yield accepted()

        # Get to one block of the Nov 15, 2018 HF activation
        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])
        yield test

        # Check that the MTP is just before the configured fork point.
        assert_equal(node.getblockheader(node.getbestblockhash())['mediantime'],
                     MAGNETIC_ANOMALY_START_TIME - 1)

        # Check that block with small transactions, non push only signatures and
        # non clean stack are still accepted.
        small_tx_block = block(
            4444, out[0], MIN_TX_SIZE - 1, pushonly=False, cleanstack=False)
        assert_equal(len(small_tx_block.vtx[1].serialize()), MIN_TX_SIZE - 1)
        yield accepted()

        # Now MTP is exactly the fork time. Small transaction are now rejected.
        assert_equal(node.getblockheader(node.getbestblockhash())['mediantime'],
                     MAGNETIC_ANOMALY_START_TIME)

        # Now that the for activated, it is not possible to have
        # small transactions anymore.
        small_tx_block = block(4445, out[1], MIN_TX_SIZE - 1)
        assert_equal(len(small_tx_block.vtx[1].serialize()), MIN_TX_SIZE - 1)
        yield rejected(RejectResult(16, b'bad-txns-undersize'))

        # Rewind bad block.
        tip(4444)

        # Now that the for activated, it is not possible to have
        # non push only transactions.
        non_pushonly_tx_block = block(
            4446, out[1], MIN_TX_SIZE, pushonly=False)
        yield rejected(RejectResult(16, b'blk-bad-inputs'))

        # Rewind bad block.
        tip(4444)

        # Now that the for activated, it is not possible to have
        # non clean stack transactions.
        non_cleanstack_tx_block = block(
            4447, out[1], MIN_TX_SIZE, cleanstack=False)
        yield rejected(RejectResult(16, b'blk-bad-inputs'))

        # Rewind bad block.
        tip(4444)

        # But large transactions are still ok.
        large_tx_block = block(3333, out[1], MIN_TX_SIZE)
        assert_equal(len(large_tx_block.vtx[1].serialize()), MIN_TX_SIZE)
        yield accepted()
Пример #10
0
    def get_tests(self):
        if self.tip is None:
            self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
        self.block_time = int(time.time()) + 1
        '''
        Create a new block with an anyone-can-spend coinbase
        '''
        height = 1
        block = create_block(self.tip, create_coinbase(height),
                             self.block_time)
        self.block_time += 1
        block.solve()
        # Save the coinbase for later
        self.block1 = block
        self.tip = block.sha256
        height += 1
        yield TestInstance([[block, True]])
        '''
        Now we need that block to mature so we can spend the coinbase.
        '''
        test = TestInstance(sync_every_block=False)
        for i in range(100):
            block = create_block(self.tip, create_coinbase(height),
                                 self.block_time)
            block.solve()
            self.tip = block.sha256
            self.block_time += 1
            test.blocks_and_transactions.append([block, True])
            height += 1
        yield test
        '''
        Now we use merkle-root malleability to generate an invalid block with
        same blockheader.
        Manufacture a block with 3 transactions (coinbase, spend of prior
        coinbase, spend of that spend).  Duplicate the 3rd transaction to 
        leave merkle root and blockheader unchanged but invalidate the block.
        '''
        block2 = create_block(self.tip, create_coinbase(height),
                              self.block_time)
        self.block_time += 1

        # b'0x51' is OP_TRUE
        tx1 = create_transaction(self.block1.vtx[0], 0, b'\x51', 50 * COIN)
        tx2 = create_transaction(tx1, 0, b'\x51', 50 * COIN)

        block2.vtx.extend([tx1, tx2])
        block2.hashMerkleRoot = block2.calc_merkle_root()
        block2.rehash()
        block2.solve()
        orig_hash = block2.sha256
        block2_orig = copy.deepcopy(block2)

        # Mutate block 2
        block2.vtx.append(tx2)
        assert_equal(block2.hashMerkleRoot, block2.calc_merkle_root())
        assert_equal(orig_hash, block2.rehash())
        assert (block2_orig.vtx != block2.vtx)

        self.tip = block2.sha256
        yield TestInstance([[block2, RejectResult(16, b'bad-txns-duplicate')]])

        # Check transactions for duplicate inputs
        self.log.info("Test duplicate input block.")

        block2_dup = copy.deepcopy(block2_orig)
        block2_dup.vtx[2].vin.append(block2_dup.vtx[2].vin[0])
        block2_dup.vtx[2].rehash()
        block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
        block2_dup.rehash()
        block2_dup.solve()
        yield TestInstance(
            [[block2_dup,
              RejectResult(16, b'bad-txns-inputs-duplicate')],
             [block2_orig, True]])
        height += 1
        '''
        Make sure that a totally screwed up block is not valid.
        '''
        block3 = create_block(self.tip, create_coinbase(height),
                              self.block_time)
        self.block_time += 1
        block3.vtx[0].vout[0].nValue = 100 * COIN  # Too high!
        block3.vtx[0].sha256 = None
        block3.vtx[0].calc_sha256()
        block3.hashMerkleRoot = block3.calc_merkle_root()
        block3.rehash()
        block3.solve()

        yield TestInstance([[block3, RejectResult(16, b'bad-cb-amount')]])
Пример #11
0
    def get_tests(self):
        # shorthand for functions
        block = lambda *a, **kw: self.chain.next_block(
            *a, coinbase_key=self.coinbase_key, simple_output=True, **kw)
        create_and_sign_tx = lambda *a, **kw: create_and_sign_transaction(
            *a,
            private_key=self.coinbase_key,
            **({k: v
                for k, v in kw.items() if not k == 'private_key'}))
        update_block = self.chain.update_block
        tip = self.chain.set_tip
        accepted = self.accepted
        rejected = self.rejected

        self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16))
        save_spendable_output = self.chain.save_spendable_output
        get_spendable_output = self.chain.get_spendable_output

        # Create a new block
        block(0)
        yield accepted()

        test, out, _ = prepare_init_chain(self.chain, 99, 33)

        yield test

        # Start by building a couple of blocks on top (which output is spent is
        # in parentheses):
        #     genesis -> b1 (0) -> b2 (1)
        block(1, spend=out[0])
        save_spendable_output()
        yield accepted()

        block(2, spend=out[1])
        yield accepted()
        save_spendable_output()

        # so fork like this:
        #
        #     genesis -> b1 (0) -> b2 (1)
        #                      \-> b3 (1)
        #
        # Nothing should happen at this point. We saw b2 first so it takes
        # priority.
        tip(1)
        b3 = block(3, spend=out[1])
        txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0)
        yield rejected()

        # Now we add another block to make the alternative chain longer.
        #
        #     genesis -> b1 (0) -> b2 (1)
        #                      \-> b3 (1) -> b4 (2)
        block(4, spend=out[2])
        yield accepted()

        # ... and back to the first chain.
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
        #                      \-> b3 (1) -> b4 (2)
        tip(2)
        block(5, spend=out[2])
        save_spendable_output()
        yield rejected()

        block(6, spend=out[3])
        yield accepted()

        # Try to create a fork that double-spends
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
        #                                          \-> b7 (2) -> b8 (4)
        #                      \-> b3 (1) -> b4 (2)
        tip(5)
        block(7, spend=out[2])
        yield rejected()

        block(8, spend=out[4])
        yield rejected()

        # Try to create a block that has too much fee
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
        #                                                    \-> b9 (4)
        #                      \-> b3 (1) -> b4 (2)
        tip(6)
        block(9, spend=out[4], additional_coinbase_value=1)
        yield rejected(RejectResult(16, b'bad-cb-amount'))

        # Create a fork that ends in a block with too much fee (the one that causes the reorg)
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b10 (3) -> b11 (4)
        #                      \-> b3 (1) -> b4 (2)
        tip(5)
        block(10, spend=out[3])
        yield rejected()

        block(11, spend=out[4], additional_coinbase_value=1)
        yield rejected(RejectResult(16, b'bad-cb-amount'))

        # Try again, but with a valid fork first
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b14 (5)
        #                                              (b12 added last)
        #                      \-> b3 (1) -> b4 (2)
        tip(5)
        b12 = block(12, spend=out[3])
        save_spendable_output()
        b13 = block(13, spend=out[4])
        # Deliver the block header for b12, and the block b13.
        # b13 should be accepted but the tip won't advance until b12 is
        # delivered.
        yield TestInstance([[CBlockHeader(b12), None], [b13, False]])

        save_spendable_output()
        # b14 is invalid, but the node won't know that until it tries to connect
        # Tip still can't advance because b12 is missing
        block(14, spend=out[5], additional_coinbase_value=1)
        yield rejected()

        yield TestInstance([[b12, True, b13.sha256]])  # New tip should be b13.

        # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
        #                      \-> b3 (1) -> b4 (2)

        # Test that a block with a lot of checksigs is okay
        lots_of_checksigs = CScript([OP_CHECKSIG] *
                                    (MAX_BLOCK_SIGOPS_PER_MB - 1))
        tip(13)
        block(15, spend=out[5], script=lots_of_checksigs)
        yield accepted()
        save_spendable_output()

        # Test that a block with too many checksigs is rejected
        too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB))
        block(16, spend=out[6], script=too_many_checksigs)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Attempt to spend a transaction created on a different fork
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        block(17, spend=txout_b3)
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # Attempt to spend a transaction created on a different fork (on a fork this time)
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5)
        #                                                                \-> b18 (b3.vtx[1]) -> b19 (6)
        #                      \-> b3 (1) -> b4 (2)
        tip(13)
        block(18, spend=txout_b3)
        yield rejected()

        block(19, spend=out[6])
        yield rejected()

        # Attempt to spend a coinbase at depth too low
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        block(20, spend=out[7])
        yield rejected(
            RejectResult(16, b'bad-txns-premature-spend-of-coinbase'))

        # Attempt to spend a coinbase at depth too low (on a fork this time)
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5)
        #                                                                \-> b21 (6) -> b22 (5)
        #                      \-> b3 (1) -> b4 (2)
        tip(13)
        block(21, spend=out[6])
        yield rejected()

        block(22, spend=out[5])
        yield rejected()

        # Create a block on either side of LEGACY_MAX_BLOCK_SIZE and make sure its accepted/rejected
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
        #                                                                           \-> b24 (6) -> b25 (7)
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        b23 = block(23, spend=out[6])
        tx = CTransaction()
        script_length = LEGACY_MAX_BLOCK_SIZE - len(b23.serialize()) - 69
        script_output = CScript([b'\x00' * script_length])
        tx.vout.append(CTxOut(0, script_output))
        tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0)))
        b23 = update_block(23, [tx])
        # Make sure the math above worked out to produce a max-sized block
        assert_equal(len(b23.serialize()), LEGACY_MAX_BLOCK_SIZE)
        yield accepted()
        save_spendable_output()

        # Create blocks with a coinbase input script size out of range
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
        #                                                                           \-> ... (6) -> ... (7)
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        b26 = block(26, spend=out[6])
        b26.vtx[0].vin[0].scriptSig = b'\x00'
        b26.vtx[0].rehash()
        # update_block causes the merkle root to get updated, even with no new
        # transactions, and updates the required state.
        b26 = update_block(26, [])
        yield rejected(RejectResult(16, b'bad-cb-length'))

        # Extend the b26 chain to make sure bitcoind isn't accepting b26
        b27 = block(27, spend=out[7])
        yield rejected(False)

        # Now try a too-large-coinbase script
        tip(15)
        b28 = block(28, spend=out[6])
        b28.vtx[0].vin[0].scriptSig = b'\x00' * 101
        b28.vtx[0].rehash()
        b28 = update_block(28, [])
        yield rejected(RejectResult(16, b'bad-cb-length'))

        # Extend the b28 chain to make sure bitcoind isn't accepting b28
        b29 = block(29, spend=out[7])
        yield rejected(False)

        # b30 has a max-sized coinbase scriptSig.
        tip(23)
        b30 = block(30)
        b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
        b30.vtx[0].rehash()
        b30 = update_block(30, [])
        yield accepted()
        save_spendable_output()

        # b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY
        #
        #     genesis -> ... -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10)
        #                                                                \-> b36 (11)
        #                                                    \-> b34 (10)
        #                                         \-> b32 (9)
        #

        # MULTISIG: each op code counts as 20 sigops.  To create the edge case,
        # pack another 19 sigops at the end.
        lots_of_multisigs = CScript([OP_CHECKMULTISIG] *
                                    ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) +
                                    [OP_CHECKSIG] * 19)
        b31 = block(31, spend=out[8], script=lots_of_multisigs)
        assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS_PER_MB)
        yield accepted()
        save_spendable_output()

        # this goes over the limit because the coinbase has one sigop
        too_many_multisigs = CScript([OP_CHECKMULTISIG] *
                                     (MAX_BLOCK_SIGOPS_PER_MB // 20))
        b32 = block(32, spend=out[9], script=too_many_multisigs)
        assert_equal(get_legacy_sigopcount_block(b32),
                     MAX_BLOCK_SIGOPS_PER_MB + 1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # CHECKMULTISIGVERIFY
        tip(31)
        lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] *
                                    ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) +
                                    [OP_CHECKSIG] * 19)
        block(33, spend=out[9], script=lots_of_multisigs)
        yield accepted()
        save_spendable_output()

        too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] *
                                     (MAX_BLOCK_SIGOPS_PER_MB // 20))
        block(34, spend=out[10], script=too_many_multisigs)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # CHECKSIGVERIFY
        tip(33)
        lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] *
                                    (MAX_BLOCK_SIGOPS_PER_MB - 1))
        b35 = block(35, spend=out[10], script=lots_of_checksigs)
        yield accepted()
        save_spendable_output()

        too_many_checksigs = CScript([OP_CHECKSIGVERIFY] *
                                     (MAX_BLOCK_SIGOPS_PER_MB))
        block(36, spend=out[11], script=too_many_checksigs)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Check spending of a transaction in a block which failed to connect
        #
        # b6  (3)
        # b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10)
        #                                                                                     \-> b37 (11)
        #                                                                                     \-> b38 (11/37)
        #

        # save 37's spendable output, but then double-spend out11 to invalidate
        # the block
        tip(35)
        b37 = block(37, spend=out[11])
        txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0)
        tx = create_and_sign_tx(out[11].tx, out[11].n, 0)
        b37 = update_block(37, [tx])
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # attempt to spend b37's first non-coinbase tx, at which point b37 was
        # still considered valid
        tip(35)
        block(38, spend=txout_b37)
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # Check P2SH SigOp counting
        #
        #
        #   13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12)
        #                                                                                        \-> b40 (12)
        #
        # b39 - create some P2SH outputs that will require 6 sigops to spend:
        #
        #           redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG
        #           p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL
        #
        tip(35)
        b39 = block(39)
        b39_outputs = 0
        b39_sigops_per_output = 6

        # Build the redeem script, hash it, use hash to create the p2sh script
        redeem_script = CScript([self.coinbase_pubkey] +
                                [OP_2DUP, OP_CHECKSIGVERIFY] * 5 +
                                [OP_CHECKSIG])
        redeem_script_hash = hash160(redeem_script)
        p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])

        # Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE
        # This must be signed because it is spending a coinbase
        spend = out[11]
        tx = create_tx(spend.tx, spend.n, 1, p2sh_script)
        tx.vout.append(
            CTxOut(spend.tx.vout[spend.n].nValue - 1, CScript([OP_TRUE])))
        sign_tx(tx, spend.tx, spend.n, self.coinbase_key)
        tx.rehash()
        b39 = update_block(39, [tx])
        b39_outputs += 1

        # Until block is full, add tx's with 1 satoshi to p2sh_script, the rest
        # to OP_TRUE
        tx_new = None
        tx_last = tx
        total_size = len(b39.serialize())
        while (total_size < LEGACY_MAX_BLOCK_SIZE):
            tx_new = create_tx(tx_last, 1, 1, p2sh_script)
            tx_new.vout.append(
                CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))
            tx_new.rehash()
            total_size += len(tx_new.serialize())
            if total_size >= LEGACY_MAX_BLOCK_SIZE:
                break
            b39.vtx.append(tx_new)  # add tx to block
            tx_last = tx_new
            b39_outputs += 1

        b39 = update_block(39, [])
        yield accepted()
        save_spendable_output()

        # Test sigops in P2SH redeem scripts
        #
        # b40 creates 3333 tx's spending the 6-sigop P2SH outputs from b39 for a total of 19998 sigops.
        # The first tx has one sigop and then at the end we add 2 more to put us just over the max.
        #
        # b41 does the same, less one, so it has the maximum sigops permitted.
        #
        tip(39)
        b40 = block(40, spend=out[12])
        sigops = get_legacy_sigopcount_block(b40)
        numTxes = (MAX_BLOCK_SIGOPS_PER_MB - sigops) // b39_sigops_per_output
        assert_equal(numTxes <= b39_outputs, True)

        lastOutpoint = COutPoint(b40.vtx[1].sha256, 0)
        lastAmount = b40.vtx[1].vout[0].nValue
        new_txs = []
        for i in range(1, numTxes + 1):
            tx = CTransaction()
            tx.vout.append(CTxOut(1, CScript([OP_TRUE])))
            tx.vin.append(CTxIn(lastOutpoint, b''))
            # second input is corresponding P2SH output from b39
            tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b''))
            # Note: must pass the redeem_script (not p2sh_script) to the
            # signature hash function
            sighash = SignatureHashForkId(redeem_script, tx, 1,
                                          SIGHASH_ALL | SIGHASH_FORKID,
                                          lastAmount)
            sig = self.coinbase_key.sign(sighash) + bytes(
                bytearray([SIGHASH_ALL | SIGHASH_FORKID]))
            scriptSig = CScript([sig, redeem_script])

            tx.vin[1].scriptSig = scriptSig
            tx.rehash()
            new_txs.append(tx)
            lastOutpoint = COutPoint(tx.sha256, 0)
            lastAmount = tx.vout[0].nValue

        b40_sigops_to_fill = MAX_BLOCK_SIGOPS_PER_MB - \
            (numTxes * b39_sigops_per_output + sigops) + 1
        tx = CTransaction()
        tx.vin.append(CTxIn(lastOutpoint, b''))
        tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill)))
        tx.rehash()
        new_txs.append(tx)
        update_block(40, new_txs)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # same as b40, but one less sigop
        tip(39)
        b41 = block(41, spend=None)
        update_block(41, b40.vtx[1:-1])
        b41_sigops_to_fill = b40_sigops_to_fill - 1
        tx = CTransaction()
        tx.vin.append(CTxIn(lastOutpoint, b''))
        tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill)))
        tx.rehash()
        update_block(41, [tx])
        yield accepted()

        # Fork off of b39 to create a constant base again
        #
        # b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13)
        #                                                                  \-> b41 (12)
        #
        tip(39)
        block(42, spend=out[12])
        yield rejected()
        save_spendable_output()

        block(43, spend=out[13])
        yield accepted()
        save_spendable_output()

        # Test a number of really invalid scenarios
        #
        #  -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b44 (14)
        #                                                                                   \-> ??? (15)

        # The next few blocks are going to be created "by hand" since they'll do funky things, such as having
        # the first transaction be non-coinbase, etc.  The purpose of b44 is to
        # make sure this works.
        height = self.chain.block_heights[self.chain.tip.sha256] + 1
        coinbase = create_coinbase(height, self.coinbase_pubkey)
        b44 = CBlock()
        b44.nTime = self.chain.tip.nTime + 1
        b44.hashPrevBlock = self.chain.tip.sha256
        b44.nBits = 0x207fffff
        b44.vtx.append(coinbase)
        b44.hashMerkleRoot = b44.calc_merkle_root()
        b44.solve()
        self.chain.tip = b44
        self.chain.block_heights[b44.sha256] = height
        self.chain.blocks[44] = b44
        yield accepted()

        # A block with a non-coinbase as the first tx
        non_coinbase = create_tx(out[15].tx, out[15].n, 1)
        b45 = CBlock()
        b45.nTime = self.chain.tip.nTime + 1
        b45.hashPrevBlock = self.chain.tip.sha256
        b45.nBits = 0x207fffff
        b45.vtx.append(non_coinbase)
        b45.hashMerkleRoot = b45.calc_merkle_root()
        b45.calc_sha256()
        b45.solve()
        self.chain.block_heights[
            b45.sha256] = self.chain.block_heights[self.chain.tip.sha256] + 1
        self.chain.tip = b45
        self.chain.blocks[45] = b45
        yield rejected(RejectResult(16, b'bad-cb-missing'))

        # A block with no txns
        tip(44)
        b46 = CBlock()
        b46.nTime = b44.nTime + 1
        b46.hashPrevBlock = b44.sha256
        b46.nBits = 0x207fffff
        b46.vtx = []
        b46.hashMerkleRoot = 0
        b46.solve()
        self.chain.block_heights[
            b46.sha256] = self.chain.block_heights[b44.sha256] + 1
        self.chain.tip = b46
        assert 46 not in self.chain.blocks
        self.chain.blocks[46] = b46
        s = ser_uint256(b46.hashMerkleRoot)
        yield rejected(RejectResult(16, b'bad-cb-missing'))

        # A block with invalid work
        tip(44)
        b47 = block(47, do_solve_block=False)
        target = uint256_from_compact(b47.nBits)
        while b47.sha256 < target:  # changed > to <
            b47.nNonce += 1
            b47.rehash()
        yield rejected(RejectResult(16, b'high-hash'))

        # A block with timestamp > 2 hrs in the future
        tip(44)
        b48 = block(48, do_solve_block=False)
        b48.nTime = int(time.time()) + 60 * 60 * 3
        b48.solve()
        yield rejected(RejectResult(16, b'time-too-new'))

        # A block with an invalid merkle hash
        tip(44)
        b49 = block(49)
        b49.hashMerkleRoot += 1
        b49.solve()
        yield rejected(RejectResult(16, b'bad-txnmrklroot'))

        # A block with an incorrect POW limit
        tip(44)
        b50 = block(50)
        b50.nBits = b50.nBits - 1
        b50.solve()
        yield rejected(RejectResult(16, b'bad-diffbits'))

        # A block with two coinbase txns
        tip(44)
        b51 = block(51)
        cb2 = create_coinbase(51, self.coinbase_pubkey)
        b51 = update_block(51, [cb2])
        yield rejected(RejectResult(16, b'bad-tx-coinbase'))

        # A block w/ duplicate txns
        # Note: txns have to be in the right position in the merkle tree to
        # trigger this error
        tip(44)
        b52 = block(52, spend=out[15])
        tx = create_tx(b52.vtx[1], 0, 1)
        b52 = update_block(52, [tx, tx])
        yield rejected(RejectResult(16, b'bad-txns-duplicate'))

        # Test block timestamps
        #  -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
        #                                                                                   \-> b54 (15)
        #
        tip(43)
        block(53, spend=out[14])
        yield rejected()  # rejected since b44 is at same height
        save_spendable_output()

        # invalid timestamp (b35 is 5 blocks back, so its time is
        # MedianTimePast)
        b54 = block(54, spend=out[15])
        b54.nTime = b35.nTime - 1
        b54.solve()
        yield rejected(RejectResult(16, b'time-too-old'))

        # valid timestamp
        tip(53)
        b55 = block(55, spend=out[15])
        b55.nTime = b35.nTime
        update_block(55, [])
        yield accepted()
        save_spendable_output()

        # Test CVE-2012-2459
        #
        # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16)
        #                                                \-> b57   (16)
        #                                                \-> b56p2 (16)
        #                                                \-> b56   (16)
        #
        # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without
        #                           affecting the merkle root of a block, while still invalidating it.
        #                           See:  src/consensus/merkle.h
        #
        #  b57 has three txns:  coinbase, tx, tx1.  The merkle root computation will duplicate tx.
        #  Result:  OK
        #
        #  b56 copies b57 but duplicates tx1 and does not recalculate the block hash.  So it has a valid merkle
        #  root but duplicate transactions.
        #  Result:  Fails
        #
        #  b57p2 has six transactions in its merkle tree:
        #       - coinbase, tx, tx1, tx2, tx3, tx4
        #  Merkle root calculation will duplicate as necessary.
        #  Result:  OK.
        #
        #  b56p2 copies b57p2 but adds both tx3 and tx4.  The purpose of the test is to make sure the code catches
        #  duplicate txns that are not next to one another with the "bad-txns-duplicate" error (which indicates
        #  that the error was caught early, avoiding a DOS vulnerability.)

        # b57 - a good block with 2 txs, don't submit until end
        tip(55)
        b57 = block(57)
        tx = create_and_sign_tx(out[16].tx, out[16].n, 1)
        tx1 = create_tx(tx, 0, 1)
        b57 = update_block(57, [tx, tx1])

        # b56 - copy b57, add a duplicate tx
        tip(55)
        b56 = copy.deepcopy(b57)
        self.chain.blocks[56] = b56
        assert_equal(len(b56.vtx), 3)
        b56 = update_block(56, [tx1])
        assert_equal(b56.hash, b57.hash)
        yield rejected(RejectResult(16, b'bad-txns-duplicate'))

        # b57p2 - a good block with 6 tx'es, don't submit until end
        tip(55)
        b57p2 = block("57p2")
        tx = create_and_sign_tx(out[16].tx, out[16].n, 1)
        tx1 = create_tx(tx, 0, 1)
        tx2 = create_tx(tx1, 0, 1)
        tx3 = create_tx(tx2, 0, 1)
        tx4 = create_tx(tx3, 0, 1)
        b57p2 = update_block("57p2", [tx, tx1, tx2, tx3, tx4])

        # b56p2 - copy b57p2, duplicate two non-consecutive tx's
        tip(55)
        b56p2 = copy.deepcopy(b57p2)
        self.chain.blocks["b56p2"] = b56p2
        assert_equal(b56p2.hash, b57p2.hash)
        assert_equal(len(b56p2.vtx), 6)
        b56p2 = update_block("b56p2", [tx3, tx4])
        yield rejected(RejectResult(16, b'bad-txns-duplicate'))

        tip("57p2")
        yield accepted()

        tip(57)
        yield rejected()  # rejected because 57p2 seen first
        save_spendable_output()

        # Test a few invalid tx types
        #
        # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
        #                                                                                    \-> ??? (17)
        #

        # tx with prevout.n out of range
        tip(57)
        b58 = block(58, spend=out[17])
        tx = CTransaction()
        assert (len(out[17].tx.vout) < 42)
        tx.vin.append(
            CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]),
                  0xffffffff))
        tx.vout.append(CTxOut(0, b""))
        tx.calc_sha256()
        b58 = update_block(58, [tx])
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # tx with output value > input value out of range
        tip(57)
        b59 = block(59)
        tx = create_and_sign_tx(out[17].tx, out[17].n, 51 * COIN)
        b59 = update_block(59, [tx])
        yield rejected(RejectResult(16, b'bad-txns-in-belowout'))

        # reset to good chain
        tip(57)
        b60 = block(60, spend=out[17])
        yield accepted()
        save_spendable_output()

        # Test BIP30
        #
        # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
        #                                                                                    \-> b61 (18)
        #
        # Blocks are not allowed to contain a transaction whose id matches that of an earlier,
        # not-fully-spent transaction in the same chain. To test, make identical coinbases;
        # the second one should be rejected.
        #
        tip(60)
        b61 = block(61, spend=out[18])
        b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[
            0].scriptSig  # equalize the coinbases
        b61.vtx[0].rehash()
        b61 = update_block(61, [])
        assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
        yield rejected(RejectResult(16, b'bad-txns-BIP30'))

        # Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
        #
        #   -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
        #                                                                                     \-> b62 (18)
        #
        tip(60)
        b62 = block(62)
        tx = CTransaction()
        tx.nLockTime = 0xffffffff  # this locktime is non-final
        assert (out[18].n < len(out[18].tx.vout))
        tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256,
                                      out[18].n)))  # don't set nSequence
        tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
        assert (tx.vin[0].nSequence < 0xffffffff)
        tx.calc_sha256()
        b62 = update_block(62, [tx])
        yield rejected(RejectResult(16, b'bad-txns-nonfinal'))

        # Test a non-final coinbase is also rejected
        #
        #   -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
        #                                                                                     \-> b63 (-)
        #
        tip(60)
        b63 = block(63)
        b63.vtx[0].nLockTime = 0xffffffff
        b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
        b63.vtx[0].rehash()
        b63 = update_block(63, [])
        yield rejected(RejectResult(16, b'bad-txns-nonfinal'))

        #  This checks that a block with a bloated VARINT between the block_header and the array of tx such that
        #  the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_MAX_BLOCK_SIZE without the bloated varint,
        #  does not cause a subsequent, identical block with canonical encoding to be rejected.  The test does not
        #  care whether the bloated block is accepted or rejected; it only cares that the second block is accepted.
        #
        #  What matters is that the receiving node should not reject the bloated block, and then reject the canonical
        #  block on the basis that it's the same as an already-rejected block (which would be a consensus failure.)
        #
        #  -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18)
        #                                                                                        \
        #                                                                                         b64a (18)
        #  b64a is a bloated block (non-canonical varint)
        #  b64 is a good block (same as b64 but w/ canonical varint)
        #
        tip(60)
        regular_block = block("64a", spend=out[18])

        # make it a "broken_block," with non-canonical serialization
        b64a = CBrokenBlock(regular_block)
        b64a.initialize(regular_block)
        self.chain.blocks["64a"] = b64a
        self.chain.tip = b64a
        tx = CTransaction()

        # use canonical serialization to calculate size
        script_length = LEGACY_MAX_BLOCK_SIZE - \
            len(b64a.normal_serialize()) - 69
        script_output = CScript([b'\x00' * script_length])
        tx.vout.append(CTxOut(0, script_output))
        tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
        b64a = update_block("64a", [tx])
        assert_equal(len(b64a.serialize()), LEGACY_MAX_BLOCK_SIZE + 8)
        yield TestInstance([[self.chain.tip, None]])

        # comptool workaround: to make sure b64 is delivered, manually erase
        # b64a from blockstore
        self.test.block_store.erase(b64a.sha256)

        tip(60)
        b64 = CBlock(b64a)
        b64.vtx = copy.deepcopy(b64a.vtx)
        assert_equal(b64.hash, b64a.hash)
        assert_equal(len(b64.serialize()), LEGACY_MAX_BLOCK_SIZE)
        self.chain.blocks[64] = b64
        update_block(64, [])
        yield accepted()
        save_spendable_output()

        # Spend an output created in the block itself
        #
        # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
        #
        tip(64)
        b65 = block(65)
        tx1 = create_and_sign_tx(out[19].tx, out[19].n,
                                 out[19].tx.vout[0].nValue)
        tx2 = create_and_sign_tx(tx1, 0, 0)
        update_block(65, [tx1, tx2])
        yield accepted()
        save_spendable_output()

        # Attempt to spend an output created later in the same block
        #
        # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
        #                                                                                    \-> b66 (20)
        tip(65)
        b66 = block(66)
        tx1 = create_and_sign_tx(out[20].tx, out[20].n,
                                 out[20].tx.vout[0].nValue)
        tx2 = create_and_sign_tx(tx1, 0, 1)
        update_block(66, [tx2, tx1])
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # Attempt to double-spend a transaction created in a block
        #
        # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
        #                                                                                    \-> b67 (20)
        #
        #
        tip(65)
        b67 = block(67)
        tx1 = create_and_sign_tx(out[20].tx, out[20].n,
                                 out[20].tx.vout[0].nValue)
        tx2 = create_and_sign_tx(tx1, 0, 1)
        tx3 = create_and_sign_tx(tx1, 0, 2)
        update_block(67, [tx1, tx2, tx3])
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # More tests of block subsidy
        #
        # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
        #                                                                                    \-> b68 (20)
        #
        # b68 - coinbase with an extra 10 satoshis,
        #       creates a tx that has 9 satoshis from out[20] go to fees
        #       this fails because the coinbase is trying to claim 1 satoshi too much in fees
        #
        # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee
        #       this succeeds
        #
        tip(65)
        b68 = block(68, additional_coinbase_value=10)
        tx = create_and_sign_tx(out[20].tx, out[20].n,
                                out[20].tx.vout[0].nValue - 9)
        update_block(68, [tx])
        yield rejected(RejectResult(16, b'bad-cb-amount'))

        tip(65)
        b69 = block(69, additional_coinbase_value=10)
        tx = create_and_sign_tx(out[20].tx, out[20].n,
                                out[20].tx.vout[0].nValue - 10)
        update_block(69, [tx])
        yield accepted()
        save_spendable_output()

        # Test spending the outpoint of a non-existent transaction
        #
        # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
        #                                                                                    \-> b70 (21)
        #
        tip(69)
        block(70, spend=out[21])
        bogus_tx = CTransaction()
        bogus_tx.sha256 = uint256_from_str(
            b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c"
        )
        tx = CTransaction()
        tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff))
        tx.vout.append(CTxOut(1, b""))
        update_block(70, [tx])
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
        #
        #  -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
        #                                                                                      \-> b71 (21)
        #
        # b72 is a good block.
        # b71 is a copy of 72, but re-adds one of its transactions.  However, it has the same hash as b71.
        #
        tip(69)
        b72 = block(72)
        tx1 = create_and_sign_tx(out[21].tx, out[21].n, 2)
        tx2 = create_and_sign_tx(tx1, 0, 1)
        b72 = update_block(72, [tx1, tx2])  # now tip is 72
        b71 = copy.deepcopy(b72)
        b71.vtx.append(tx2)  # add duplicate tx2
        self.chain.block_heights[b71.sha256] = self.chain.block_heights[
            b69.sha256] + 1  # b71 builds off b69
        self.chain.blocks[71] = b71

        assert_equal(len(b71.vtx), 4)
        assert_equal(len(b72.vtx), 3)
        assert_equal(b72.sha256, b71.sha256)

        tip(71)
        yield rejected(RejectResult(16, b'bad-txns-duplicate'))
        tip(72)
        yield accepted()
        save_spendable_output()

        # Test some invalid scripts and MAX_BLOCK_SIGOPS_PER_MB
        #
        # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21)
        #                                                                                    \-> b** (22)
        #

        # b73 - tx with excessive sigops that are placed after an excessively large script element.
        #       The purpose of the test is to make sure those sigops are counted.
        #
        #       script is a bytearray of size 20,526
        #
        #       bytearray[0-19,998]     : OP_CHECKSIG
        #       bytearray[19,999]       : OP_PUSHDATA4
        #       bytearray[20,000-20,003]: 521  (max_script_element_size_before_genesis+1, in little-endian format)
        #       bytearray[20,004-20,525]: unread data (script_element)
        #       bytearray[20,526]       : OP_CHECKSIG (this puts us over the limit)
        #
        tip(72)
        b73 = block(73)
        size = MAX_BLOCK_SIGOPS_PER_MB - 1 + \
            MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 1 + 5 + 1
        a = bytearray([OP_CHECKSIG] * size)
        a[MAX_BLOCK_SIGOPS_PER_MB - 1] = int("4e", 16)  # OP_PUSHDATA4

        element_size = MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 1
        a[MAX_BLOCK_SIGOPS_PER_MB] = element_size % 256
        a[MAX_BLOCK_SIGOPS_PER_MB + 1] = element_size // 256
        a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0
        a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0

        tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
        b73 = update_block(73, [tx])
        assert_equal(get_legacy_sigopcount_block(b73),
                     MAX_BLOCK_SIGOPS_PER_MB + 1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # b74/75 - if we push an invalid script element, all prevous sigops are counted,
        #          but sigops after the element are not counted.
        #
        #       The invalid script element is that the push_data indicates that
        #       there will be a large amount of data (0xffffff bytes), but we only
        #       provide a much smaller number.  These bytes are CHECKSIGS so they would
        #       cause b75 to fail for excessive sigops, if those bytes were counted.
        #
        #       b74 fails because we put MAX_BLOCK_SIGOPS_PER_MB+1 before the element
        #       b75 succeeds because we put MAX_BLOCK_SIGOPS_PER_MB before the element
        #
        #
        tip(72)
        b74 = block(74)
        size = MAX_BLOCK_SIGOPS_PER_MB - 1 + \
            MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 42  # total = 20,561
        a = bytearray([OP_CHECKSIG] * size)
        a[MAX_BLOCK_SIGOPS_PER_MB] = 0x4e
        a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xfe
        a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff
        a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff
        a[MAX_BLOCK_SIGOPS_PER_MB + 4] = 0xff
        tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
        b74 = update_block(74, [tx])
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        tip(72)
        b75 = block(75)
        size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 42
        a = bytearray([OP_CHECKSIG] * size)
        a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e
        a[MAX_BLOCK_SIGOPS_PER_MB] = 0xff
        a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xff
        a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff
        a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff
        tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
        b75 = update_block(75, [tx])
        yield accepted()
        save_spendable_output()

        # Check that if we push an element filled with CHECKSIGs, they are not
        # counted
        tip(75)
        b76 = block(76)
        size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 1 + 5
        a = bytearray([OP_CHECKSIG] * size)
        a[MAX_BLOCK_SIGOPS_PER_MB -
          1] = 0x4e  # PUSHDATA4, but leave the following bytes as just checksigs
        tx = create_and_sign_tx(out[23].tx, 0, 1, CScript(a))
        b76 = update_block(76, [tx])
        yield accepted()
        save_spendable_output()

        # Test transaction resurrection
        #
        # -> b77 (24) -> b78 (25) -> b79 (26)
        #            \-> b80 (25) -> b81 (26) -> b82 (27)
        #
        #    b78 creates a tx, which is spent in b79. After b82, both should be in mempool
        #
        #    The tx'es must be unsigned and pass the node's mempool policy.  It is unsigned for the
        #    rather obscure reason that the Python signature code does not distinguish between
        #    Low-S and High-S values (whereas the bitcoin code has custom code which does so);
        #    as a result of which, the odds are 50% that the python code will use the right
        #    value and the transaction will be accepted into the mempool. Until we modify the
        #    test framework to support low-S signing, we are out of luck.
        #
        #    To get around this issue, we construct transactions which are not signed and which
        #    spend to OP_TRUE.  If the standard-ness rules change, this test would need to be
        #    updated.  (Perhaps to spend to a P2SH OP_TRUE script)
        #
        tip(76)
        block(77)
        tx77 = create_and_sign_tx(out[24].tx, out[24].n, 10 * COIN)
        update_block(77, [tx77])
        yield accepted()
        save_spendable_output()

        block(78)
        tx78 = create_tx(tx77, 0, 9 * COIN)
        update_block(78, [tx78])
        yield accepted()

        block(79)
        tx79 = create_tx(tx78, 0, 8 * COIN)
        update_block(79, [tx79])
        yield accepted()

        # mempool should be empty
        assert_equal(len(self.nodes[0].getrawmempool()), 0)

        tip(77)
        block(80, spend=out[25])
        yield rejected()
        save_spendable_output()

        block(81, spend=out[26])
        yield rejected()  # other chain is same length
        save_spendable_output()

        block(82, spend=out[27])
        yield accepted()  # now this chain is longer, triggers re-org
        save_spendable_output()

        # now check that tx78 and tx79 have been put back into the peer's
        # mempool
        mempool = self.nodes[0].getrawmempool()
        assert_equal(len(mempool), 2)
        assert (tx78.hash in mempool)
        assert (tx79.hash in mempool)

        # Test invalid opcodes in dead execution paths.
        #
        #  -> b81 (26) -> b82 (27) -> b83 (28)
        #
        b83 = block(83)
        op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF]
        script = CScript(op_codes)
        tx1 = create_and_sign_tx(out[28].tx, out[28].n,
                                 out[28].tx.vout[0].nValue, script)

        tx2 = create_and_sign_tx(tx1, 0, 0, CScript([OP_TRUE]))
        tx2.vin[0].scriptSig = CScript([OP_FALSE])
        tx2.rehash()

        update_block(83, [tx1, tx2])
        yield accepted()
        save_spendable_output()

        # Reorg on/off blocks that have OP_RETURN in them (and try to spend them)
        #
        #  -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31)
        #                                    \-> b85 (29) -> b86 (30)            \-> b89a (32)
        #
        #
        b84 = block(84)
        tx1 = create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN]))
        tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
        tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
        tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
        tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
        tx1.calc_sha256()
        sign_tx(tx1, out[29].tx, out[29].n, self.coinbase_key)
        tx1.rehash()
        tx2 = create_tx(tx1, 1, 0, CScript([OP_RETURN]))
        tx2.vout.append(CTxOut(0, CScript([OP_RETURN])))
        tx3 = create_tx(tx1, 2, 0, CScript([OP_RETURN]))
        tx3.vout.append(CTxOut(0, CScript([OP_TRUE])))
        tx4 = create_tx(tx1, 3, 0, CScript([OP_TRUE]))
        tx4.vout.append(CTxOut(0, CScript([OP_RETURN])))
        tx5 = create_tx(tx1, 4, 0, CScript([OP_RETURN]))

        update_block(84, [tx1, tx2, tx3, tx4, tx5])
        yield accepted()
        save_spendable_output()

        tip(83)
        block(85, spend=out[29])
        yield rejected()

        block(86, spend=out[30])
        yield accepted()

        tip(84)
        block(87, spend=out[30])
        yield rejected()
        save_spendable_output()

        block(88, spend=out[31])
        yield accepted()
        save_spendable_output()

        # trying to spend the OP_RETURN output is rejected
        block("89a", spend=out[32])
        tx = create_tx(tx1, 0, 0, CScript([OP_TRUE]))
        update_block("89a", [tx])
        yield rejected()

        #  Test re-org of a week's worth of blocks (1088 blocks)
        #  This test takes a minute or two and can be accomplished in memory
        #
        if self.options.runbarelyexpensive:
            tip(88)
            LARGE_REORG_SIZE = 1088
            test1 = TestInstance(sync_every_block=False,
                                 sync_timeout=300,
                                 timeout_to_requested_block=600)
            spend = out[32]
            for i in range(89, LARGE_REORG_SIZE + 89):
                b = block(i, spend)
                tx = CTransaction()
                script_length = LEGACY_MAX_BLOCK_SIZE - len(b.serialize()) - 69
                script_output = CScript([b'\x00' * script_length])
                tx.vout.append(CTxOut(0, script_output))
                tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
                b = update_block(i, [tx])
                assert_equal(len(b.serialize()), LEGACY_MAX_BLOCK_SIZE)
                test1.blocks_and_transactions.append([self.chain.tip, True])
                save_spendable_output()
                spend = self.chain.get_spendable_output()

            yield test1
            chain1_tip = i

            # now create alt chain of same length
            tip(88)
            test2 = TestInstance(sync_every_block=False)
            for i in range(89, LARGE_REORG_SIZE + 89):
                block("alt" + str(i))
                test2.blocks_and_transactions.append([self.chain.tip, False])
            yield test2

            # extend alt chain to trigger re-org
            block("alt" + str(chain1_tip + 1))
            yield accepted()

            # ... and re-org back to the first chain
            tip(chain1_tip)
            block(chain1_tip + 1)
            yield rejected()
            block(chain1_tip + 2)
            yield accepted()

            chain1_tip += 2
Пример #12
0
    def get_tests(self):
        node = self.nodes[0]
        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0

        # returns a test case that asserts that the current tip was accepted
        def accepted(expectedTipHash=None):
            if expectedTipHash is None:
                return TestInstance([[self.tip, True]])
            else:
                return TestInstance([[self.tip, True, expectedTipHash]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # shorthand for functions
        block = self.next_block

        # Reference for blocks mined in this test:
        #
        #       11  21   -- 221 - 222
        #      /   /    /
        # 0 - 1 - 2  - 22 - 23 - 24 - 25
        #     \
        #      -- 12 - 13 - 14

        # Generate some valid blocks
        block(0)
        yield accepted()
        block(1)
        yield accepted()
        block(2)
        yield accepted()

        # Explicitly invalidate blocks 1 and 2
        # See below for why we do this
        node.invalidateblock(self.blocks[1].hash)
        assert_equal(self.blocks[0].hash, node.getbestblockhash())
        node.invalidateblock(self.blocks[2].hash)
        assert_equal(self.blocks[0].hash, node.getbestblockhash())

        # Mining on top of blocks 1 or 2 is rejected
        tip(1)
        block(11)
        yield rejected(RejectResult(16, b'bad-prevblk'))

        tip(2)
        block(21)
        yield rejected(RejectResult(16, b'bad-prevblk'))

        # Reconsider block 2 to remove invalid status from *both* 1 and 2
        # The goal is to test that block 1 is not retaining any internal state
        # that prevents us from accepting blocks building on top of block 1
        node.reconsiderblock(self.blocks[2].hash)
        assert_equal(self.blocks[2].hash, node.getbestblockhash())

        # Mining on the block 1 chain should be accepted
        # (needs to mine two blocks because less-work chains are not processed)
        test = TestInstance(sync_every_block=False)
        tip(1)
        block(12)
        test.blocks_and_transactions.append([self.tip, None])
        block(13)
        test.blocks_and_transactions.append([self.tip, None])
        yield test

        # Mining on the block 2 chain should still be accepted
        # (needs to mine two blocks because less-work chains are not processed)
        test = TestInstance(sync_every_block=False)
        tip(2)
        block(22)
        test.blocks_and_transactions.append([self.tip, None])
        # Mine block 221 for later
        block(221)
        test.blocks_and_transactions.append([self.tip, None])
        yield test

        # Mine more blocks from block 22 to be longest chain
        test = TestInstance(sync_every_block=False)
        tip(22)
        block(23)
        test.blocks_and_transactions.append([self.tip, None])
        block(24)
        test.blocks_and_transactions.append([self.tip, None])
        yield test

        # Sanity checks
        assert_equal(self.blocks[24].hash, node.getbestblockhash())
        assert any(self.blocks[221].hash == chaintip["hash"]
                   for chaintip in node.getchaintips())

        # Invalidating the block 2 chain should reject new blocks on that chain
        node.invalidateblock(self.blocks[2].hash)
        assert_equal(self.blocks[13].hash, node.getbestblockhash())

        # Mining on the block 2 chain should be rejected
        tip(24)
        block(25)
        yield rejected(RejectResult(16, b'bad-prevblk'))

        # Continued mining on the block 1 chain is still ok
        tip(13)
        block(14)
        yield accepted()

        # Mining on a once-valid chain forking from block 2's longest chain,
        # which is now invalid, should also be rejected.
        tip(221)
        block(222)
        yield rejected(RejectResult(16, b'bad-prevblk'))
Пример #13
0
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand for functions
        block = self.next_block
        node = self.nodes[0]

        # Create a new block
        block(0, block_size=LEGACY_MAX_BLOCK_SIZE)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # block up to LEGACY_MAX_BLOCK_SIZE are accepted.
        block(1, spend=out[0], block_size=LEGACY_MAX_BLOCK_SIZE)
        yield accepted()

        # bigger block are reject as the fork isn't activated yet.
        block(2, spend=out[1], block_size=LEGACY_MAX_BLOCK_SIZE + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block
        tip(1)

        # Create a transaction that we will use to test SIGHASH_FORID
        script_forkid = CScript([self.forkid_pubkey, OP_CHECKSIG])
        tx_forkid = self.create_and_sign_transaction(out[1].tx, out[1].n, 1,
                                                     script_forkid)

        # Create a block that would activate the HF. We also add the
        # transaction that will allow us to test SIGHASH_FORKID
        b03 = block(3)
        b03.nTime = UAHF_START_TIME
        update_block(3, [tx_forkid])
        yield accepted()

        # Pile up 4 blocks on top to get to the point just before activation.
        block(4, spend=out[2])
        yield accepted()
        block(5, spend=out[3])
        yield accepted()
        block(6, spend=out[4])
        yield accepted()
        block(7, spend=out[5])
        yield accepted()

        # bigger block are still rejected as the fork isn't activated yet.
        block(8, spend=out[6], block_size=LEGACY_MAX_BLOCK_SIZE + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block
        tip(7)

        # build a transaction using SIGHASH_FORKID
        tx_spend = self.create_tx(tx_forkid, 0, 1, CScript([OP_TRUE]))
        sighash_spend = SignatureHashForkId(script_forkid, tx_spend, 0,
                                            SIGHASH_FORKID | SIGHASH_ALL, 1)
        sig_forkid = self.forkid_key.sign(sighash_spend)
        tx_spend.vin[0].scriptSig = CScript(
            [sig_forkid + bytes(bytearray([SIGHASH_FORKID | SIGHASH_ALL]))])
        tx_spend.rehash()

        # This transaction can't get into the mempool yet
        try:
            node.sendrawtransaction(ToHex(tx_spend))
        except JSONRPCException as exp:
            assert_equal(exp.error["message"], RPC_SIGHASH_FORKID_ERROR)
        else:
            assert (False)

        # The transaction is rejected, so the mempool should still be empty
        assert_equal(set(node.getrawmempool()), set())

        # check that SIGHASH_FORKID transaction are still rejected
        block(9)
        update_block(9, [tx_spend])
        yield rejected(RejectResult(16, SIGHASH_INVALID_ERROR))

        # Rewind bad block
        tip(7)

        # Pile up another block, to activate. OP_RETURN anti replay
        # outputs are still considered valid.
        antireplay_script = CScript([OP_RETURN, ANTI_REPLAY_COMMITMENT])
        block(10, spend=out[6], script=antireplay_script)
        yield accepted()

        # Now that the HF is activated, replay protected tx are
        # accepted in the mempool
        tx_spend_id = node.sendrawtransaction(ToHex(tx_spend))
        assert_equal(set(node.getrawmempool()), {tx_spend_id})

        # Mark the HF
        self.uahfEnabled = True

        # HF is active now, we MUST create a big block.
        block(11, spend=out[7], block_size=LEGACY_MAX_BLOCK_SIZE)
        yield rejected(RejectResult(16, b'bad-blk-too-small'))

        # Rewind bad block
        tip(10)

        # HF is active, now we can create bigger blocks and use
        # SIGHASH_FORKID replay protection.
        block(12, spend=out[7], block_size=LEGACY_MAX_BLOCK_SIZE + 1)
        update_block(12, [tx_spend])
        yield accepted()

        # We save this block id to test reorg
        fork_block_id = node.getbestblockhash()

        # The transaction has been mined, it's not in the mempool anymore
        assert_equal(set(node.getrawmempool()), set())

        # Test OP_RETURN replay protection
        block(13, spend=out[8], script=antireplay_script)
        yield rejected(RejectResult(16, b'bad-txn-replay'))

        # Rewind bad block
        tip(12)

        # Check that only the first block has to be > 1MB
        block(14, spend=out[8])
        yield accepted()

        # Now we reorg just when the HF activated. The
        # SIGHASH_FORKID transaction is back in the mempool
        node.invalidateblock(fork_block_id)
        assert (tx_spend_id in set(node.getrawmempool()))

        # And now just before when the HF activated. The
        # SIGHASH_FORKID should be kicked out the mempool
        node.invalidateblock(node.getbestblockhash())
        assert (tx_spend_id not in set(node.getrawmempool()))
Пример #14
0
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # shorthand
        block = self.next_block
        node_nonstd = self.nodes[0]
        node_std = self.nodes[1]

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            block.vtx.extend(new_transactions)
            old_sha256 = block.sha256
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # Returns 2 transactions:
        # 1) txfund: create outputs in segwit addresses
        # 2) txspend: spends outputs from segwit addresses
        def create_segwit_fund_and_spend_tx(spend):
            # To make sure we'll be able to recover coins sent to segwit addresses,
            # we test using historical recoveries from btc.com:
            # Spending from a P2SH-P2WPKH coin,
            #   txhash:a45698363249312f8d3d93676aa714be59b0bd758e62fa054fb1ea6218480691
            redeem_script0 = bytearray.fromhex(
                '0014fcf9969ce1c98a135ed293719721fb69f0b686cb')
            # Spending from a P2SH-P2WSH coin,
            #   txhash:6b536caf727ccd02c395a1d00b752098ec96e8ec46c96bee8582be6b5060fa2f
            redeem_script1 = bytearray.fromhex(
                '0020fc8b08ed636cb23afcb425ff260b3abd03380a2333b54cfa5d51ac52d803baf4'
            )
            redeem_scripts = [redeem_script0, redeem_script1]

            # Fund transaction to segwit addresses
            txfund = CTransaction()
            txfund.vin = [CTxIn(COutPoint(spend.tx.sha256, spend.n))]
            amount = (50 * COIN - 1000) // len(redeem_scripts)
            for redeem_script in redeem_scripts:
                txfund.vout.append(
                    CTxOut(
                        amount,
                        CScript([OP_HASH160,
                                 hash160(redeem_script), OP_EQUAL])))
            txfund.rehash()

            # Segwit spending transaction
            # We'll test if a node that checks for standardness accepts this
            # txn. It should fail exclusively because of the restriction in
            # the scriptSig (non clean stack..), so all other characteristcs
            # must pass standardness checks. For this reason, we create
            # standard P2SH outputs.
            txspend = CTransaction()
            for i in range(len(redeem_scripts)):
                txspend.vin.append(
                    CTxIn(COutPoint(txfund.sha256, i),
                          CScript([redeem_scripts[i]])))
            txspend.vout = [
                CTxOut(
                    50 * COIN - 2000,
                    CScript(
                        [OP_HASH160,
                         hash160(CScript([OP_TRUE])), OP_EQUAL]))
            ]
            txspend.rehash()

            return txfund, txspend

        # Check we are not banned when sending a txn that node_nonstd rejects.
        def check_for_no_ban_on_rejected_tx(tx, reject_code, reject_reason):
            # Check that our connection to node_std is open
            assert (node_std.p2p.state == 'connected')

            # The P2PConnection stores a public counter for each message type
            # and the last receive message of each type. We use this counter to
            # identify that we received a new reject message.
            with mininode_lock:
                rejects_count = node_std.p2p.message_count['reject']

            # Send the transaction directly. We use a ping for synchronization:
            # if we have been banned, the pong message won't be received, a
            # timeout occurs and the test fails.
            node_std.p2p.send_message(msg_tx(tx))
            node_std.p2p.sync_with_ping()

            # Check we haven't been disconnected
            assert (node_std.p2p.state == 'connected')

            # Check the reject message matches what we expected
            with mininode_lock:
                assert (node_std.p2p.message_count['reject'] == rejects_count +
                        1)
                reject_msg = node_std.p2p.last_message['reject']
                assert (reject_msg.code == reject_code
                        and reject_msg.reason == reject_reason
                        and reject_msg.data == tx.sha256)

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Create segwit funding and spending transactions
        txfund, txspend = create_segwit_fund_and_spend_tx(out[0])

        # Create blocks to get closer to activate the fork.
        # Mine txfund, as it can't go into node_std mempool because it's
        # nonstandard.
        b = block(5555)
        b.nTime = GREAT_WALL_START_TIME - 1
        update_block(5555, [txfund])
        yield accepted()

        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])
        yield test

        # Since the TestManager is not connected to node_std, we must check
        # both nodes are synchronized before continuing.
        sync_blocks(self.nodes)

        # Check we are just before the activation time
        assert_equal(
            node_nonstd.getblockheader(
                node_nonstd.getbestblockhash())['mediantime'],
            GREAT_WALL_START_TIME - 1)
        assert_equal(
            node_std.getblockheader(node_std.getbestblockhash())['mediantime'],
            GREAT_WALL_START_TIME - 1)

        # Before the fork, segwit spending txns are rejected.
        assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR,
                                node_nonstd.sendrawtransaction, ToHex(txspend))
        assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR,
                                node_std.sendrawtransaction, ToHex(txspend))

        # Blocks containing segwit spending txns are rejected as well.
        block(2)
        update_block(2, [txspend])
        yield rejected(RejectResult(16, b'blk-bad-inputs'))

        # Rewind bad block
        tip(5104)

        # Check that non-upgraded nodes checking for standardness are not
        # banning nodes sending segwit spending txns.
        check_for_no_ban_on_rejected_tx(txspend, 64, CLEANSTACK_ERROR)

        # Activate the fork in both nodes!
        forkblock = block(5556)
        yield accepted()
        sync_blocks(self.nodes)

        # Check we just activated the fork
        assert_equal(
            node_nonstd.getblockheader(
                node_nonstd.getbestblockhash())['mediantime'],
            GREAT_WALL_START_TIME)
        assert_equal(
            node_std.getblockheader(node_std.getbestblockhash())['mediantime'],
            GREAT_WALL_START_TIME)

        # Segwit spending txns are accepted in the mempool of nodes not checking
        # for standardness, but rejected in nodes that check.
        node_nonstd.sendrawtransaction(ToHex(txspend))
        assert (txspend.hash in node_nonstd.getrawmempool())
        assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR,
                                node_std.sendrawtransaction, ToHex(txspend))

        # Check that upgraded nodes checking for standardness are not banning
        # nodes sending segwit spending txns.
        check_for_no_ban_on_rejected_tx(txspend, 64, CLEANSTACK_ERROR)

        # Blocks containing segwit spending txns are now accepted in both
        # nodes.
        block(5)
        postforkblock = update_block(5, [txspend])
        yield accepted()
        sync_blocks(self.nodes)

        # Ok, now we check if a reorg work properly accross the activation.
        node_nonstd.invalidateblock(postforkblock.hash)
        assert (txspend.hash in node_nonstd.getrawmempool())

        # Also check that nodes checking for standardness don't return a segwit
        # spending txn into the mempool when disconnecting a block.
        node_std.invalidateblock(postforkblock.hash)
        assert (txspend.hash not in node_std.getrawmempool())

        # Deactivate the fork. The spending tx has been evicted from the
        # mempool
        node_nonstd.invalidateblock(forkblock.hash)
        assert (len(node_nonstd.getrawmempool()) == 0)
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            block.vtx.extend(new_transactions)
            old_sha256 = block.sha256
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand
        block = self.next_block
        node = self.nodes[0]

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Generate a key pair to test P2SH sigops count
        private_key = CECKey()
        private_key.set_secretbytes(b"replayprotection")
        public_key = private_key.get_pubkey()

        # This is a little handier to use than the version in blocktools.py
        def create_fund_and_spend_tx(spend, forkvalue=0):
            # Fund transaction
            script = CScript([public_key, OP_CHECKSIG])
            txfund = create_transaction(spend.tx, spend.n, b'', 50 * COIN,
                                        script)
            txfund.rehash()

            # Spend transaction
            txspend = CTransaction()
            txspend.vout.append(CTxOut(50 * COIN - 1000, CScript([OP_TRUE])))
            txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            sighashtype = (forkvalue << 8) | SIGHASH_ALL | SIGHASH_FORKID
            sighash = SignatureHashForkId(script, txspend, 0, sighashtype,
                                          50 * COIN)
            sig = private_key.sign(sighash) + \
                bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID]))
            txspend.vin[0].scriptSig = CScript([sig])
            txspend.rehash()

            return [txfund, txspend]

        def send_transaction_to_mempool(tx):
            tx_id = node.sendrawtransaction(ToHex(tx))
            assert (tx_id in set(node.getrawmempool()))
            return tx_id

        # Before the fork, no replay protection required to get in the mempool.
        txns = create_fund_and_spend_tx(out[0])
        send_transaction_to_mempool(txns[0])
        send_transaction_to_mempool(txns[1])

        # And txns get mined in a block properly.
        block(1)
        update_block(1, txns)
        yield accepted()

        # Replay protected transactions are rejected.
        replay_txns = create_fund_and_spend_tx(out[1], 0xffdead)
        send_transaction_to_mempool(replay_txns[0])
        assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR,
                                node.sendrawtransaction, ToHex(replay_txns[1]))

        # And block containing them are rejected as well.
        block(2)
        update_block(2, replay_txns)
        yield rejected(RejectResult(16, b'blk-bad-inputs'))

        # Rewind bad block
        tip(1)

        # Create a block that would activate the replay protection.
        bfork = block(5555)
        bfork.nTime = REPLAY_PROTECTION_START_TIME - 1
        update_block(5555, [])
        yield accepted()

        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])
        yield test

        # Check we are just before the activation time
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            REPLAY_PROTECTION_START_TIME - 1)

        # We are just before the fork, replay protected txns still are rejected
        assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR,
                                node.sendrawtransaction, ToHex(replay_txns[1]))

        block(3)
        update_block(3, replay_txns)
        yield rejected(RejectResult(16, b'blk-bad-inputs'))

        # Rewind bad block
        tip(5104)

        # Send some non replay protected txns in the mempool to check
        # they get cleaned at activation.
        txns = create_fund_and_spend_tx(out[2])
        send_transaction_to_mempool(txns[0])
        tx_id = send_transaction_to_mempool(txns[1])

        # Activate the replay protection
        block(5556)
        yield accepted()

        # Check we just activated the replay protection
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            REPLAY_PROTECTION_START_TIME)

        # Non replay protected transactions are not valid anymore,
        # so they should be removed from the mempool.
        assert (tx_id not in set(node.getrawmempool()))

        # Good old transactions are now invalid.
        send_transaction_to_mempool(txns[0])
        assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR,
                                node.sendrawtransaction, ToHex(txns[1]))

        # They also cannot be mined
        block(4)
        update_block(4, txns)
        yield rejected(RejectResult(16, b'blk-bad-inputs'))

        # Rewind bad block
        tip(5556)

        # The replay protected transaction is now valid
        replay_tx0_id = send_transaction_to_mempool(replay_txns[0])
        replay_tx1_id = send_transaction_to_mempool(replay_txns[1])

        # Make sure the transaction are ready to be mined.
        tmpl = node.getblocktemplate()

        found_id0 = False
        found_id1 = False

        for txn in tmpl['transactions']:
            txid = txn['txid']
            if txid == replay_tx0_id:
                found_id0 = True
            elif txid == replay_tx1_id:
                found_id1 = True

        assert (found_id0 and found_id1)

        # And the mempool is still in good shape.
        assert (replay_tx0_id in set(node.getrawmempool()))
        assert (replay_tx1_id in set(node.getrawmempool()))

        # They also can also be mined
        b5 = block(5)
        update_block(5, replay_txns)
        yield accepted()

        # Ok, now we check if a reorg work properly accross the activation.
        postforkblockid = node.getbestblockhash()
        node.invalidateblock(postforkblockid)
        assert (replay_tx0_id in set(node.getrawmempool()))
        assert (replay_tx1_id in set(node.getrawmempool()))

        # Deactivating replay protection.
        forkblockid = node.getbestblockhash()
        node.invalidateblock(forkblockid)
        # The funding tx is not evicted from the mempool, since it's valid in
        # both sides of the fork
        assert (replay_tx0_id in set(node.getrawmempool()))
        assert (replay_tx1_id not in set(node.getrawmempool()))

        # Check that we also do it properly on deeper reorg.
        node.reconsiderblock(forkblockid)
        node.reconsiderblock(postforkblockid)
        node.invalidateblock(forkblockid)
        assert (replay_tx0_id in set(node.getrawmempool()))
        assert (replay_tx1_id not in set(node.getrawmempool()))
    def get_tests(self):

        # Test no excessiveblocksize parameter specified
        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        # shorthand for functions
        block = self.chain.next_block

        # Create a new block
        block(0)
        self.chain.save_spendable_output()
        yield self.accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(self.chain.get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(15):
            n = i + 1
            block(n, spend=out[i], block_size=n * ONE_MEGABYTE)
            yield self.accepted()

        # Start moving MTP forward
        bfork = block(5555, out[15], block_size=32 * ONE_MEGABYTE)
        bfork.nTime = MAGNETIC_START_TIME - 1
        self.chain.update_block(5555, [])
        yield self.accepted()

        # Get to one block of the Nov 15, 2018 HF activation
        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.chain.tip, True])
        yield test

        # Check that the MTP is just before the configured fork point.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MAGNETIC_START_TIME - 1)

        # Before we acivate the Nov 15, 2018 HF, 32MB is the limit.
        block(4444, spend=out[16], block_size=32 * ONE_MEGABYTE + 1)
        yield self.rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block.
        self.chain.set_tip(5104)

        # Activate the Nov 15, 2018 HF
        block(5556)
        yield self.accepted()

        # Now MTP is exactly the fork time. Bigger blocks are now accepted.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MAGNETIC_START_TIME)

        # block of maximal size
        block(17, spend=out[16], block_size=128 * ONE_MEGABYTE)
        yield self.accepted()

        # Oversized blocks will cause us to be disconnected
        assert (not self.test.test_nodes[0].closed)
        block(18, spend=out[17], block_size=128 * ONE_MEGABYTE + 1)
        self.test.connections[0].send_message(msg_block((self.chain.tip)))
        self.test.wait_for_disconnections()
        assert (self.test.test_nodes[0].closed)

        # Rewind bad block and remake connection to node
        self.chain.set_tip(17)
        self.restart_network()
        self.test.wait_for_verack()

        # Check we can still mine a good size block
        block(5557, spend=out[18], block_size=self.excessive_block_size)
        yield self.accepted()
Пример #17
0
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # shorthand
        block = self.next_block
        node = self.nodes[0]
        node_ban = self.nodes[1]

        # save the current tip so its coinbase can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get a coinbase that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(199):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Generate a key pair to test P2SH sigops count
        privkeybytes = b"Schnorr!" * 4
        private_key = CECKey()
        private_key.set_secretbytes(privkeybytes)
        # get uncompressed public key serialization
        public_key = private_key.get_pubkey()

        def create_fund_and_spend_tx(spend, multi=False, sig='schnorr'):
            if multi:
                script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG])
            else:
                script = CScript([public_key, OP_CHECKSIG])

            # Fund transaction
            txfund = create_transaction(spend.tx, spend.n, b'', 50 * COIN,
                                        script)
            txfund.rehash()

            # Spend transaction
            txspend = CTransaction()
            txspend.vout.append(CTxOut(50 * COIN - 1000, CScript([OP_TRUE])))
            txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            sighashtype = SIGHASH_ALL | SIGHASH_FORKID
            hashbyte = bytes([sighashtype & 0xff])
            sighash = SignatureHashForkId(script, txspend, 0, sighashtype,
                                          50 * COIN)
            if sig == 'schnorr':
                txsig = schnorr.sign(privkeybytes, sighash) + hashbyte
            elif sig == 'ecdsa':
                txsig = private_key.sign(sighash) + hashbyte
            elif isinstance(sig, bytes):
                txsig = sig + hashbyte
            if multi:
                txspend.vin[0].scriptSig = CScript([b'', txsig])
            else:
                txspend.vin[0].scriptSig = CScript([txsig])
            txspend.rehash()

            return txfund, txspend

        def send_transaction_to_mempool(tx):
            tx_id = node.sendrawtransaction(ToHex(tx))
            assert (tx_id in set(node.getrawmempool()))
            return tx_id

        # Check we are disconnected when sending a txn that node_ban rejects.
        # (Can't actually get banned, since bitcoind won't ban local peers.)
        def check_for_ban_on_rejected_tx(tx):
            # Take a connection
            p2p = node_ban.p2ps.pop()
            assert (p2p.state == 'connected')

            # make sure we can ping
            p2p.sync_with_ping()

            # send the naughty transaction
            p2p.send_message(msg_tx(tx))

            # if not "banned", this will timeout and raise exception.
            p2p.wait_for_disconnect()

        # Setup fundings
        fundings = []
        fund, schnorrchecksigtx = create_fund_and_spend_tx(out[0])
        fundings.append(fund)
        fund, schnorrmultisigtx = create_fund_and_spend_tx(out[1], multi=True)
        fundings.append(fund)
        fund, ecdsachecksigtx = create_fund_and_spend_tx(out[2], sig='ecdsa')
        fundings.append(fund)

        fund, sig64checksigtx = create_fund_and_spend_tx(out[5], sig=sig64)
        fundings.append(fund)
        fund, sig64multisigtx = create_fund_and_spend_tx(out[6],
                                                         multi=True,
                                                         sig=sig64)
        fundings.append(fund)

        for fund in fundings:
            send_transaction_to_mempool(fund)
        block(1, transactions=fundings)
        yield accepted()

        # we're now set up for the various spends; make sure the other node
        # is set up, too.
        sync_blocks(self.nodes)

        # Typical ECDSA and Schnorr CHECKSIG are valid
        schnorr_tx_id = send_transaction_to_mempool(schnorrchecksigtx)
        ecdsa_tx_id = send_transaction_to_mempool(ecdsachecksigtx)
        # It can also be mined
        block(2, transactions=[schnorrchecksigtx, ecdsachecksigtx])
        yield accepted()
        assert schnorr_tx_id not in set(node.getrawmempool())
        assert ecdsa_tx_id not in set(node.getrawmempool())

        # Schnorr in multisig is rejected with mandatory error.
        assert_raises_rpc_error(-26, RPC_SCHNORR_MULTISIG_ERROR,
                                node.sendrawtransaction,
                                ToHex(schnorrmultisigtx))
        # And it is banworthy.
        check_for_ban_on_rejected_tx(schnorrmultisigtx)
        # And it can't be mined
        block(13, transactions=[schnorrmultisigtx])
        yield rejected(RejectResult(16, b'blk-bad-inputs'))
        # Rewind bad block
        tip(2)

        # If we try to submit a bad 64-byte sig, we fail with mandatory errors.
        # In CHECKSIG it's invalid Schnorr and hence NULLFAIL.
        assert_raises_rpc_error(-26,
                                RPC_NULLFAIL_ERROR, node.sendrawtransaction,
                                ToHex(sig64checksigtx))
        # In CHECKMULTISIG it's invalid length and hence BAD_LENGTH.
        assert_raises_rpc_error(-26, RPC_SCHNORR_MULTISIG_ERROR,
                                node.sendrawtransaction,
                                ToHex(sig64multisigtx))
        # Getting sent these transactions is banworthy.
        check_for_ban_on_rejected_tx(sig64checksigtx)
        check_for_ban_on_rejected_tx(sig64multisigtx)
        # And they can't be mined either...
        block(14, transactions=[sig64checksigtx])
        yield rejected(RejectResult(16, b'blk-bad-inputs'))
        # Rewind bad block
        tip(2)
        block(15, transactions=[sig64multisigtx])
        yield rejected(RejectResult(16, b'blk-bad-inputs'))
        # Rewind bad block
        tip(2)
Пример #18
0
    def get_tests(self):
        node = self.nodes[0]
        self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]

        # First, we generate some coins to spend.
        node.generate(125)

        # Create various outputs using the OP_AND to check for activation.
        tx_hex = self.create_and_tx(25)
        txid = node.sendrawtransaction(tx_hex)
        assert (txid in set(node.getrawmempool()))

        node.generate(1)
        assert (txid not in set(node.getrawmempool()))

        # register the spendable outputs.
        tx = FromHex(CTransaction(), tx_hex)
        tx.rehash()
        spendable_ands = [
            PreviousSpendableOutput(tx, i) for i in range(len(tx.vout))
        ]

        def spend_and():
            outpoint = spendable_ands.pop()
            out = outpoint.tx.vout[outpoint.n]
            value = int(out.nValue - (self.relayfee * COIN))
            tx = CTransaction()
            tx.vin = [CTxIn(COutPoint(outpoint.tx.sha256, outpoint.n))]
            tx.vout = [CTxOut(value, CScript([]))]
            tx.rehash()
            return tx

        # Check that large opreturn are not accepted yet.
        print("Try to use the monolith opcodes before activation")

        tx0 = spend_and()
        tx0_hex = ToHex(tx0)
        assert_raises_jsonrpc(-26, RPC_DISABLED_OPCODE_ERROR,
                              node.sendrawtransaction, tx0_hex)

        # Push MTP forward just before activation.
        print("Pushing MTP just before the activation and check again")
        node.setmocktime(MONOLITH_START_TIME)

        # returns a test case that asserts that the current tip was accepted
        def accepted(tip):
            return TestInstance([[tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(tip, reject=None):
            if reject is None:
                return TestInstance([[tip, False]])
            else:
                return TestInstance([[tip, reject]])

        def next_block(block_time):
            # get block height
            blockchaininfo = node.getblockchaininfo()
            height = int(blockchaininfo['blocks']) + 1

            # create the block
            coinbase = create_coinbase(absoluteHeight=height)
            coinbase.rehash()
            block = create_block(int(node.getbestblockhash(), 16), coinbase,
                                 block_time)
            block.nVersion = 4

            # Do PoW, which is cheap on regnet
            block.solve()
            return block

        for i in range(6):
            b = next_block(MONOLITH_START_TIME + i - 1)
            yield accepted(b)

        # Check again just before the activation time
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MONOLITH_START_TIME - 1)
        assert_raises_jsonrpc(-26, RPC_DISABLED_OPCODE_ERROR,
                              node.sendrawtransaction, tx0_hex)

        def add_tx(block, tx):
            block.vtx.append(tx)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()

        b = next_block(MONOLITH_START_TIME + 6)
        add_tx(b, tx0)
        yield rejected(b, RejectResult(16, b'blk-bad-inputs'))

        print("Activates the new opcodes")
        fork_block = next_block(MONOLITH_START_TIME + 6)
        yield accepted(fork_block)

        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MONOLITH_START_TIME)

        tx0id = node.sendrawtransaction(tx0_hex)
        assert (tx0id in set(node.getrawmempool()))

        # Transactions can also be included in blocks.
        monolithblock = next_block(MONOLITH_START_TIME + 7)
        add_tx(monolithblock, tx0)
        yield accepted(monolithblock)

        print("Cause a reorg that deactivate the monolith opcodes")

        # Invalidate the monolith block, ensure tx0 gets back to the mempool.
        assert (tx0id not in set(node.getrawmempool()))

        node.invalidateblock(format(monolithblock.sha256, 'x'))
        assert (tx0id in set(node.getrawmempool()))

        node.invalidateblock(format(fork_block.sha256, 'x'))
        assert (tx0id not in set(node.getrawmempool()))
Пример #19
0
    def get_tests(self):
        # shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        # Create a new block
        block(0)
        self.chain.save_spendable_output()
        yield self.accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(105):
            block(5000 + i)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(self.chain.get_spendable_output())

        # Block with height 107.
        block(1, spend=out[0])
        yield self.accepted()

        # Create block with height 108 (genesis NOT activated).
        block(2, spend=out[1])
        tx0 = create_transaction(out[2].tx, out[2].n, b"", 100000,
                                 CScript([OP_RETURN]))
        self.chain.update_block(2, [tx0])
        # \x51 is OP_TRUE
        tx1 = create_transaction(tx0, 0, b'\x51', 1, CScript([OP_TRUE]))
        b108_rejected = self.chain.update_block(2, [tx1])
        self.log.info(
            "Created block %s on height %d that tries to spend from block on height %d.",
            b108_rejected.hash, self.genesisactivationheight - 1,
            self.genesisactivationheight - 1)
        yield self.rejected(RejectResult(16,
                                         b'bad-txns-inputs-missingorspent'))

        # Rewind bad block (height is 107).
        self.chain.set_tip(1)

        # Create block on height 108 (genesis NOT activated).
        block(3, spend=out[3])
        tx0 = create_transaction(out[4].tx, out[4].n, b"", 100000,
                                 CScript([OP_RETURN]))
        b108 = self.chain.update_block(3, [tx0])
        self.log.info("Created block %s on height %d.", b108.hash,
                      self.genesisactivationheight - 1)
        yield self.accepted()

        # Create block on height 109 and try to spend from block on height 108
        block(4, spend=out[5])
        # \x51 is OP_TRUE
        tx1 = create_transaction(tx0, 0, b'\x51', 1, CScript([OP_TRUE]))
        b109_rejected = self.chain.update_block(4, [tx1])
        self.log.info(
            "Created block %s on height %d that tries to spend from block on height %d.",
            b109_rejected.hash, self.genesisactivationheight,
            self.genesisactivationheight - 1)
        yield self.rejected(RejectResult(16,
                                         b'bad-txns-inputs-missingorspent'))

        # Rewind bad block (height is 108).
        self.chain.set_tip(3)

        # Create block on height 109 and try to spend from block on height 109.
        block(5, spend=out[6])
        tx0 = create_transaction(out[7].tx, out[7].n, b"", 100000,
                                 CScript([OP_RETURN]))

        self.chain.update_block(5, [tx0])
        # \x51 is OP_TRUE
        tx1 = create_transaction(tx0, 0, b'\x51', 1, CScript([OP_TRUE]))
        b109_accepted = self.chain.update_block(5, [tx1])
        self.log.info(
            "Created block %s on height %d that tries to spend from block on height %d.",
            b109_accepted.hash, self.genesisactivationheight,
            self.genesisactivationheight)
        yield self.accepted()

        #############

        # At this point, we have tx0 and tx1 in cache script marked as valid.
        # Now, invalidate blocks 109 and 108 so that we are in state before genesis.

        node.invalidateblock(format(b109_accepted.sha256, 'x'))
        sleep(1)

        # tx0 and tx1 are in mempool (currently valid because it was sent after genesis)
        assert_equal(True, tx0.hash in node.getrawmempool())
        assert_equal(True, tx1.hash in node.getrawmempool())

        node.invalidateblock(format(b108.sha256, 'x'))

        # tx0 and tx1 are not in mempool (mempool is deleted when 108 is invalidated)
        assert_equal(False, tx0.hash in node.getrawmempool())
        assert_equal(False, tx1.hash in node.getrawmempool())

        # So we are at height 107.
        assert_equal(node.getblock(node.getbestblockhash())['height'], 107)

        self.nodes[0].generate(1)
        tx = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['tx']
        assert_equal(False, tx0.hash in tx)
        assert_equal(False, tx1.hash in tx)

        # Now we are at height 108.
        assert_equal(node.getblock(node.getbestblockhash())['height'], 108)
Пример #20
0
    def get_tests(self):
        if self.tip is None:
            self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
        self.block_time = int(time.time()) + 1
        '''
        Create a new block with an anyone-can-spend coinbase
        '''
        height = 1
        block = create_block(self.tip, create_coinbase(height),
                             self.block_time)
        self.block_time += 1
        block.solve()
        # Save the coinbase for later
        self.block1 = block
        self.tip = block.sha256
        height += 1
        yield TestInstance([[block, True]])
        '''
        Now we need that block to mature so we can spend the coinbase.
        '''
        test = TestInstance(sync_every_block=False)
        for i in range(100):
            block = create_block(self.tip, create_coinbase(height),
                                 self.block_time)
            block.solve()
            self.tip = block.sha256
            self.block_time += 1
            test.blocks_and_transactions.append([block, True])
            height += 1
        yield test

        # b'\x64' is OP_NOTIF
        # Transaction will be rejected with code 16 (REJECT_INVALID)
        tx1 = create_transaction(self.block1.vtx[0], 0, b'\x64',
                                 50 * COIN - 12000)
        yield TestInstance([[
            tx1,
            RejectResult(RejectInvalid, b'mandatory-script-verify-flag-failed')
        ]])

        self.log.debug(
            "[tx_check 001] should reject coinbase tx -----------------------------------------------------"
        )
        # Transaction will be rejected with code 16 (REJECT_INVALID)
        tx1 = create_coinbase(0)
        yield TestInstance(
            [[tx1, RejectResult(RejectInvalid, b'bad-tx-coinbase')]])

        self.log.debug(
            "[tx_check 002] should reject non final tx ----------------------------------------------------"
        )
        # Transaction will be rejected with code 16 (REJECT_INVALID)
        tx1 = create_transaction(self.block1.vtx[0], 0, b'', 50 * COIN - 200)
        tx1.nLockTime = 5000000  #high lock height
        tx1.vin[0].nSequence = 0  #not final sequence
        tx1.rehash()
        yield TestInstance(
            [[tx1, RejectResult(RejectNonstandard, b'bad-txns-nonfinal')]])

        self.log.debug(
            "[tx_check 003] should reject tx whose input has already spent in mempool ---------------------"
        )
        # Transaction will be rejected with code 16 (REJECT_INVALID)
        tx1 = create_transaction(self.block1.vtx[0], 0, b'\x51',
                                 50 * COIN - 300)
        yield TestInstance([[
            tx1,
            RejectResult(
                RejectNonstandard,
                b'non-mandatory-script-verify-flag (Script did not clean its stack)'
            )
        ]])

        self.log.debug(
            "[tx_check 004-0] should reject tx whose input has already spent in mempool ---------------------"
        )
        self.log.debug(
            "          004-1 --------------------- create a transaction tx1 with output"
        )
        tx1 = create_transaction(self.block1.vtx[0], 0, b'', 50 * COIN - 300,
                                 CScript([OP_TRUE]))
        yield TestInstance([[tx1, True]])
        self.log.debug(
            "          004-2 --------------------- spend output of tx1")
        tx2 = create_transaction(tx1, 0, b'', 50 * COIN - 300 - 100)
        yield TestInstance([[tx2, True]])
        self.log.debug(
            "          004-3 --------------------- try spend output of tx1 again"
        )
        tx3 = create_transaction(tx1, 0, b'', 50 * COIN - 300 - 200)
        yield TestInstance([[tx3, False]])

        self.log.debug(
            "[tx_check finished] --------------------------------------------------------------------------"
        )
Пример #21
0
    def get_tests(self):
        # Shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        block(0)
        yield self.accepted()

        test, out, _ = prepare_init_chain(self.chain, 580, 200)

        yield test

        # Create block on height 581 with some transactions for us to spend
        nLockTime = self.start_time
        block(1, spend=out[0])
        spend_tx1 = self.create_transaction(out[1].tx, out[1].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx2 = self.create_transaction(out[2].tx, out[2].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        self.chain.update_block(1, [spend_tx1, spend_tx2])
        yield self.accepted()

        # At height 582, create a transaction with nSequence set to prevent mining until height 584
        rej_tx = self.create_locked_transaction(spend_tx1, 0, CScript(), 1,
                                                CScript([OP_TRUE]), nLockTime,
                                                self.make_sequence(584))
        # The transaction should be rejected because genesis has not yet activated and the transaction is not yet minable
        yield TestInstance([[rej_tx, RejectResult(64, b'non-BIP68-final')]])

        # At height 582, create a transaction with nSequence set to prevent mining until height 583.
        tx1 = self.create_locked_transaction(spend_tx1, 0, CScript(), 1,
                                             CScript([OP_TRUE]), nLockTime,
                                             self.make_sequence(583))
        # The transaction should be accepted because genesis has not yet activated but the transaction is minable in the next block
        yield TestInstance([[tx1, True]])
        assert (tx1.hash in self.nodes[0].getrawmempool())

        # Move height on to 583, tx1 is mined and removed from mempool
        self.nodes[0].generate(1)
        assert (tx1.hash not in self.nodes[0].getrawmempool())

        # Create block with bip68 non-final txn in it. It will be rejected.
        self.build_on_tip(9)
        block(10, spend=out[5])
        bip68_non_final_tx = self.create_locked_transaction(
            spend_tx2, 0, CScript(), 1, CScript([OP_TRUE]), nLockTime,
            self.make_sequence(587))
        self.chain.update_block(10, [bip68_non_final_tx])
        yield self.rejected(RejectResult(16, b'bad-txns-nonfinal'))

        # Move height on to block 600; genesis is activated.
        self.nodes[0].generate(self.genesisactivationheight -
                               self.nodes[0].getblockcount())
        self.log.info("Genesis activated, height {}".format(
            self.nodes[0].getblockcount()))

        # Get tip from bitcoind because it's further along than we are, and we want to build on it
        self.build_on_tip(2)

        # Create block on height 601 with some more transactions for us to spend
        nLockTime = self.start_time
        block(3, spend=out[10])
        spend_tx2 = self.create_transaction(out[12].tx, out[12].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx3 = self.create_transaction(out[13].tx, out[13].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx4 = self.create_transaction(out[14].tx, out[14].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx5 = self.create_transaction(out[15].tx, out[15].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx6 = self.create_transaction(out[16].tx, out[16].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx7 = self.create_transaction(out[17].tx, out[17].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        spend_tx8 = self.create_transaction(out[18].tx, out[18].n, CScript(),
                                            100000, CScript([OP_TRUE]))
        self.chain.update_block(
            3, [spend_tx2, spend_tx3, spend_tx4, spend_tx5, spend_tx6])
        yield self.accepted()

        # Check txn with empty vin is rejected with the expected code (fix for CORE-430).
        tx2 = self.create_locked_transaction(spend_tx2, 0, CScript(), 1000,
                                             CScript([OP_TRUE]), nLockTime,
                                             0x00000001)
        tx2.vin = []
        tx2.rehash()
        yield TestInstance([[tx2, RejectResult(16, b'bad-txns-vin-empty')]])

        # At height 601, create a transaction with nLockTime in the past & nSequence set to the value
        # that would have (pre-genesis) meant minable at height 605.
        tx2 = self.create_locked_transaction(spend_tx2, 0, CScript(), 1000,
                                             CScript([OP_TRUE]), nLockTime,
                                             self.make_sequence(605))
        # The transaction should be accepted because this is now considered final.
        yield TestInstance([[tx2, True]])
        mempool = self.nodes[0].getrawmempool()
        assert (tx2.hash in mempool)

        # At height 601, create a transaction with nLockTime in the future & nSequence not 0xFFFFFFFF.
        nLockTime = int(time.time()) + 1000
        tx3 = self.create_locked_transaction(spend_tx3, 0, CScript(), 1000,
                                             CScript([OP_TRUE]), nLockTime,
                                             0x00000003)
        tx4 = self.create_locked_transaction(spend_tx4, 0, CScript(), 1000,
                                             CScript([OP_TRUE]), nLockTime,
                                             0x00000003)
        tx5 = self.create_locked_transaction(spend_tx5, 0, CScript(), 1000,
                                             CScript([OP_TRUE]),
                                             nLockTime + 30, 0x00000003)
        # The transactions should be accepted into the non-final-mempool but not the main mempool.
        yield TestInstance([[tx3, DiscardResult()], [tx4, DiscardResult()],
                            [tx5, DiscardResult()]])
        mempool = self.nodes[0].getrawmempool()
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx3.hash not in mempool)
        assert (tx3.hash in nonfinalmempool)
        assert (tx4.hash not in mempool)
        assert (tx4.hash in nonfinalmempool)
        assert (tx5.hash not in mempool)
        assert (tx5.hash in nonfinalmempool)

        # At height 601, create a transaction with nLockTime in the future & nSequence equal to 0xFFFFFFFF.
        tx6 = self.create_locked_transaction(spend_tx6, 0, CScript(), 1000,
                                             CScript([OP_TRUE]), nLockTime,
                                             0xFFFFFFFF)
        # The transaction will be considered final and accepted into the main mempool.
        yield TestInstance([[tx6, True]])
        mempool = self.nodes[0].getrawmempool()
        assert (tx6.hash in mempool)

        # Create transaction that tries to double spend UTXOs locked by txn3. It will be discarded.
        tx3_double_spend = self.create_transaction(spend_tx3, 0, CScript(),
                                                   1000, CScript([OP_TRUE]))
        tx3_double_spend.vin.append(spend_tx7.vin[0])
        yield TestInstance([[tx3_double_spend, DiscardResult()]])

        # Move height on to 602. Txn2 and Txn6 will be mined and removed from mempool, txn3, 4 & 5 will not have been moved there.
        self.nodes[0].generate(1)
        mempool = self.nodes[0].getrawmempool()
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx2.hash not in mempool)
        assert (tx3.hash not in mempool)
        assert (tx4.hash not in mempool)
        assert (tx5.hash not in mempool)
        assert (tx6.hash not in mempool)
        assert (tx3.hash in nonfinalmempool)
        assert (tx4.hash in nonfinalmempool)
        assert (tx5.hash in nonfinalmempool)

        # Create standard transaction which tries to spend non-final txn3. It will be an orphan.
        txn_spend = self.create_transaction(tx3, 0, CScript(), 100,
                                            CScript([OP_TRUE]))
        yield TestInstance([[txn_spend, DiscardResult()]])

        # Create non-final transaction which tries to spend non-final txn3. It will be rejected.
        txn_spend = self.create_locked_transaction(tx3, 0, CScript(), 100,
                                                   CScript([OP_TRUE]),
                                                   nLockTime, 0x00000003)
        yield TestInstance(
            [[txn_spend,
              RejectResult(64, b'too-long-non-final-chain')]])

        # Send update to txn3 with lower nSequence. It will be rejected.
        tx3.vin[0].nSequence -= 1
        tx3.rehash()
        yield TestInstance([[tx3, RejectResult(16, b'bad-txn-update')]])

        # Send update to txn3 with higher nSequence. It will be accepted.
        tx3.vin[0].nSequence += 2
        tx3.rehash()
        yield TestInstance([[tx3, DiscardResult()]])
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert_equal(len(nonfinalmempool), 3)
        assert (tx3.hash in nonfinalmempool)
        assert (tx4.hash in nonfinalmempool)
        assert (tx5.hash in nonfinalmempool)

        # Send multiple updates together for txn3 with higher and different nSequence numbers.
        # Just one of the updates will be accepted, but due to PTV we can't be sure which.
        tx3_update1 = copy.deepcopy(tx3)
        tx3_update2 = copy.deepcopy(tx3)
        tx3_update3 = copy.deepcopy(tx3)
        tx3_update1.vin[0].nSequence += 1
        tx3_update2.vin[0].nSequence += 3  # update2 has the highest nSequence
        tx3_update3.vin[0].nSequence += 2
        tx3_update1.rehash()
        tx3_update2.rehash()
        tx3_update3.rehash()
        updateHashes = {tx3_update1.hash, tx3_update2.hash, tx3_update3.hash}
        yield TestInstance([[tx3_update1, None], [tx3_update2, None],
                            [tx3_update3, None]])
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert_equal(len(nonfinalmempool), 3)
        assert (bool(tx3_update1.hash in nonfinalmempool)
                ^ bool(tx3_update2.hash in nonfinalmempool)
                ^ bool(tx3_update3.hash in nonfinalmempool))
        assert (tx4.hash in nonfinalmempool)
        assert (tx5.hash in nonfinalmempool)

        # Remember which update got accepted
        tx3_update_accepted = None
        for update in updateHashes:
            if update in nonfinalmempool:
                tx3_update_accepted = update
                break

        # Send an invalid update in a single txn which wants to update both txn4 and txn5. It will be rejected.
        tx4_tx5_invalid = copy.deepcopy(tx4)
        tx4_tx5_invalid.vin.append(tx5.vin[0])
        tx4_tx5_invalid.vin[0].nSequence += 1
        tx4_tx5_invalid.vin[1].nSequence += 1
        tx4_tx5_invalid.rehash()
        yield TestInstance(
            [[tx4_tx5_invalid,
              RejectResult(16, b'bad-txn-update')]])
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert_equal(len(nonfinalmempool), 3)
        assert (tx4.hash in nonfinalmempool)
        assert (tx5.hash in nonfinalmempool)

        # Send an invalid update for txn4 which changes the number of inputs. It will be rejected.
        tx4_invalid = copy.deepcopy(tx4)
        tx4_invalid.vin.append(spend_tx8.vin[0])
        tx4_invalid.vin[0].nSequence += 1
        tx4_invalid.rehash()
        yield TestInstance([[tx4_invalid,
                             RejectResult(16, b'bad-txn-update')]])
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert_equal(len(nonfinalmempool), 3)
        assert (tx4.hash in nonfinalmempool)

        # Send an update for txn3 with nSequence = 0xFFFFFFFF. It will be finalised and moved
        # into the main mempool.
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx3_update_accepted in nonfinalmempool)
        tx3.vin[0].nSequence = 0xFFFFFFFF
        tx3.rehash()
        yield TestInstance([[tx3, True]])
        mempool = self.nodes[0].getrawmempool()
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx3.hash in mempool)
        assert (tx3.hash not in nonfinalmempool)
        assert (tx3_update_accepted not in nonfinalmempool)

        # Move time on beyond the nLockTime for txn4. It will be finalised and moved into the
        # main mempool.
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx4.hash in nonfinalmempool)
        assert (tx5.hash in nonfinalmempool)
        self.nodes[0].setmocktime(nLockTime + 1)
        self.nodes[0].generate(6)
        wait_until(lambda: tx4.hash in self.nodes[0].getrawmempool(),
                   timeout=5)
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx4.hash not in nonfinalmempool)
        assert (tx5.hash in nonfinalmempool)

        # Get tip from bitcoind because it's further along than we are, and we want to build on it
        self.build_on_tip(4)

        # Create block with some more transactions for us to spend
        block(5, spend=out[20])
        spend_tx11 = self.create_transaction(out[21].tx, out[21].n, CScript(),
                                             100000, CScript([OP_TRUE]))
        spend_tx12 = self.create_transaction(out[22].tx, out[22].n, CScript(),
                                             100000, CScript([OP_TRUE]))
        self.chain.update_block(5, [spend_tx11, spend_tx12])
        yield self.accepted()

        # Send txn time-locked for the current block height so that it can be immediately mined
        self.nodes[0].generate(1)
        currentHeight = self.nodes[0].getblockcount()
        tx7 = self.create_locked_transaction(spend_tx11, 0, CScript(), 1000,
                                             CScript([OP_TRUE]), currentHeight,
                                             0x01)
        yield TestInstance([[tx7, True]])
        # Mine it & spend it
        self.nodes[0].generate(1)
        tx7_spend = self.create_transaction(tx7, 0, CScript(), 100,
                                            CScript([OP_TRUE]))
        yield TestInstance([[tx7_spend, True]])
        mempool = self.nodes[0].getrawmempool()
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx7.hash not in nonfinalmempool)
        assert (tx7.hash not in mempool)
        assert (tx7_spend.hash not in nonfinalmempool)
        assert (tx7_spend.hash in mempool)
        # Reorg back so tx7 is no longer final
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        # Both txns will have been removed from the mempool
        mempool = self.nodes[0].getrawmempool()
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        assert (tx7.hash not in nonfinalmempool)
        assert (tx7.hash not in mempool)
        assert (tx7_spend.hash not in nonfinalmempool)
        assert (tx7_spend.hash not in mempool)

        # Send non-final txn
        currentHeight = self.nodes[0].getblockcount()
        tx7 = self.create_locked_transaction(spend_tx11, 0, CScript(), 1000,
                                             CScript([OP_TRUE]),
                                             currentHeight + 1, 0x01)
        yield TestInstance([[tx7, DiscardResult()]])
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        mempool = self.nodes[0].getrawmempool()
        assert (tx7.hash in nonfinalmempool)
        # Create a block that contains a txn that conflicts with the previous non-final txn
        tx7_double_spend = self.create_transaction(spend_tx11, 0, CScript(),
                                                   1000, CScript([OP_TRUE]))
        block(6, spend=out[30])
        self.chain.update_block(6, [tx7_double_spend])
        yield self.accepted()
        nonfinalmempool = self.nodes[0].getrawnonfinalmempool()
        mempool = self.nodes[0].getrawmempool()
        assert (tx7.hash not in nonfinalmempool)
        assert (tx7.hash not in mempool)

        # Reorg back and check that the transaction from the block is the txn picked to be kept in the mempool
        mempool = self.nodes[0].getrawmempool()
        assert (tx7_double_spend.hash not in mempool)
        assert (tx7.hash not in mempool)
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        mempool = self.nodes[0].getrawmempool()
        assert (tx7_double_spend.hash in mempool)
        assert (tx7.hash not in mempool)

        # Check that post-genesis if we receive a block containing a txn that pre-genesis would have been BIP68
        # non-final but now is final, we accept that block ok.
        self.nodes[0].generate(1)
        self.build_on_tip(11)
        currentHeight = self.nodes[0].getblockcount()
        block(12, spend=out[31])
        bip68_non_final_tx = self.create_locked_transaction(
            spend_tx12, 0, CScript(), 1, CScript([OP_TRUE]), 0,
            self.make_sequence(currentHeight + 10))
        self.chain.update_block(12, [bip68_non_final_tx])
        yield self.accepted()

        # Check that post-genesis we will not accept a block containing a non-final transaction
        currentHeight = self.nodes[0].getblockcount()
        block(13, spend=out[32])
        non_final_tx = self.create_locked_transaction(out[32].tx, out[32].n,
                                                      CScript(), 1,
                                                      CScript([OP_TRUE]),
                                                      currentHeight + 1, 0x01)
        self.chain.update_block(13, [non_final_tx])
        yield self.rejected(RejectResult(16, b'bad-txns-nonfinal'))
Пример #22
0
    def get_tests(self):
        # shorthand for functions
        block = self.chain.next_block

        node0 = self.nodes[0]
        node1 = self.nodes[1]
        node2 = self.nodes[2]

        self.chain.set_genesis_hash(int(node1.getbestblockhash(), 16))

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(0,100):
            block(i, coinbase_pubkey=self.coinbase_pubkey)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        yield test

        # create two addresses on the node0
        address1 = node0.getnewaddress()
        scriptPubKey1 = node0.validateaddress(address1)["scriptPubKey"]
        address2 = node0.getnewaddress()
        scriptPubKey2 = node0.validateaddress(address2)["scriptPubKey"]

        # import P2SH(P2PKH) on node1 and node2
        # have to do in this way because it seems that we can't create P2PKH address and later add P2SH(P2PKH) to the same private key
        node1.importaddress(scriptPubKey1, "x", True, True) # importing script, not key
        node1.importprivkey(node0.dumpprivkey(address1))
        node2.importaddress(scriptPubKey2, "x", True, True) # importing script, not key
        node2.importprivkey(node0.dumpprivkey(address2))

        out = [self.chain.get_spendable_output() for _ in range(50)]

        # Create a p2sh transactions
        def new_P2SH_tx(scriptPubKey):
            output = out.pop(0)
            redeem_script = CScript(hex_str_to_bytes(scriptPubKey))
            redeem_script_hash = hash160(redeem_script)
            p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])
            return create_and_sign_transaction(spend_tx=output.tx, n=output.n,
                                               value=output.tx.vout[0].nValue-100,
                                               coinbase_key=self.coinbase_key,
                                               script=p2sh_script)

        # Add the transactions to the block
        assert node0.getblockcount() < self.genesisactivationheight, "We must be before genesis"
        block(100)
        new_tx1 = new_P2SH_tx(scriptPubKey1)
        self.chain.update_block(100, [new_tx1]) # sending funds to P2SH address BEFORE genesis
        yield self.accepted()

        current_height = node1.getblockcount()

        for i in range(self.genesisactivationheight - current_height):
            block(101+i, coinbase_pubkey=self.coinbase_pubkey)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        yield test

        assert node0.getblockcount() >= self.genesisactivationheight, "We must be after genesis"

        block(150)
        new_tx2 = new_P2SH_tx(scriptPubKey2)
        self.chain.update_block(150, [new_tx2]) # sending funds to P2SH address AFTER genesis
        yield self.rejected(RejectResult(16, b'bad-txns-vout-p2sh'))

        self.chain.set_tip(149)

        balance1 = node1.getbalance("*", 1, False)
        assert balance1 * COIN == new_tx1.vout[0].nValue, "Wallet has registered pre genesis transaction."
        balance2 = node2.getbalance("*", 1, False)
        assert balance2 * COIN == 0, "No funds in wallet as transaction is not accepted."

        # Pre genesis P2SH transaction can be spent through wallet
        node1.sendtoaddress(node0.getnewaddress(), balance1 - 1)

        balance1_new = node1.getbalance("*", 1, False)
        assert balance1 > balance1_new, "Pre genesis P2SH is spent."
    def get_tests(self):

        # move the tip back to a previous block
        def tip(number):
            self.chain.set_tip(number)

        # shorthand for functions
        block = self.chain.next_block

        node = self.nodes[0]
        gen_hash = int(node.getbestblockhash(), 16)
        self.chain.set_genesis_hash(gen_hash)
        spendable_outputs = []

        # Create a new block
        block(0)
        # Test 1
        self.chain.save_spendable_output()
        yield self.accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(119):
            block(5000 + i)
            test.blocks_and_transactions.append([self.chain.tip, True])
            self.chain.save_spendable_output()
        # Test 2
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(119):
            out.append(self.chain.get_spendable_output())

        logger.info(
            "Before Genesis, blocks 2, 4, 6 should be rejected because of too many sigops"
        )
        # Test 3
        # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop
        # Test that a block with a lot of checksigs is okay
        #tip(0) - height = 120

        lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB)
        b1 = block(1,
                   spend=out[0],
                   script=lots_of_checksigs,
                   block_size=ONE_MEGABYTE)
        self.chain.save_spendable_output()
        yield self.accepted()
        # Test 4
        # Test that a block with too many checksigs is rejected
        b2 = block(2,
                   spend=out[1],
                   script=lots_of_checksigs,
                   block_size=ONE_MEGABYTE,
                   extra_sigops=1)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # Test 5
        # Test that a block with a lot of checkmultisigs is ok

        tip(1)
        lots_of_multisigs = CScript([OP_CHECKMULTISIG] *
                                    (MAX_BLOCK_SIGOPS_PER_MB // 20))
        b3 = block(3,
                   spend=out[3],
                   script=lots_of_multisigs,
                   block_size=ONE_MEGABYTE)
        assert_equal(get_legacy_sigopcount_block(b3), MAX_BLOCK_SIGOPS_PER_MB)
        yield self.accepted()

        # Test 6
        # this goes over the limit because the coinbase has one sigop
        b4 = block(4,
                   spend=out[4],
                   script=lots_of_multisigs,
                   block_size=ONE_MEGABYTE,
                   extra_sigops=1)
        assert_equal(get_legacy_sigopcount_block(b4),
                     MAX_BLOCK_SIGOPS_PER_MB + 1)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # CHECKSIGVERIFY
        tip(3)
        # Test 7
        lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] *
                                    (MAX_BLOCK_SIGOPS_PER_MB))
        b5 = block(5,
                   spend=out[5],
                   script=lots_of_checksigs,
                   block_size=ONE_MEGABYTE)
        assert_equal(get_legacy_sigopcount_block(b5), MAX_BLOCK_SIGOPS_PER_MB)
        self.chain.save_spendable_output()
        yield self.accepted()

        # Test 8
        b6 = block(6,
                   spend=out[6],
                   script=lots_of_checksigs,
                   block_size=ONE_MEGABYTE,
                   extra_sigops=1)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))
        tip(5)
        b7 = block(7)
        yield self.accepted()

        logger.info(
            "Genesis is enabled, all the following blocks should be accepted")

        # Test 10
        # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop
        # Test that a block with a lot of checksigs is okay
        lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB)
        b9 = block(9,
                   spend=out[9],
                   script=lots_of_checksigs,
                   block_size=ONE_MEGABYTE)
        self.chain.save_spendable_output()
        yield self.accepted()

        # Test 11
        # Test that a block with 'too many' checksigs is now accepted
        b10 = block(10,
                    spend=out[10],
                    script=lots_of_checksigs,
                    block_size=ONE_MEGABYTE,
                    extra_sigops=1)
        yield self.accepted()
        self.chain.save_spendable_output()

        # Test 12
        # this goes over the limit because the coinbase has one sigop, but should pass bcs genesis threshold was reached
        b11 = block(11,
                    spend=out[11],
                    script=lots_of_multisigs,
                    block_size=ONE_MEGABYTE,
                    extra_sigops=1)
        yield self.accepted()

        # CHECKSIGVERIFY
        # Test 13
        lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] *
                                    MAX_BLOCK_SIGOPS_PER_MB)
        b12 = block(12,
                    spend=out[12],
                    script=lots_of_checksigs,
                    block_size=ONE_MEGABYTE)
        self.chain.save_spendable_output()
        yield self.accepted()
        # Test 14
        b13 = block(13,
                    spend=out[13],
                    script=lots_of_checksigs,
                    block_size=ONE_MEGABYTE,
                    extra_sigops=1)
        self.chain.save_spendable_output()
        yield self.accepted()

        # Test 15
        # Test that a block with MAX_BLOCK_SIGOPS_PER_MB_POST_GENESIS checksigs is OK
        MAX_BLOCK_SIGOPS_PER_MB_POST_GENESIS = 1000000
        lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB)
        b14 = block(14,
                    spend=out[14],
                    script=lots_of_checksigs,
                    block_size=ONE_MEGABYTE + 500,
                    extra_sigops=MAX_BLOCK_SIGOPS_PER_MB_POST_GENESIS -
                    MAX_BLOCK_SIGOPS_PER_MB)
        yield self.accepted()
Пример #24
0
    def get_tests(self):
        node = self.nodes[0]
        self.chain.set_genesis_hash(int(node.getbestblockhash(), 16))

        # shorthand for functions
        block = self.chain.next_block

        block(0)
        yield self.accepted()

        test, out, _ = prepare_init_chain(self.chain, 99, 100)

        yield test

        # Let's build some blocks and test them.
        for i in range(16):
            n = i + 1
            block(n, spend=out[i], block_size=n * ONE_MEGABYTE // 2)
            yield self.accepted()

        # block of maximal size
        block(17, spend=out[16], block_size=self.excessive_block_size)
        yield self.accepted()

        # Oversized blocks will cause us to be disconnected
        assert (not self.test.test_nodes[0].closed)
        block(18, spend=out[17], block_size=self.excessive_block_size + 1)
        self.test.connections[0].send_message(msg_block((self.chain.tip)))
        self.test.wait_for_disconnections()
        assert (self.test.test_nodes[0].closed)

        # Rewind bad block and remake connection to node
        self.chain.set_tip(17)
        self.restart_network()
        self.test.wait_for_verack()

        # Accept many sigops
        lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB)
        block(19,
              spend=out[17],
              script=lots_of_checksigs,
              block_size=ONE_MEGABYTE)
        yield self.accepted()

        block(20,
              spend=out[18],
              script=lots_of_checksigs,
              block_size=ONE_MEGABYTE,
              extra_sigops=1)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        self.chain.set_tip(19)

        # Accept 40k sigops per block > 1MB and <= 2MB
        block(21,
              spend=out[18],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB,
              block_size=ONE_MEGABYTE + 1)
        yield self.accepted()

        # Accept 40k sigops per block > 1MB and <= 2MB
        block(22,
              spend=out[19],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB,
              block_size=2 * ONE_MEGABYTE)
        yield self.accepted()

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(23,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=ONE_MEGABYTE + 1)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        self.chain.set_tip(22)

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(24,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=2 * ONE_MEGABYTE)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        self.chain.set_tip(22)

        # Accept 60k sigops per block > 2MB and <= 3MB
        block(25,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB,
              block_size=2 * ONE_MEGABYTE + 1)
        yield self.accepted()

        # Accept 60k sigops per block > 2MB and <= 3MB
        block(26,
              spend=out[21],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB,
              block_size=3 * ONE_MEGABYTE)
        yield self.accepted()

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(27,
              spend=out[22],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=2 * ONE_MEGABYTE + 1)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        self.chain.set_tip(26)

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(28,
              spend=out[22],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=3 * ONE_MEGABYTE)
        yield self.rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        self.chain.set_tip(26)

        # Too many sigops in one txn
        too_many_tx_checksigs = CScript([OP_CHECKSIG] *
                                        (MAX_BLOCK_SIGOPS_PER_MB + 1))
        block(29,
              spend=out[22],
              script=too_many_tx_checksigs,
              block_size=ONE_MEGABYTE + 1)
        yield self.rejected(RejectResult(16, b'bad-txn-sigops'))

        # Rewind bad block
        self.chain.set_tip(26)

        # Generate a key pair to test P2SH sigops count
        private_key = CECKey()
        private_key.set_secretbytes(b"fatstacks")
        public_key = private_key.get_pubkey()

        # P2SH
        # Build the redeem script, hash it, use hash to create the p2sh script
        redeem_script = CScript([public_key] +
                                [OP_2DUP, OP_CHECKSIGVERIFY] * 5 +
                                [OP_CHECKSIG])
        redeem_script_hash = hash160(redeem_script)
        p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])

        # Create a p2sh transaction
        p2sh_tx = self.chain.create_tx_with_script(out[22], 1, p2sh_script)

        # Add the transaction to the block
        block(30)
        self.chain.update_block(30, [p2sh_tx])
        yield self.accepted()

        # Creates a new transaction using the p2sh transaction included in the
        # last block
        def spend_p2sh_tx(output_script=CScript([OP_TRUE])):
            # Create the transaction
            spent_p2sh_tx = CTransaction()
            spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b''))
            spent_p2sh_tx.vout.append(CTxOut(1, output_script))
            # Sign the transaction using the redeem script
            sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0,
                                          SIGHASH_ALL | SIGHASH_FORKID,
                                          p2sh_tx.vout[0].nValue)
            sig = private_key.sign(sighash) + \
                bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID]))
            spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script])
            spent_p2sh_tx.rehash()
            return spent_p2sh_tx

        # Sigops p2sh limit
        p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \
            redeem_script.GetSigOpCount(True)
        # Too many sigops in one p2sh txn
        too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1))
        block(31, spend=out[23], block_size=ONE_MEGABYTE + 1)
        self.chain.update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)])
        yield self.rejected(RejectResult(16, b'bad-txn-sigops'))

        # Rewind bad block
        self.chain.set_tip(30)

        # Max sigops in one p2sh txn
        max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit))
        block(32, spend=out[23], block_size=ONE_MEGABYTE + 1)
        self.chain.update_block(32, [spend_p2sh_tx(max_p2sh_sigops)])
        yield self.accepted()

        # Submit a very large block via RPC
        large_block = block(33,
                            spend=out[24],
                            block_size=self.excessive_block_size)
        node.submitblock(ToHex(large_block))
    def get_tests(self):

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # Test no excessiveblocksize parameter specified
        node = self.nodes[0]
        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(15):
            n = i + 1
            block(n, spend=out[i], block_size=n * ONE_MEGABYTE)
            yield accepted()

        # Start moving MTP forward
        bfork = block(5555, out[15], block_size=32 * ONE_MEGABYTE)
        bfork.nTime = MAGNETIC_START_TIME - 1
        update_block(5555, [])
        yield accepted()

        # Get to one block of the Nov 15, 2018 HF activation
        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])
        yield test

        # Check that the MTP is just before the configured fork point.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MAGNETIC_START_TIME - 1)

        # Before we acivate the Nov 15, 2018 HF, 32MB is the limit.
        block(4444, spend=out[16], block_size=32 * ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block.
        tip(5104)

        # Activate the Nov 15, 2018 HF
        block(5556)
        yield accepted()

        # Now MTP is exactly the fork time. Bigger blocks are now accepted.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MAGNETIC_START_TIME)

        # block of maximal size
        block(17, spend=out[16], block_size=128 * ONE_MEGABYTE)
        yield accepted()

        # Reject oversized blocks with bad-blk-length error
        block(18, spend=out[17], block_size=128 * ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))
    def get_tests(self):
        node = self.nodes[0]
        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions=[]):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(17):
            n = i + 1
            block(n)
            yield accepted()

        block(5556)
        yield accepted()

        # Block with regular ordering are now rejected.
        block(5557, out[17], tx_count=16)
        yield rejected(RejectResult(16, b'tx-ordering'))

        # Rewind bad block.
        tip(5556)

        # After we activate the Nov 15, 2018 HF, transaction order is enforced.
        def ordered_block(block_number, spend):
            b = block(block_number, spend=spend, tx_count=16)
            make_conform_to_ctor(b)
            update_block(block_number)
            return b

        # Now that the fork activated, we need to order transaction per txid.
        ordered_block(4445, out[17])
        yield accepted()

        ordered_block(4446, out[18])
        yield accepted()

        # Generate a block with a duplicated transaction.
        double_tx_block = ordered_block(4447, out[19])
        assert_equal(len(double_tx_block.vtx), 16)
        double_tx_block.vtx = double_tx_block.vtx[:8] + \
            [double_tx_block.vtx[8]] + double_tx_block.vtx[8:]
        update_block(4447)
        yield rejected(RejectResult(16, b'bad-txns-duplicate'))

        # Rewind bad block.
        tip(4446)

        # Check over two blocks.
        proper_block = ordered_block(4448, out[20])
        yield accepted()

        replay_tx_block = ordered_block(4449, out[21])
        assert_equal(len(replay_tx_block.vtx), 16)
        replay_tx_block.vtx.append(proper_block.vtx[5])
        replay_tx_block.vtx = [replay_tx_block.vtx[0]] + \
            sorted(replay_tx_block.vtx[1:], key=lambda tx: tx.get_id())
        update_block(4449)
        yield rejected(RejectResult(16, b'bad-txns-BIP30'))
Пример #27
0
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previous marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject = None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # add transactions to a block produced by next_block
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            old_hash = block.sha256
            self.add_transactions_to_block(block, new_transactions)
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            self.block_heights[block.sha256] = self.block_heights[old_hash]
            del self.block_heights[old_hash]
            self.blocks[block_number] = block
            return block

        # creates a new block and advances the tip to that block
        block = self.next_block


        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()


        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(1000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test


        # Start by building a couple of blocks on top (which output is spent is
        # in parentheses):
        #     genesis -> b1 (0) -> b2 (1)
        out0 = get_spendable_output()
        block(1, spend=out0)
        save_spendable_output()
        yield accepted()

        out1 = get_spendable_output()
        b2 = block(2, spend=out1)
        yield accepted()


        # so fork like this:
        #
        #     genesis -> b1 (0) -> b2 (1)
        #                      \-> b3 (1)
        #
        # Nothing should happen at this point. We saw b2 first so it takes priority.
        tip(1)
        b3 = block(3, spend=out1)
        txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1)
        yield rejected()


        # Now we add another block to make the alternative chain longer.
        #
        #     genesis -> b1 (0) -> b2 (1)
        #                      \-> b3 (1) -> b4 (2)
        out2 = get_spendable_output()
        block(4, spend=out2)
        yield accepted()


        # ... and back to the first chain.
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
        #                      \-> b3 (1) -> b4 (2)
        tip(2)
        block(5, spend=out2)
        save_spendable_output()
        yield rejected()

        out3 = get_spendable_output()
        block(6, spend=out3)
        yield accepted()


        # Try to create a fork that double-spends
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
        #                                          \-> b7 (2) -> b8 (4)
        #                      \-> b3 (1) -> b4 (2)
        tip(5)
        block(7, spend=out2)
        yield rejected()

        out4 = get_spendable_output()
        block(8, spend=out4)
        yield rejected()


        # Try to create a block that has too much fee
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
        #                                                    \-> b9 (4)
        #                      \-> b3 (1) -> b4 (2)
        tip(6)
        block(9, spend=out4, additional_coinbase_value=1)
        yield rejected(RejectResult(16, b'bad-cb-amount'))


        # Create a fork that ends in a block with too much fee (the one that causes the reorg)
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b10 (3) -> b11 (4)
        #                      \-> b3 (1) -> b4 (2)
        tip(5)
        block(10, spend=out3)
        yield rejected()

        block(11, spend=out4, additional_coinbase_value=1)
        yield rejected(RejectResult(16, b'bad-cb-amount'))


        # Try again, but with a valid fork first
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b14 (5)
        #                                              (b12 added last)
        #                      \-> b3 (1) -> b4 (2)
        tip(5)
        b12 = block(12, spend=out3)
        save_spendable_output()
        #yield TestInstance([[b12, False]])
        b13 = block(13, spend=out4)
        # Deliver the block header for b12, and the block b13.
        # b13 should be accepted but the tip won't advance until b12 is delivered.
        yield TestInstance([[CBlockHeader(b12), None], [b13, False]])

        save_spendable_output()
        out5 = get_spendable_output()
        # b14 is invalid, but the node won't know that until it tries to connect
        # Tip still can't advance because b12 is missing
        block(14, spend=out5, additional_coinbase_value=1)
        yield rejected()

        yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13.

        # Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
        #                      \-> b3 (1) -> b4 (2)

        # Test that a block with a lot of checksigs is okay
        lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50 - 1))
        tip(13)
        block(15, spend=out5, script=lots_of_checksigs)
        yield accepted()


        # Test that a block with too many checksigs is rejected
        out6 = get_spendable_output()
        too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50))
        block(16, spend=out6, script=too_many_checksigs)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))


        # Attempt to spend a transaction created on a different fork
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        block(17, spend=txout_b3)
        yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))

        # Attempt to spend a transaction created on a different fork (on a fork this time)
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5)
        #                                                                \-> b18 (b3.vtx[1]) -> b19 (6)
        #                      \-> b3 (1) -> b4 (2)
        tip(13)
        block(18, spend=txout_b3)
        yield rejected()

        block(19, spend=out6)
        yield rejected()

        # Attempt to spend a coinbase at depth too low
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        out7 = get_spendable_output()
        block(20, spend=out7)
        yield rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase'))

        # Attempt to spend a coinbase at depth too low (on a fork this time)
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5)
        #                                                                \-> b21 (6) -> b22 (5)
        #                      \-> b3 (1) -> b4 (2)
        tip(13)
        block(21, spend=out6)
        yield rejected()

        block(22, spend=out5)
        yield rejected()

        # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
        #                                                                           \-> b24 (6) -> b25 (7)
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        b23 = block(23, spend=out6)
        old_hash = b23.sha256
        tx = CTransaction()
        script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69
        script_output = CScript([b'\x00' * script_length])
        tx.vout.append(CTxOut(0, script_output))
        tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 1)))
        b23 = update_block(23, [tx])
        # Make sure the math above worked out to produce a max-sized block
        assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE)
        yield accepted()

        # Make the next block one byte bigger and check that it fails
        tip(15)
        b24 = block(24, spend=out6)
        script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69
        script_output = CScript([b'\x00' * (script_length+1)])
        tx.vout = [CTxOut(0, script_output)]
        b24 = update_block(24, [tx])
        assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        b25 = block(25, spend=out7)
        yield rejected()

        # Create blocks with a coinbase input script size out of range
        #     genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6  (3)
        #                                          \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
        #                                                                           \-> ... (6) -> ... (7)
        #                      \-> b3 (1) -> b4 (2)
        tip(15)
        b26 = block(26, spend=out6)
        b26.vtx[0].vin[0].scriptSig = b'\x00'
        b26.vtx[0].rehash()
        # update_block causes the merkle root to get updated, even with no new
        # transactions, and updates the required state.
        b26 = update_block(26, [])
        yield rejected(RejectResult(16, b'bad-cb-length'))

        # Extend the b26 chain to make sure bitcoind isn't accepting b26
        b27 = block(27, spend=out7)
        yield rejected()

        # Now try a too-large-coinbase script
        tip(15)
        b28 = block(28, spend=out6)
        b28.vtx[0].vin[0].scriptSig = b'\x00' * 101
        b28.vtx[0].rehash()
        b28 = update_block(28, [])
        yield rejected(RejectResult(16, b'bad-cb-length'))

        # Extend the b28 chain to make sure bitcoind isn't accepted b28
        b29 = block(29, spend=out7)
        # TODO: Should get a reject message back with "bad-prevblk", except
        # there's a bug that prevents this from being detected.  Just note
        # failure for now, and add the reject result later.
        yield rejected()

        # b30 has a max-sized coinbase scriptSig.
        tip(23)
        b30 = block(30)
        b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
        b30.vtx[0].rehash()
        b30 = update_block(30, [])
        yield accepted()
    def get_tests(self):
        node = self.nodes[0]
        self.genesis_hash = int(node.getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions=[]):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(15):
            n = i + 1
            block(n)
            yield accepted()

        # Start moving MTP forward
        bfork = block(5555)
        bfork.nTime = MAGNETIC_ANOMALY_START_TIME - 1
        update_block(5555)
        yield accepted()

        # Get to one block of the Nov 15, 2018 HF activation
        for i in range(5):
            block(5100 + i)
            test.blocks_and_transactions.append([self.tip, True])
        yield test

        # Check that the MTP is just before the configured fork point.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MAGNETIC_ANOMALY_START_TIME - 1)

        # Activate the Nov 15, 2018 HF
        block(5556, out[16], tx_count=16)
        yield accepted()

        # Now MTP is exactly the fork time. Transactions are expected to be ordered now.
        assert_equal(
            node.getblockheader(node.getbestblockhash())['mediantime'],
            MAGNETIC_ANOMALY_START_TIME)

        # Block with regular ordering are now rejected.
        block(5557, out[17], tx_count=16)
        yield rejected(RejectResult(16, b'tx-ordering'))

        # Rewind bad block.
        tip(5556)

        # After we activate the Nov 15, 2018 HF, transaction order is enforced.
        def ordered_block(block_number, spend):
            b = block(block_number, spend=spend, tx_count=16)
            b.vtx = [b.vtx[0]] + sorted(b.vtx[1:], key=lambda tx: tx.get_id())
            update_block(block_number)
            return b

        # Now that the fork activated, we need to order transaction per txid.
        ordered_block(4445, out[17])
        yield accepted()

        # Invalidate the best block and make sure we are back at the fork point.
        ctorblockhash = node.getbestblockhash()
        node.invalidateblock(ctorblockhash)
        forkblockhash = node.getbestblockhash()
        assert (forkblockhash != ctorblockhash)
        assert_equal(
            node.getblockheader(forkblockhash)['mediantime'],
            MAGNETIC_ANOMALY_START_TIME)

        assert_equal(len(node.getrawmempool()), 15)
        node.generate(1)
        generatedblockhash = node.getbestblockhash()
        assert (forkblockhash != generatedblockhash)

        # Reconstruct tip.
        tip_hash = node.getbestblockhash()
        self.tip = CBlock()
        self.tip.sha256 = int(tip_hash, 16)
        self.tip.nTime = timestamp = node.getblock(tip_hash)['time']
        self.block_heights[self.tip.sha256] = node.getblock(tip_hash)['height']

        ordered_block(4446, out[18])
        yield accepted()

        # Generate a block with a duplicated transaction.
        double_tx_block = ordered_block(4447, out[19])
        assert_equal(len(double_tx_block.vtx), 16)
        double_tx_block.vtx = double_tx_block.vtx[:8] + \
            [double_tx_block.vtx[8]] + double_tx_block.vtx[8:]
        update_block(4447)
        yield rejected(RejectResult(16, b'bad-txns-duplicate'))

        # Rewind bad block.
        tip(4446)

        # Check over two blocks.
        proper_block = ordered_block(4448, out[20])
        yield accepted()

        replay_tx_block = ordered_block(4449, out[21])
        assert_equal(len(replay_tx_block.vtx), 16)
        replay_tx_block.vtx.append(proper_block.vtx[5])
        replay_tx_block.vtx = [replay_tx_block.vtx[0]] + \
            sorted(replay_tx_block.vtx[1:], key=lambda tx: tx.get_id())
        update_block(4449)
        yield rejected(RejectResult(16, b'bad-txns-BIP30'))
Пример #29
0
    def get_tests(self):
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # returns a test case that asserts that the current tip was accepted
        def accepted():
            return TestInstance([[self.tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(reject=None):
            if reject is None:
                return TestInstance([[self.tip, False]])
            else:
                return TestInstance([[self.tip, reject]])

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            self.add_transactions_to_block(block, new_transactions)
            old_sha256 = block.sha256
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # shorthand for functions
        block = self.next_block

        # Create a new block
        block(0)
        save_spendable_output()
        yield accepted()

        # Now we need that block to mature so we can spend the coinbase.
        test = TestInstance(sync_every_block=False)
        for i in range(99):
            block(5000 + i)
            test.blocks_and_transactions.append([self.tip, True])
            save_spendable_output()
        yield test

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Let's build some blocks and test them.
        for i in range(16):
            n = i + 1
            block(n, spend=out[i], block_size=n * ONE_MEGABYTE)
            yield accepted()

        # block of maximal size
        block(17, spend=out[16], block_size=self.excessive_block_size)
        yield accepted()

        # Reject oversized blocks with bad-blk-length error
        block(18, spend=out[17], block_size=self.excessive_block_size + 1)
        yield rejected(RejectResult(16, b'bad-blk-length'))

        # Rewind bad block.
        tip(17)

        # Accept many sigops
        lots_of_checksigs = CScript([OP_CHECKSIG] *
                                    (MAX_BLOCK_SIGOPS_PER_MB - 1))
        block(19,
              spend=out[17],
              script=lots_of_checksigs,
              block_size=ONE_MEGABYTE)
        yield accepted()

        too_many_blk_checksigs = CScript([OP_CHECKSIG] *
                                         MAX_BLOCK_SIGOPS_PER_MB)
        block(20,
              spend=out[18],
              script=too_many_blk_checksigs,
              block_size=ONE_MEGABYTE)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(19)

        # Accept 40k sigops per block > 1MB and <= 2MB
        block(21,
              spend=out[18],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB,
              block_size=ONE_MEGABYTE + 1)
        yield accepted()

        # Accept 40k sigops per block > 1MB and <= 2MB
        block(22,
              spend=out[19],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB,
              block_size=2 * ONE_MEGABYTE)
        yield accepted()

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(23,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(22)

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(24,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=2 * ONE_MEGABYTE)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(22)

        # Accept 60k sigops per block > 2MB and <= 3MB
        block(25,
              spend=out[20],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB,
              block_size=2 * ONE_MEGABYTE + 1)
        yield accepted()

        # Accept 60k sigops per block > 2MB and <= 3MB
        block(26,
              spend=out[21],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB,
              block_size=3 * ONE_MEGABYTE)
        yield accepted()

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(27,
              spend=out[22],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=2 * ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(26)

        # Reject more than 40k sigops per block > 1MB and <= 2MB.
        block(28,
              spend=out[22],
              script=lots_of_checksigs,
              extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1,
              block_size=3 * ONE_MEGABYTE)
        yield rejected(RejectResult(16, b'bad-blk-sigops'))

        # Rewind bad block
        tip(26)

        # Too many sigops in one txn
        too_many_tx_checksigs = CScript([OP_CHECKSIG] *
                                        (MAX_BLOCK_SIGOPS_PER_MB + 1))
        block(29,
              spend=out[22],
              script=too_many_tx_checksigs,
              block_size=ONE_MEGABYTE + 1)
        yield rejected(RejectResult(16, b'bad-txn-sigops'))

        # Rewind bad block
        tip(26)

        # P2SH
        # Build the redeem script, hash it, use hash to create the p2sh script
        redeem_script = CScript([self.coinbase_pubkey] +
                                [OP_2DUP, OP_CHECKSIGVERIFY] * 5 +
                                [OP_CHECKSIG])
        redeem_script_hash = hash160(redeem_script)
        p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])

        # Create a p2sh transaction
        p2sh_tx = self.create_and_sign_transaction(out[22].tx, out[22].n, 1,
                                                   p2sh_script)

        # Add the transaction to the block
        block(30)
        update_block(30, [p2sh_tx])
        yield accepted()

        # Creates a new transaction using the p2sh transaction included in the
        # last block
        def spend_p2sh_tx(output_script=CScript([OP_TRUE])):
            # Create the transaction
            spent_p2sh_tx = CTransaction()
            spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b''))
            spent_p2sh_tx.vout.append(CTxOut(1, output_script))
            # Sign the transaction using the redeem script
            sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0,
                                          SIGHASH_ALL | SIGHASH_FORKID,
                                          p2sh_tx.vout[0].nValue)
            sig = self.coinbase_key.sign(sighash) + bytes(
                bytearray([SIGHASH_ALL | SIGHASH_FORKID]))
            spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script])
            spent_p2sh_tx.rehash()
            return spent_p2sh_tx

        # Sigops p2sh limit
        p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \
            redeem_script.GetSigOpCount(True)
        # Too many sigops in one p2sh txn
        too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1))
        block(31, spend=out[23], block_size=ONE_MEGABYTE + 1)
        update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)])
        yield rejected(RejectResult(16, b'bad-txn-sigops'))

        # Rewind bad block
        tip(30)

        # Max sigops in one p2sh txn
        max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit))
        block(32, spend=out[23], block_size=ONE_MEGABYTE + 1)
        update_block(32, [spend_p2sh_tx(max_p2sh_sigops)])
        yield accepted()

        # Check that compact block also work for big blocks
        node = self.nodes[0]
        peer = TestNode()
        peer.add_connection(NodeConn('127.0.0.1', p2p_port(0), node, peer))

        # Start up network handling in another thread and wait for connection
        # to be etablished
        NetworkThread().start()
        peer.wait_for_verack()

        # Wait for SENDCMPCT
        def received_sendcmpct():
            return (peer.last_sendcmpct != None)

        got_sendcmpt = wait_until(received_sendcmpct, timeout=30)
        assert (got_sendcmpt)

        sendcmpct = msg_sendcmpct()
        sendcmpct.version = 1
        sendcmpct.announce = True
        peer.send_and_ping(sendcmpct)

        # Exchange headers
        def received_getheaders():
            return (peer.last_getheaders != None)

        got_getheaders = wait_until(received_getheaders, timeout=30)
        assert (got_getheaders)

        # Return the favor
        peer.send_message(peer.last_getheaders)

        # Wait for the header list
        def received_headers():
            return (peer.last_headers != None)

        got_headers = wait_until(received_headers, timeout=30)
        assert (got_headers)

        # It's like we know about the same headers !
        peer.send_message(peer.last_headers)

        # Send a block
        b33 = block(33, spend=out[24], block_size=ONE_MEGABYTE + 1)
        yield accepted()

        # Checks the node to forward it via compact block
        def received_block():
            return (peer.last_cmpctblock != None)

        got_cmpctblock = wait_until(received_block, timeout=30)
        assert (got_cmpctblock)

        # Was it our block ?
        cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header
        cmpctblk_header.calc_sha256()
        assert (cmpctblk_header.sha256 == b33.sha256)

        # Send a bigger block
        peer.clear_block_data()
        b34 = block(34, spend=out[25], block_size=8 * ONE_MEGABYTE)
        yield accepted()

        # Checks the node to forward it via compact block
        got_cmpctblock = wait_until(received_block, timeout=30)
        assert (got_cmpctblock)

        # Was it our block ?
        cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header
        cmpctblk_header.calc_sha256()
        assert (cmpctblk_header.sha256 == b34.sha256)

        # Let's send a compact block and see if the node accepts it.
        # First, we generate the block and send all transaction to the mempool
        b35 = block(35, spend=out[26], block_size=8 * ONE_MEGABYTE)
        for i in range(1, len(b35.vtx)):
            node.sendrawtransaction(ToHex(b35.vtx[i]), True)

        # Now we create the compact block and send it
        comp_block = HeaderAndShortIDs()
        comp_block.initialize_from_block(b35)
        peer.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))

        # Check that compact block is received properly
        assert (int(node.getbestblockhash(), 16) == b35.sha256)
Пример #30
0
    def get_tests(self):
        node = self.nodes[0]

        # First, we generate some coins to spend.
        node.generate(125)

        # Create various outputs using the OP_CHECKDATASIG
        # to check for activation.
        tx_hex = self.create_checkdatasig_tx(25)
        txid = node.sendrawtransaction(tx_hex)
        assert (txid in set(node.getrawmempool()))

        node.generate(1)
        assert (txid not in set(node.getrawmempool()))

        # register the spendable outputs.
        tx = FromHex(CTransaction(), tx_hex)
        tx.rehash()
        spendable_checkdatasigs = [
            PreviousSpendableOutput(tx, i) for i in range(len(tx.vout))
        ]

        def spend_checkdatasig():
            outpoint = spendable_checkdatasigs.pop()
            out = outpoint.tx.vout[outpoint.n]
            tx = CTransaction()
            tx.vin = [CTxIn(COutPoint(outpoint.tx.sha256, outpoint.n))]
            tx.vout = [
                CTxOut(out.nValue, CScript([])),
                CTxOut(0, CScript([random.getrandbits(800), OP_RETURN]))
            ]
            tx.vout[0].nValue -= get_relay_fee(node, unit="sat")
            tx.rehash()
            return tx

        # Check that transactions using checkdatasig are not accepted yet.
        self.log.info("Try to use the checkdatasig opcodes before activation")

        tx0 = spend_checkdatasig()
        tx0_hex = ToHex(tx0)
        assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR,
                                node.sendrawtransaction, tx0_hex)
        assert_equal(
            get_bip135_status(node, 'bip135test0')['status'], 'started')

        # CDSV regtest start happened at height 99, activation should be at height 299
        self.log.info(
            "Advance to height 298, just before activation, and check again")
        node.generate(172)

        # returns a test case that asserts that the current tip was accepted
        def accepted(tip):
            return TestInstance([[tip, True]])

        # returns a test case that asserts that the current tip was rejected
        def rejected(tip, reject=None):
            if reject is None:
                return TestInstance([[tip, False]])
            else:
                return TestInstance([[tip, reject]])

        def next_block():
            # get block height
            tiphash = node.getbestblockhash()
            tipheader = node.getblockheader(tiphash)
            height = int(tipheader['height'])

            # create the block
            coinbase = create_coinbase(absoluteHeight=height + 1)
            coinbase.rehash()
            version = 0x20000001  # Signal for CDSV on bit 0
            block = create_block(int(tiphash, 16),
                                 coinbase,
                                 int(tipheader['time']) + 600,
                                 nVersion=version)

            # Do PoW, which is cheap on regnet
            block.solve()
            return block

        assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR,
                                node.sendrawtransaction, tx0_hex)

        def add_tx(block, tx):
            block.vtx.append(tx)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()

        b = next_block()
        add_tx(b, tx0)
        yield rejected(b, RejectResult(16, b'blk-bad-inputs'))  #1

        assert_equal(
            get_bip135_status(node, 'bip135test0')['status'], 'locked_in')

        self.log.info("Activates checkdatasig")
        fork_block = next_block()
        yield accepted(fork_block)  #2

        assert_equal(
            get_bip135_status(node, 'bip135test0')['status'], 'active')

        tx0id = node.sendrawtransaction(tx0_hex)
        assert (tx0id in set(node.getrawmempool()))

        # Transactions can also be included in blocks.
        nextblock = next_block()
        add_tx(nextblock, tx0)
        yield accepted(nextblock)  #3

        self.log.info("Cause a reorg that deactivate the checkdatasig opcodes")

        # Invalidate the checkdatasig block, ensure tx0 gets back to the mempool.
        assert (tx0id not in set(node.getrawmempool()))

        node.invalidateblock(format(nextblock.sha256, 'x'))
        assert (tx0id in set(node.getrawmempool()))

        node.invalidateblock(format(fork_block.sha256, 'x'))
        assert (tx0id not in set(node.getrawmempool()))