Example #1
0
    def test_null_locators(self, test_node, inv_node):
        tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
        tip_hash = int(tip["hash"], 16)

        inv_node.check_last_inv_announcement(inv=[tip_hash])
        test_node.check_last_inv_announcement(inv=[tip_hash])

        self.log.info(
            "Verify getheaders with null locator and valid hashstop returns headers."
        )
        test_node.clear_block_announcements()
        test_node.send_get_headers(locator=[], hashstop=tip_hash)
        test_node.check_last_headers_announcement(headers=[tip_hash])

        self.log.info(
            "Verify getheaders with null locator and invalid hashstop does not return headers."
        )
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        block = create_block(
            int(tip["hash"], 16),
            self.create_coinbase(tip["height"] + 1, snapshot_hash),
            tip["mediantime"] + 1)
        block.solve()
        test_node.send_header_for_blocks([block])
        test_node.clear_block_announcements()
        test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16))
        test_node.sync_with_ping()
        assert_equal(test_node.block_announced, False)
        inv_node.clear_block_announcements()
        test_node.send_message(msg_block(block))
        inv_node.check_last_inv_announcement(inv=[int(block.hash, 16)])
Example #2
0
    def send_block(self, node, txs, accept=False):
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        block = create_block(
            self.tip,
            create_coinbase(self.lastblockheight + 1, coin, snapshot_hash),
            self.lastblocktime + 1)
        block.vtx[0] = sign_coinbase(self.nodes[0], block.vtx[0])
        block.nVersion = 4
        for tx in txs:
            tx.rehash()
            block.vtx.append(tx)

        block.ensure_ltor()
        block.compute_merkle_trees()
        block.solve()

        node.p2p.send_and_ping(msg_block(block))

        if (accept):
            assert_equal(node.getbestblockhash(), block.hash)
            self.tip = block.sha256
            self.lastblockhash = block.hash
            self.lastblocktime += 1
            self.lastblockheight += 1
        else:
            assert_equal(node.getbestblockhash(), self.lastblockhash)
Example #3
0
    def test_bip68_not_consensus(self):
        assert get_bip9_status(self.nodes[0], 'csv')['status'] != 'active'
        txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)

        tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
        tx1.rehash()

        # Make an anyone-can-spend transaction
        tx2 = CTransaction()
        tx2.nVersion = 1
        tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
        tx2.vout = [
            CTxOut(int(tx1.vout[0].nValue - self.relayfee * UNIT),
                   CScript([b'a']))
        ]

        # sign tx2
        tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"]
        tx2 = FromHex(tx2, tx2_raw)
        tx2.rehash()
        self.nodes[0].sendrawtransaction(ToHex(tx2))

        # Now make an invalid (non-final) spend of tx2 according to BIP68
        non_final_sequence_value = 100

        tx3 = CTransaction()
        tx3.nVersion = 2
        tx3.vin = [
            CTxIn(COutPoint(tx2.sha256, 0), nSequence=non_final_sequence_value)
        ]
        tx3.vout = [
            CTxOut(int(tx2.vout[0].nValue - self.relayfee * UNIT),
                   CScript([b'a' * 35]))
        ]
        tx3.rehash()

        # Make sure the transaction will not be accepted into mempool
        assert_raises_rpc_error(-26, NOT_FINAL_ERROR,
                                self.nodes[0].sendrawtransaction, ToHex(tx3))

        # Make a block that violates bip68; ensure that the tip updates
        tip = int(self.nodes[0].getbestblockhash(), 16)
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash

        coinbase = create_coinbase(self.nodes[0].getblockcount() + 1,
                                   self.nodes[0].listunspent()[0],
                                   snapshot_hash)
        coinbase = sign_transaction(self.nodes[0], coinbase)
        coinbase.rehash()

        block = create_block(tip, coinbase)
        block.nVersion = 3
        block.vtx.extend([tx1, tx2, tx3])
        block.ensure_ltor()
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)
Example #4
0
 def solve_and_send_block(prevhash, height, time):
     snapshot_hash = get_tip_snapshot_meta(node).hash
     stake = get_unspent_coins(node, 1)[0]
     b = create_block(
         prevhash,
         sign_coinbase(node,
                       create_coinbase(height, stake, snapshot_hash)),
         time)
     b.solve()
     node.p2p.send_message(msg_block(b))
     node.p2p.sync_with_ping()
     return b
Example #5
0
def build_block_on_tip(node, staking_coin):
    tip = node.getblockheader(node.getbestblockhash())

    height = tip['height']
    snapshot_meta = get_tip_snapshot_meta(node)

    coinbase = create_coinbase(height + 1, staking_coin, snapshot_meta.hash)
    coinbase = sign_transaction(node, coinbase)
    coinbase.rehash()
    block = create_block(int(tip['hash'], 16), coinbase, tip["mediantime"] + 1)
    block.solve()
    return block
 def create_block(self):
     node = self.nodes[0]
     stake = node.listunspent()[0]
     tip_hash = node.getblockchaininfo()['bestblockhash']
     tip_header = node.getblockheader(tip_hash)
     snapshot_hash = get_tip_snapshot_meta(node).hash
     coinbase = create_coinbase(tip_header["height"] + 1, stake,
                                snapshot_hash)
     coinbase = sign_coinbase(self.nodes[0], coinbase)
     block = create_block(int(tip_hash, 16), coinbase,
                          tip_header["mediantime"] + 1)
     block.solve()
     block.rehash()
     return block
    def send_blocks_with_version(self, peer, numblocks, version):
        """Send numblocks blocks to peer with version set"""
        tip = self.nodes[0].getbestblockhash()
        height = self.nodes[0].getblockcount()
        block_time = self.nodes[0].getblockheader(tip)["time"] + 1
        tip = int(tip, 16)

        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        for _ in range(numblocks):
            stake = self.nodes[0].listunspent()[0]
            coinbase = create_coinbase(height + 1, stake, snapshot_meta.hash)
            coinbase = sign_coinbase(self.nodes[0], coinbase)
            block = create_block(tip, coinbase, block_time)
            block.nVersion = version
            block.solve()
            peer.send_message(msg_block(block))
            self.nodes[0].waitforblockheight(height+1, 10000)
            block_time += 1
            height += 1
            tip = block.sha256

            snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        peer.sync_with_ping()
Example #8
0
        def build_block_with_immature_stake(node):
            height = node.getblockcount()
            stakes = node.listunspent()
            # Take the latest, immature stake
            stake = min(stakes, key=lambda x: x['confirmations'])
            snapshot_meta = get_tip_snapshot_meta(node)
            coinbase = sign_coinbase(
                node, create_coinbase(height, stake, snapshot_meta.hash))

            tip = int(node.getbestblockhash(), 16)
            block_time = node.getblock(
                self.nodes[0].getbestblockhash())['time'] + 1
            block = create_block(tip, coinbase, block_time)

            return block
Example #9
0
def submit_block_with_tx(node, tx):
    ctx = CTransaction()
    ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))

    tip = node.getbestblockhash()
    height = node.getblockcount() + 1
    block_time = node.getblockheader(tip)["mediantime"] + 1
    snapshot_hash = get_tip_snapshot_meta(node).hash
    stake = node.listunspent()[0]
    block = create_block(int(tip, 16), sign_coinbase(node, create_coinbase(height, stake, snapshot_hash)), block_time)
    block.vtx.append(ctx)
    block.rehash()
    block.compute_merkle_trees()
    block.solve()
    node.p2p.send_and_ping(msg_block(block))
    assert_equal(node.getbestblockhash(), block.hash)
    return block
Example #10
0
    def build_block_on_tip(self, node, txs=[]):
        height = node.getblockcount()
        tip = node.getbestblockhash()
        mtp = node.getblockheader(tip)['mediantime']

        meta = get_tip_snapshot_meta(node)
        coin = get_unspent_coins(node, 1)[0]
        block = create_block(
            int(tip, 16),
            sign_coinbase(node, create_coinbase(height + 1, coin, meta.hash)),
            mtp + 1)
        block.nVersion = 4
        if txs:
            block.vtx.extend(txs)
            block.ensure_ltor()

        block.compute_merkle_trees()
        block.solve()

        return block
Example #11
0
def build_block_with_remote_stake(node):
    height = node.getblockcount()
    snapshot_meta = get_tip_snapshot_meta(node)
    stakes = node.liststakeablecoins()

    coin = stakes['stakeable_coins'][0]['coin']
    script_pubkey = hex_str_to_bytes(coin['script_pub_key']['hex'])
    stake = {
        'txid': coin['out_point']['txid'],
        'vout': coin['out_point']['n'],
        'amount': coin['amount'],
    }

    tip = int(node.getbestblockhash(), 16)
    block_time = node.getblock(
        node.getbestblockhash())['time'] + 1
    coinbase = sign_coinbase(
        node, create_coinbase(
            height, stake, snapshot_meta.hash, raw_script_pubkey=script_pubkey))

    return create_block(tip, coinbase, block_time)
Example #12
0
    def get_empty_block(self, sync_height):
        sync_blocks(self.nodes, height=sync_height)
        node0 = self.nodes[0]

        hashprev = uint256_from_str(unhexlify(node0.getbestblockhash())[::-1])
        snapshot_hash = get_tip_snapshot_meta(node0).hash

        if len(self.spendable_outputs) > 0:
            block_time = self.spendable_outputs[-1].nTime + 1
        else:
            block_time = int(time_time()) + 2

        block = create_block(hashprev=hashprev,
                             coinbase=sign_coinbase(
                                 self.nodes[0],
                                 create_coinbase(height=sync_height + 1,
                                                 stake=node0.listunspent()[0],
                                                 snapshot_hash=snapshot_hash)),
                             nTime=block_time)
        block.solve()

        return block
Example #13
0
    def run_test(self):
        """Main test logic"""

        # Create P2P connections to two of the nodes
        self.nodes[0].add_p2p_connection(BaseNode())

        # Start up network handling in another thread. This needs to be called
        # after the P2P connections have been created.
        network_thread_start()
        # wait_for_verack ensures that the P2P connection is fully up.
        self.nodes[0].p2p.wait_for_verack()

        self.setup_stake_coins(self.nodes[0])

        # Generating a block on one of the nodes will get us out of IBD
        blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
        self.sync_all([self.nodes[0:1]])

        # Notice above how we called an RPC by calling a method with the same
        # name on the node object. Notice also how we used a keyword argument
        # to specify a named RPC argument. Neither of those are defined on the
        # node object. Instead there's some __getattr__() magic going on under
        # the covers to dispatch unrecognised attribute calls to the RPC
        # interface.

        # Logs are nice. Do plenty of them. They can be used in place of comments for
        # breaking the test into sub-sections.
        self.log.info("Starting test!")

        self.log.info("Calling a custom function")
        custom_function()

        self.log.info("Calling a custom method")
        self.custom_method()

        self.log.info("Create some blocks")
        self.tip = int(self.nodes[0].getbestblockhash(), 16)
        self.block_time = self.nodes[0].getblock(
            self.nodes[0].getbestblockhash())['time'] + 1

        height = self.nodes[0].getblockcount()

        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        stakes = self.nodes[0].listunspent()
        for stake in stakes:
            # Use the mininode and blocktools functionality to manually build a block
            # Calling the generate() rpc is easier, but this allows us to exactly
            # control the blocks and transactions.
            coinbase = sign_coinbase(
                self.nodes[0],
                create_coinbase(height, stake, snapshot_meta.hash))
            block = create_block(self.tip, coinbase, self.block_time)
            # Wait until the active chain picks up the previous block
            wait_until(lambda: self.nodes[0].getblockcount() == height,
                       timeout=5)
            snapshot_meta = update_snapshot_with_tx(self.nodes[0],
                                                    snapshot_meta, height + 1,
                                                    coinbase)
            block.solve()
            block_message = msg_block(block)
            # Send message is used to send a P2P message to the node over our P2PInterface
            self.nodes[0].p2p.send_message(block_message)
            self.tip = block.sha256
            blocks.append(self.tip)
            self.block_time += 1
            height += 1

        self.log.info(
            "Wait for node1 to reach current tip (height %d) using RPC" %
            height)
        self.nodes[1].waitforblockheight(height)

        self.log.info("Connect node2 and node1")
        connect_nodes(self.nodes[1], 2)

        self.log.info("Add P2P connection to node2")
        # We can't add additional P2P connections once the network thread has started. Disconnect the connection
        # to node0, wait for the network thread to terminate, then connect to node2. This is specific to
        # the current implementation of the network thread and may be improved in future.
        self.nodes[0].disconnect_p2ps()
        network_thread_join()

        self.nodes[2].add_p2p_connection(BaseNode())
        network_thread_start()
        self.nodes[2].p2p.wait_for_verack()

        self.log.info(
            "Wait for node2 reach current tip. Test that it has propagated all the blocks to us"
        )

        getdata_request = msg_getdata()
        for block in blocks:
            getdata_request.inv.append(CInv(2, block))
        self.nodes[2].p2p.send_message(getdata_request)

        # wait_until() will loop until a predicate condition is met. Use it to test properties of the
        # P2PInterface objects.
        wait_until(lambda: sorted(blocks) == sorted(
            list(self.nodes[2].p2p.block_receive_map.keys())),
                   timeout=5,
                   lock=mininode_lock)

        self.log.info("Check that each block was received only once")
        # The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
        # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
        # and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
        with mininode_lock:
            for block in self.nodes[2].p2p.block_receive_map.values():
                assert_equal(block, 1)
Example #14
0
    def test_sequence_lock_unconfirmed_inputs(self):
        # Store height so we can easily reset the chain at the end of the test
        cur_height = self.nodes[0].getblockcount()

        # Create a mempool tx.
        txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
        tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
        tx1.rehash()

        # Anyone-can-spend mempool tx.
        # Sequence lock of 0 should pass.
        tx2 = CTransaction()
        tx2.nVersion = 2
        tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
        tx2.vout = [
            CTxOut(int(tx1.vout[0].nValue - self.relayfee * UNIT),
                   CScript([b'a']))
        ]
        tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"]
        tx2 = FromHex(tx2, tx2_raw)
        tx2.rehash()

        self.nodes[0].sendrawtransaction(tx2_raw)

        # Create a spend of the 0th output of orig_tx with a sequence lock
        # of 1, and test what happens when submitting.
        # orig_tx.vout[0] must be an anyone-can-spend output
        def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
            sequence_value = 1
            if not use_height_lock:
                sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG

            tx = CTransaction()
            tx.nVersion = 2
            tx.vin = [
                CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)
            ]
            tx.vout = [
                CTxOut(int(orig_tx.vout[0].nValue - relayfee * UNIT),
                       CScript([b'a' * 35]))
            ]
            tx.rehash()

            if (orig_tx.hash in node.getrawmempool()):
                # sendrawtransaction should fail if the tx is in the mempool
                assert_raises_rpc_error(-26, NOT_FINAL_ERROR,
                                        node.sendrawtransaction, ToHex(tx))
            else:
                # sendrawtransaction should succeed if the tx is not in the mempool
                node.sendrawtransaction(ToHex(tx))

            return tx

        test_nonzero_locks(tx2,
                           self.nodes[0],
                           self.relayfee,
                           use_height_lock=True)
        test_nonzero_locks(tx2,
                           self.nodes[0],
                           self.relayfee,
                           use_height_lock=False)

        # Now mine some blocks, but make sure tx2 doesn't get mined.
        # Use prioritisetransaction to lower the effective feerate to 0
        self.nodes[0].prioritisetransaction(txid=tx2.hash,
                                            fee_delta=int(-self.relayfee *
                                                          UNIT))
        cur_time = int(time.time())
        for i in range(10):
            self.nodes[0].setmocktime(cur_time + 600)
            self.nodes[0].generate(1)
            cur_time += 600

        assert tx2.hash in self.nodes[0].getrawmempool()
        tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        test_nonzero_locks(tx2,
                           self.nodes[0],
                           self.relayfee,
                           use_height_lock=True)
        test_nonzero_locks(tx2,
                           self.nodes[0],
                           self.relayfee,
                           use_height_lock=False)

        # Mine tx2, and then try again
        self.nodes[0].prioritisetransaction(txid=tx2.hash,
                                            fee_delta=int(self.relayfee *
                                                          UNIT))

        # Advance the time on the node so that we can test timelocks
        self.nodes[0].setmocktime(cur_time + 600)
        self.nodes[0].generate(1)
        assert tx2.hash not in self.nodes[0].getrawmempool()

        # Now that tx2 is not in the mempool, a sequence locked spend should
        # succeed
        tx3 = test_nonzero_locks(tx2,
                                 self.nodes[0],
                                 self.relayfee,
                                 use_height_lock=False)
        assert tx3.hash in self.nodes[0].getrawmempool()

        self.nodes[0].generate(1)
        assert tx3.hash not in self.nodes[0].getrawmempool()

        # One more test, this time using height locks
        tx4 = test_nonzero_locks(tx3,
                                 self.nodes[0],
                                 self.relayfee,
                                 use_height_lock=True)
        assert tx4.hash in self.nodes[0].getrawmempool()

        # Now try combining confirmed and unconfirmed inputs
        tx5 = test_nonzero_locks(tx4,
                                 self.nodes[0],
                                 self.relayfee,
                                 use_height_lock=True)
        assert tx5.hash not in self.nodes[0].getrawmempool()

        utxos = self.nodes[0].listunspent()
        tx5.vin.append(
            CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]),
                  nSequence=1))
        tx5.vout[0].nValue += int(utxos[0]["amount"] * UNIT)
        raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"]

        assert_raises_rpc_error(-26, NOT_FINAL_ERROR,
                                self.nodes[0].sendrawtransaction, raw_tx5)

        # Test mempool-BIP68 consistency after reorg
        #
        # State of the transactions in the last blocks:
        # ... -> [ tx2 ] ->  [ tx3 ]
        #         tip-1        tip
        # And currently tx4 is in the mempool.
        #
        # If we invalidate the tip, tx3 should get added to the mempool, causing
        # tx4 to be removed (fails sequence-lock).
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        assert tx4.hash not in self.nodes[0].getrawmempool()
        assert tx3.hash in self.nodes[0].getrawmempool()

        # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in
        # diagram above).
        # This would cause tx2 to be added back to the mempool, which in turn causes
        # tx3 to be removed.
        tip = int(
            self.nodes[0].getblockhash(self.nodes[0].getblockcount() - 1), 16)
        height = self.nodes[0].getblockcount()
        # Let's get the available stake that is not already used
        # We must exclude tx2 outputs from the list since any stake referred to them will fail
        # In order to do that, we limit outputs with the number of minimum confirmations (minconf = 2)
        avail_stake = [
            x for x in self.nodes[0].listunspent(2) if x['txid'] != tx1.hash
        ]
        for i in range(2):
            stake = avail_stake.pop()
            coinbase = sign_coinbase(
                self.nodes[0],
                create_coinbase(height, stake, tip_snapshot_meta.hash))
            block = create_block(tip, coinbase, cur_time)
            block.nVersion = 3
            block.solve()
            tip = block.sha256

            tip_snapshot_meta = update_snapshot_with_tx(
                self.nodes[0], tip_snapshot_meta, height, coinbase)

            height += 1
            self.nodes[0].p2p.send_and_ping(msg_block(block))
            cur_time += 1

        # sync as the reorg is happening
        self.nodes[0].p2p.sync_with_ping()
        mempool = self.nodes[0].getrawmempool()
        assert tx3.hash not in mempool
        assert tx2.hash in mempool

        # Reset the chain and get rid of the mocktimed-blocks
        self.nodes[0].setmocktime(0)
        self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height +
                                                                 1))
        self.nodes[0].generate(10)
Example #15
0
    def test_nonnull_locators(self, test_node, inv_node):
        tip = int(self.nodes[0].getbestblockhash(), 16)

        # PART 1
        # 1. Mine a block; expect inv announcements each time
        self.log.info(
            "Part 1: headers don't start before sendheaders message...")
        for i in range(4):
            self.log.debug("Part 1.{}: starting...".format(i))
            old_tip = tip
            tip = self.mine_blocks(1)
            inv_node.check_last_inv_announcement(inv=[tip])
            test_node.check_last_inv_announcement(inv=[tip])
            # Try a few different responses; none should affect next announcement
            if i == 0:
                # first request the block
                test_node.send_get_data([tip])
                test_node.wait_for_block(tip)
            elif i == 1:
                # next try requesting header and block
                test_node.send_get_headers(locator=[old_tip], hashstop=tip)
                test_node.send_get_data([tip])
                test_node.wait_for_block(tip)
                test_node.clear_block_announcements(
                )  # since we requested headers...
            elif i == 2:
                # this time announce own block via headers
                inv_node.clear_block_announcements()
                height = self.nodes[0].getblockcount()
                last_time = self.nodes[0].getblock(
                    self.nodes[0].getbestblockhash())['time']
                block_time = last_time + 1
                snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
                new_block = create_block(
                    tip, self.create_coinbase(height + 1, snapshot_hash),
                    block_time)
                new_block.solve()
                test_node.send_header_for_blocks([new_block])
                test_node.wait_for_getdata([new_block.sha256])
                test_node.send_message(msg_block(new_block))
                test_node.sync_with_ping()  # make sure this block is processed
                wait_until(lambda: inv_node.block_announced,
                           timeout=60,
                           lock=mininode_lock)
                inv_node.clear_block_announcements()
                test_node.clear_block_announcements()

        self.log.info("Part 1: success!")
        self.log.info(
            "Part 2: announce blocks with headers after sendheaders message..."
        )
        # PART 2
        # 2. Send a sendheaders message and test that headers announcements
        # commence and keep working.
        test_node.send_message(msg_sendheaders())
        prev_tip = int(self.nodes[0].getbestblockhash(), 16)
        test_node.send_get_headers(locator=[prev_tip], hashstop=0)
        test_node.sync_with_ping()

        # Now that we've synced headers, headers announcements should work
        tip = self.mine_blocks(1)
        inv_node.check_last_inv_announcement(inv=[tip])
        test_node.check_last_headers_announcement(headers=[tip])

        height = self.nodes[0].getblockcount() + 1
        block_time += 10  # Advance far enough ahead
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        for i in range(10):
            self.log.debug("Part 2.{}: starting...".format(i))
            # Mine i blocks, and alternate announcing either via
            # inv (of tip) or via headers. After each, new blocks
            # mined by the node should successfully be announced
            # with block header, even though the blocks are never requested
            for j in range(2):
                self.log.debug("Part 2.{}.{}: starting...".format(i, j))
                blocks = []

                coins = get_unspent_coins(self.nodes[0], i + 1)

                for b in range(i + 1):
                    coinbase = self.create_coinbase(height, snapshot_meta.hash,
                                                    coins[b])
                    blocks.append(create_block(tip, coinbase, block_time))
                    blocks[-1].solve()
                    tip = blocks[-1].sha256

                    snapshot_meta = update_snapshot_with_tx(
                        self.nodes[0], snapshot_meta, height, coinbase)

                    block_time += 1
                    height += 1

                if j == 0:
                    # Announce via inv
                    test_node.send_block_inv(tip)
                    test_node.wait_for_getheaders()
                    # Should have received a getheaders now
                    test_node.send_header_for_blocks(blocks)
                    # Test that duplicate inv's won't result in duplicate
                    # getdata requests, or duplicate headers announcements
                    [inv_node.send_block_inv(x.sha256) for x in blocks]
                    test_node.wait_for_getdata([x.sha256 for x in blocks])
                    inv_node.sync_with_ping()
                else:
                    # Announce via headers
                    test_node.send_header_for_blocks(blocks)
                    test_node.wait_for_getdata([x.sha256 for x in blocks])
                    # Test that duplicate headers won't result in duplicate
                    # getdata requests (the check is further down)
                    inv_node.send_header_for_blocks(blocks)
                    inv_node.sync_with_ping()
                [test_node.send_message(msg_block(x)) for x in blocks]
                test_node.sync_with_ping()
                inv_node.sync_with_ping()
                # This block should not be announced to the inv node (since it also
                # broadcast it)
                assert "inv" not in inv_node.last_message
                assert "headers" not in inv_node.last_message
                tip = self.mine_blocks(1)
                snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
                inv_node.check_last_inv_announcement(inv=[tip])
                test_node.check_last_headers_announcement(headers=[tip])
                height += 1
                block_time += 1

        self.log.info("Part 2: success!")

        self.log.info(
            "Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer..."
        )

        # PART 3.  Headers announcements can stop after large reorg, and resume after
        # getheaders or inv from peer.
        for j in range(2):
            self.log.debug("Part 3.{}: starting...".format(j))
            # First try mining a reorg that can propagate with header announcement
            new_block_hashes = self.mine_reorg(length=7)
            tip = new_block_hashes[-1]
            inv_node.check_last_inv_announcement(inv=[tip])
            test_node.check_last_headers_announcement(headers=new_block_hashes)

            block_time += 8

            # Mine a too-large reorg, which should be announced with a single inv
            new_block_hashes = self.mine_reorg(length=8)
            tip = new_block_hashes[-1]
            inv_node.check_last_inv_announcement(inv=[tip])
            test_node.check_last_inv_announcement(inv=[tip])

            block_time += 9

            fork_point = self.nodes[0].getblock(
                "%02x" % new_block_hashes[0])["previousblockhash"]
            fork_point = int(fork_point, 16)

            # Use getblocks/getdata
            test_node.send_getblocks(locator=[fork_point])
            test_node.check_last_inv_announcement(inv=new_block_hashes)
            test_node.send_get_data(new_block_hashes)
            test_node.wait_for_block(new_block_hashes[-1])

            for i in range(3):
                self.log.debug("Part 3.{}.{}: starting...".format(j, i))

                # Mine another block, still should get only an inv
                tip = self.mine_blocks(1)
                inv_node.check_last_inv_announcement(inv=[tip])
                test_node.check_last_inv_announcement(inv=[tip])
                if i == 0:
                    # Just get the data -- shouldn't cause headers announcements to resume
                    test_node.send_get_data([tip])
                    test_node.wait_for_block(tip)
                elif i == 1:
                    # Send a getheaders message that shouldn't trigger headers announcements
                    # to resume (best header sent will be too old)
                    test_node.send_get_headers(locator=[fork_point],
                                               hashstop=new_block_hashes[1])
                    test_node.send_get_data([tip])
                    test_node.wait_for_block(tip)
                elif i == 2:
                    # This time, try sending either a getheaders to trigger resumption
                    # of headers announcements, or mine a new block and inv it, also
                    # triggering resumption of headers announcements.
                    test_node.send_get_data([tip])
                    test_node.wait_for_block(tip)
                    if j == 0:
                        test_node.send_get_headers(locator=[tip], hashstop=0)
                        test_node.sync_with_ping()
                    else:
                        test_node.send_block_inv(tip)
                        test_node.sync_with_ping()
            # New blocks should now be announced with header
            tip = self.mine_blocks(1)
            inv_node.check_last_inv_announcement(inv=[tip])
            test_node.check_last_headers_announcement(headers=[tip])

        self.log.info("Part 3: success!")

        self.log.info("Part 4: Testing direct fetch behavior...")
        tip = self.mine_blocks(1)
        height = self.nodes[0].getblockcount() + 1
        last_time = self.nodes[0].getblock(
            self.nodes[0].getbestblockhash())['time']
        block_time = last_time + 1

        # Create 2 blocks.  Send the blocks, then send the headers.
        blocks = []
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        coins = get_unspent_coins(self.nodes[0], 2)
        for b in range(2):
            coinbase = self.create_coinbase(height, snapshot_meta.hash,
                                            coins[b])
            blocks.append(create_block(tip, coinbase, block_time))
            blocks[-1].solve()
            tip = blocks[-1].sha256
            snapshot_meta = update_snapshot_with_tx(self.nodes[0],
                                                    snapshot_meta, height,
                                                    coinbase)
            block_time += 1
            height += 1
            inv_node.send_message(msg_block(blocks[-1]))

        inv_node.sync_with_ping()  # Make sure blocks are processed
        test_node.last_message.pop("getdata", None)
        test_node.send_header_for_blocks(blocks)
        test_node.sync_with_ping()
        # should not have received any getdata messages
        with mininode_lock:
            assert "getdata" not in test_node.last_message

        # This time, direct fetch should work
        blocks = []
        snapshots = [get_tip_snapshot_meta(self.nodes[0])]
        coins = get_unspent_coins(self.nodes[0], 3)
        for b in range(3):
            coinbase = self.create_coinbase(height, snapshots[-1].hash,
                                            coins[b])
            blocks.append(create_block(tip, coinbase, block_time))
            blocks[-1].solve()
            tip = blocks[-1].sha256
            snapshots.append(
                update_snapshot_with_tx(self.nodes[0], snapshots[-1], height,
                                        coinbase))
            block_time += 1
            height += 1

        test_node.send_header_for_blocks(blocks)
        test_node.sync_with_ping()
        test_node.wait_for_getdata([x.sha256 for x in blocks],
                                   timeout=DIRECT_FETCH_RESPONSE_TIME)

        [test_node.send_message(msg_block(x)) for x in blocks]

        test_node.sync_with_ping()

        # Now announce a header that forks the last two blocks
        tip = blocks[0].sha256
        snapshot_meta = snapshots[1]
        height -= 2
        blocks = []

        # Create extra blocks for later
        coins = get_unspent_coins(self.nodes[0], 20)
        for b in range(20):
            coinbase = self.create_coinbase(height, snapshot_meta.hash,
                                            coins[b])
            blocks.append(create_block(tip, coinbase, block_time))
            blocks[-1].solve()
            tip = blocks[-1].sha256
            snapshot_meta = update_snapshot_with_tx(self.nodes[0],
                                                    snapshot_meta, height,
                                                    coinbase)
            block_time += 1
            height += 1

        # Announcing one block on fork should not trigger direct fetch
        # (less work than tip)
        test_node.last_message.pop("getdata", None)
        test_node.send_header_for_blocks(blocks[0:1])
        test_node.sync_with_ping()
        with mininode_lock:
            assert "getdata" not in test_node.last_message

        # Announcing one more block on fork should trigger direct fetch for
        # both blocks (same work as tip)
        test_node.send_header_for_blocks(blocks[1:2])
        test_node.sync_with_ping()
        test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]],
                                   timeout=DIRECT_FETCH_RESPONSE_TIME)

        # Announcing 16 more headers should trigger direct fetch for 14 more
        # blocks
        test_node.send_header_for_blocks(blocks[2:18])
        test_node.sync_with_ping()
        test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]],
                                   timeout=DIRECT_FETCH_RESPONSE_TIME)

        # Announcing 1 more header should not trigger any response
        test_node.last_message.pop("getdata", None)
        test_node.send_header_for_blocks(blocks[18:19])
        test_node.sync_with_ping()
        with mininode_lock:
            assert "getdata" not in test_node.last_message

        self.log.info("Part 4: success!")

        # Now deliver all those blocks we announced.
        [test_node.send_message(msg_block(x)) for x in blocks]
        test_node.sync_with_ping()

        self.log.info("Part 5: Testing handling of unconnecting headers")
        # First we test that receipt of an unconnecting header doesn't prevent
        # chain sync.
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        for i in range(10):
            self.log.debug("Part 5.{}: starting...".format(i))
            test_node.last_message.pop("getdata", None)
            blocks = []
            # Create two more blocks.
            coins = get_unspent_coins(self.nodes[0], 2)
            for j in range(2):
                coinbase = self.create_coinbase(height, snapshot_meta.hash,
                                                coins[j])
                blocks.append(create_block(tip, coinbase, block_time))
                blocks[-1].solve()
                tip = blocks[-1].sha256
                snapshot_meta = update_snapshot_with_tx(
                    self.nodes[0], snapshot_meta, height, coinbase)
                block_time += 1
                height += 1
            # Send the header of the second block -> this won't connect.
            with mininode_lock:
                test_node.last_message.pop("getheaders", None)
            test_node.send_header_for_blocks([blocks[1]])
            test_node.wait_for_getheaders()
            test_node.send_header_for_blocks(blocks)
            test_node.wait_for_getdata([x.sha256 for x in blocks])
            [test_node.send_message(msg_block(x)) for x in blocks]
            test_node.sync_with_ping()
            assert_equal(int(self.nodes[0].getbestblockhash(), 16),
                         blocks[1].sha256)

        blocks = []
        # Now we test that if we repeatedly don't send connecting headers, we
        # don't go into an infinite loop trying to get them to connect.
        MAX_UNCONNECTING_HEADERS = 10
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        coins = get_unspent_coins(self.nodes[0], MAX_UNCONNECTING_HEADERS + 1)
        for j in range(MAX_UNCONNECTING_HEADERS + 1):
            coinbase = self.create_coinbase(height, snapshot_meta.hash,
                                            coins[j])
            blocks.append(create_block(tip, coinbase, block_time))
            blocks[-1].solve()
            tip = blocks[-1].sha256
            snapshot_meta = update_snapshot_with_tx(self.nodes[0],
                                                    snapshot_meta, height,
                                                    coinbase)
            block_time += 1
            height += 1

        for i in range(1, MAX_UNCONNECTING_HEADERS):
            # Send a header that doesn't connect, check that we get a getheaders.
            with mininode_lock:
                test_node.last_message.pop("getheaders", None)
            test_node.send_header_for_blocks([blocks[i]])
            test_node.wait_for_getheaders()

        # Next header will connect, should re-set our count:
        test_node.send_header_for_blocks([blocks[0]])

        # Remove the first two entries (blocks[1] would connect):
        blocks = blocks[2:]

        # Now try to see how many unconnecting headers we can send
        # before we get disconnected.  Should be 5*MAX_UNCONNECTING_HEADERS
        for i in range(5 * MAX_UNCONNECTING_HEADERS - 1):
            # Send a header that doesn't connect, check that we get a getheaders.
            with mininode_lock:
                test_node.last_message.pop("getheaders", None)
            test_node.send_header_for_blocks([blocks[i % len(blocks)]])
            test_node.wait_for_getheaders()

        # Eventually this stops working.
        test_node.send_header_for_blocks([blocks[-1]])

        # Should get disconnected
        test_node.wait_for_disconnect()

        self.log.info("Part 5: success!")

        # Finally, check that the inv node never received a getdata request,
        # throughout the test
        assert "getdata" not in inv_node.last_message
Example #16
0
    def run_test(self):
        self.setup_stake_coins(self.nodes[0])
        self.nodes[0].add_p2p_connection(P2PInterface())

        network_thread_start()

        # wait_for_verack ensures that the P2P connection is fully up.
        self.nodes[0].p2p.wait_for_verack()

        self.log.info("Mining one block")
        self.coinbase_blocks = self.nodes[0].generate(1)
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info(
            "Test that invalid-according-to-cltv transactions cannot appear in a block"
        )

        spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0],
                                     self.nodeaddress, 1.0)
        cltv_invalidate(spendtx)
        spendtx.rehash()

        # First we show that this tx is valid except for CLTV by getting it
        # accepted to the mempool (which we can achieve with
        # -promiscuousmempoolflags).
        self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
        assert spendtx.hash in self.nodes[0].getrawmempool()

        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        coinbase = sign_coinbase(self.nodes[0],
                                 create_coinbase(1, coin, snapshot_hash))
        block = create_block(int(tip, 16), coinbase, block_time)
        block.nVersion = 4
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(self.nodes[0].getbestblockhash(), tip)

        wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(),
                   lock=mininode_lock)
        with mininode_lock:
            assert self.nodes[0].p2p.last_message["reject"].code in [
                REJECT_INVALID, REJECT_NONSTANDARD
            ]
            assert_equal(self.nodes[0].p2p.last_message["reject"].data,
                         block.sha256)
            if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
                # Generic rejection when a block is invalid
                assert_equal(self.nodes[0].p2p.last_message["reject"].reason,
                             b'block-validation-failed')
            else:
                assert b'Negative locktime' in self.nodes[0].p2p.last_message[
                    "reject"].reason

        self.log.info(
            "Test that a version 4 block with a valid-according-to-CLTV transaction is accepted"
        )
        spendtx = cltv_validate(self.nodes[0], spendtx, 0)
        spendtx.rehash()

        block.vtx.pop(1)
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
Example #17
0
    def run_test(self):

        self.setup_stake_coins(self.nodes[0])

        node0 = self.nodes[0].add_p2p_connection(P2PInterface())

        network_thread_start()
        node0.wait_for_verack()

        # Set node time to 60 days ago
        self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)

        # Generating a chain of 10 blocks
        block_hashes = self.nodes[0].generate(nblocks=8)
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        unspent_outputs = get_unspent_coins(self.nodes[0], 5, lock=True)
        block_hashes += self.nodes[0].generate(nblocks=2)
        unspent_outputs = get_unspent_coins(self.nodes[0], 5)

        # Create longer chain starting 2 blocks before current tip
        height = len(block_hashes) - 2
        block_hash = block_hashes[height - 1]
        block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1
        new_blocks = self.build_chain(5, block_hash, height, block_time,
                                      unspent_outputs, snapshot_meta)

        # Force reorg to a longer chain
        node0.send_message(msg_headers(new_blocks))
        node0.wait_for_getdata()
        for block in new_blocks:
            node0.send_and_ping(msg_block(block))

        # Check that reorg succeeded
        assert_equal(self.nodes[0].getblockcount(), 13)

        stale_hash = int(block_hashes[-1], 16)

        # Check that getdata request for stale block succeeds
        self.send_block_request(stale_hash, node0)
        test_function = lambda: self.last_block_equals(stale_hash, node0)
        wait_until(test_function, timeout=3)

        # Check that getheader request for stale block header succeeds
        self.send_header_request(stale_hash, node0)
        test_function = lambda: self.last_header_equals(stale_hash, node0)
        wait_until(test_function, timeout=3)

        # Longest chain is extended so stale is much older than chain tip
        self.nodes[0].setmocktime(0)
        tip = self.nodes[0].generate(nblocks=1)[0]
        assert_equal(self.nodes[0].getblockcount(), 14)

        # Send getdata & getheaders to refresh last received getheader message
        block_hash = int(tip, 16)
        self.send_block_request(block_hash, node0)
        self.send_header_request(block_hash, node0)
        node0.sync_with_ping()

        # Request for very old stale block should now fail
        self.send_block_request(stale_hash, node0)
        time.sleep(3)
        assert not self.last_block_equals(stale_hash, node0)

        # Request for very old stale block header should now fail
        self.send_header_request(stale_hash, node0)
        time.sleep(3)
        assert not self.last_header_equals(stale_hash, node0)

        # Verify we can fetch very old blocks and headers on the active chain
        block_hash = int(block_hashes[2], 16)
        self.send_block_request(block_hash, node0)
        self.send_header_request(block_hash, node0)
        node0.sync_with_ping()

        self.send_block_request(block_hash, node0)
        test_function = lambda: self.last_block_equals(block_hash, node0)
        wait_until(test_function, timeout=3)

        self.send_header_request(block_hash, node0)
        test_function = lambda: self.last_header_equals(block_hash, node0)
        wait_until(test_function, timeout=3)
Example #18
0
    def run_test(self):
        self.setup_stake_coins(self.nodes[0])
        self.nodes[0].add_p2p_connection(P2PInterface())

        self.log.info("Mining one block")
        self.coinbase_txids = [
            self.nodes[0].getblock(b)['tx'][0]
            for b in self.nodes[0].generate(1)
        ]
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info(
            "Test that transactions with non-DER signatures cannot appear in a block"
        )

        spendtx = create_transaction(self.nodes[0],
                                     self.coinbase_txids[0],
                                     self.nodeaddress,
                                     amount=1.0)
        unDERify(spendtx)
        spendtx.rehash()

        # First we show that this tx is valid except for DERSIG by getting it
        # rejected from the mempool for exactly that reason.
        assert_equal([{
            'txid':
            spendtx.hash,
            'allowed':
            False,
            'reject-reason':
            '64: non-mandatory-script-verify-flag (Non-canonical DER signature)'
        }],
                     self.nodes[0].testmempoolaccept(
                         rawtxs=[bytes_to_hex_str(spendtx.serialize())],
                         allowhighfees=True))

        # Now we verify that a block with this transaction is also invalid.
        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        coinbase = sign_coinbase(self.nodes[0],
                                 create_coinbase(1, coin, snapshot_hash))
        block = create_block(int(tip, 16), coinbase, block_time)
        block.nVersion = 3
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), int(tip, 16))

        wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(),
                   lock=mininode_lock)
        with mininode_lock:
            # We can receive different reject messages depending on whether
            # unit-e is running with multiple script check threads. If script
            # check threads are not in use, then transaction script validation
            # happens sequentially, and unit-e produces more specific reject
            # reasons.
            assert self.nodes[0].p2p.last_message["reject"].code in [
                REJECT_INVALID, REJECT_NONSTANDARD
            ]
            assert_equal(self.nodes[0].p2p.last_message["reject"].data,
                         block.sha256)
            if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
                # Generic rejection when a block is invalid
                reject_reason = self.nodes[0].p2p.last_message["reject"].reason
                assert_equal(reject_reason, b'block-validation-failed')
            else:
                assert b'Non-canonical DER signature' in self.nodes[
                    0].p2p.last_message["reject"].reason

        self.log.info(
            "Test that a version 3 block with a DERSIG-compliant transaction is accepted"
        )
        block.vtx[1] = create_transaction(self.nodes[0],
                                          self.coinbase_txids[0],
                                          self.nodeaddress,
                                          amount=1.0)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
Example #19
0
    def run_test(self):
        node = self.nodes[0]  # convenience reference to the node

        self.setup_stake_coins(self.nodes[0])

        self.bootstrap_p2p()  # Add one p2p connection to the node

        best_block = self.nodes[0].getbestblockhash()
        tip = int(best_block, 16)
        best_block_time = self.nodes[0].getblock(best_block)['time']
        block_time = best_block_time + 1

        self.log.info("Create a new block with an anyone-can-spend coinbase.")
        height = 1
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        coinbase = sign_coinbase(self.nodes[0],
                                 create_coinbase(height, coin, snapshot_hash))
        block = create_block(tip, coinbase, block_time)

        block_time += 1
        block.solve()
        # Save the coinbase for later
        block1 = block
        tip = block.sha256
        node.p2p.send_blocks_and_test([block], node, success=True)

        self.log.info("Mature the block.")

        blocks = []
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        for i in range(100):
            prev_coinbase = coinbase
            height += 1
            stake = {
                'txid': prev_coinbase.hash,
                'vout': 1,
                'amount': prev_coinbase.vout[1].nValue / UNIT
            }
            coinbase = sign_coinbase(
                self.nodes[0],
                create_coinbase(height, stake, snapshot_meta.hash))
            block = create_block(tip, coinbase, block_time)
            block.solve()
            tip = block.sha256
            block_time += 1
            blocks.append(block)

            input_utxo = UTXO(height - 1, TxType.COINBASE,
                              coinbase.vin[1].prevout, prev_coinbase.vout[1])
            output_reward = UTXO(height, TxType.COINBASE,
                                 COutPoint(coinbase.sha256, 0),
                                 coinbase.vout[0])
            output_stake = UTXO(height, TxType.COINBASE,
                                COutPoint(coinbase.sha256, 1),
                                coinbase.vout[1])
            snapshot_meta = calc_snapshot_hash(self.nodes[0], snapshot_meta,
                                               height, [input_utxo],
                                               [output_reward, output_stake],
                                               coinbase)
        node.p2p.send_blocks_and_test(blocks, node, success=True)

        # b'\x64' is OP_NOTIF
        # Transaction will be rejected with code 16 (REJECT_INVALID)
        # and we get disconnected immediately
        self.log.info('Test a transaction that is rejected')
        tx1 = create_tx_with_script(coinbase,
                                    1,
                                    script_sig=b'\x64' * 35,
                                    amount=50 * UNIT - 12000)
        node.p2p.send_txs_and_test([tx1],
                                   node,
                                   success=False,
                                   expect_disconnect=True)

        # Make two p2p connections to provide the node with orphans
        # * p2ps[0] will send valid orphan txs (one with low fee)
        # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
        self.reconnect_p2p(num_connections=2)

        self.log.info('Test orphan transaction handling ... ')
        # Create a root transaction that we withhold until all dependend transactions
        # are sent out and in the orphan cache
        SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
        tx_withhold = CTransaction()
        tx_withhold.vin.append(
            CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0)))
        tx_withhold.vout.append(
            CTxOut(nValue=PROPOSER_REWARD * UNIT - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_withhold.calc_sha256()

        # Our first orphan tx with some outputs to create further orphan txs
        tx_orphan_1 = CTransaction()
        tx_orphan_1.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
        tx_orphan_1.vout = [
            CTxOut(nValue=1 * UNIT, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
        ] * 3
        tx_orphan_1.calc_sha256()

        # A valid transaction with low fee
        tx_orphan_2_no_fee = CTransaction()
        tx_orphan_2_no_fee.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
        tx_orphan_2_no_fee.vout.append(
            CTxOut(nValue=1 * UNIT, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))

        # A valid transaction with sufficient fee
        tx_orphan_2_valid = CTransaction()
        tx_orphan_2_valid.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
        tx_orphan_2_valid.vout.append(
            CTxOut(nValue=1 * UNIT - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_2_valid.calc_sha256()

        # An invalid transaction with negative fee
        tx_orphan_2_invalid = CTransaction()
        tx_orphan_2_invalid.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2)))
        tx_orphan_2_invalid.vout.append(
            CTxOut(nValue=Decimal('1.1') * UNIT,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))

        self.log.info('Send the orphans ... ')
        # Send valid orphan txs from p2ps[0]
        node.p2p.send_txs_and_test(
            [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid],
            node,
            success=False)
        # Send invalid tx from p2ps[1]
        node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid],
                                       node,
                                       success=False)

        assert_equal(0,
                     node.getmempoolinfo()['size'])  # Mempool should be empty
        assert_equal(2, len(node.getpeerinfo()))  # p2ps[1] is still connected

        self.log.info('Send the withhold tx ... ')
        node.p2p.send_txs_and_test([tx_withhold], node, success=True)

        # Transactions that should end up in the mempool
        expected_mempool = {
            t.hash
            for t in [
                tx_withhold,  # The transaction that is the root for all orphans
                tx_orphan_1,  # The orphan transaction that splits the coins
                tx_orphan_2_valid,  # The valid transaction (with sufficient fee)
            ]
        }
        # Transactions that do not end up in the mempool
        # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
        # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)

        wait_until(lambda: 1 == len(node.getpeerinfo()),
                   timeout=12)  # p2ps[1] is no longer connected
        assert_equal(expected_mempool, set(node.getrawmempool()))

        # restart node with sending BIP61 messages disabled, check that it disconnects without sending the reject message
        self.log.info(
            'Test a transaction that is rejected, with BIP61 disabled')
        self.restart_node(0, ['-enablebip61=0', '-persistmempool=0'])
        self.reconnect_p2p(num_connections=1)
        with node.assert_debug_log(expected_msgs=[
                "{} from peer=0 was not accepted: mandatory-script-verify-flag-failed (Invalid OP_IF construction) (code 16)"
                .format(tx1.hash),
                "disconnecting peer=0",
        ]):
            node.p2p.send_txs_and_test([tx1],
                                       node,
                                       success=False,
                                       expect_disconnect=True)
        # send_txs_and_test will have waited for disconnect, so we can safely check that no reject has been received
        assert_equal(node.p2p.reject_code_received, None)
Example #20
0
    def run_test(self):
        self.setup_stake_coins(self.nodes[0])
        self.nodes[0].add_p2p_connection(P2PInterface())

        network_thread_start()

        # wait_for_verack ensures that the P2P connection is fully up.
        self.nodes[0].p2p.wait_for_verack()

        self.log.info("Mining one block")
        self.coinbase_blocks = self.nodes[0].generate(1)
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info("Test that transactions with non-DER signatures cannot appear in a block")

        spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0],
                self.nodeaddress, 1.0)
        unDERify(spendtx)
        spendtx.rehash()

        # First we show that this tx is valid except for DERSIG by getting it
        # accepted to the mempool (which we can achieve with
        # -promiscuousmempoolflags).
        self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
        assert spendtx.hash in self.nodes[0].getrawmempool()

        # Now we verify that a block with this transaction is invalid.
        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        coinbase = sign_coinbase(self.nodes[0], create_coinbase(1, coin, snapshot_hash))
        block = create_block(int(tip, 16), coinbase, block_time)
        block.nVersion = 3
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), int(tip, 16))

        wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
        with mininode_lock:
            # We can receive different reject messages depending on whether
            # unit-e is running with multiple script check threads. If script
            # check threads are not in use, then transaction script validation
            # happens sequentially, and unit-e produces more specific reject
            # reasons.
            assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
            assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
            if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
                # Generic rejection when a block is invalid
                reject_reason = self.nodes[0].p2p.last_message["reject"].reason
                assert_equal(reject_reason, b'block-validation-failed')
            else:
                assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason

        self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
        block.vtx[1] = create_transaction(self.nodes[0],
                self.coinbase_blocks[0], self.nodeaddress, 1.0)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
Example #21
0
    def test_BIP(self, bipName, activated_version, invalidate,
                 invalidatePostSignature, bitno):
        def tip_coin():
            return get_unspent_coins(self.nodes[0], 1)[0]

        self.setup_stake_coins(self.nodes[0])
        assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
        assert_equal(self.get_bip9_status(bipName)['since'], 0)

        # generate some coins
        self.nodes[0].generate(2)
        self.height = 3  # height of the next block to build
        self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
        self.nodeaddress = self.nodes[0].getnewaddress()
        self.last_block_time = int(time.time())
        self.snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        assert_equal(self.get_bip9_status(bipName)['status'], 'defined')
        assert_equal(self.get_bip9_status(bipName)['since'], 0)

        # Test 1
        # Advance from DEFINED to STARTED
        coins = get_unspent_coins(self.nodes[0], 141)
        test_blocks = self.generate_blocks(coins, 141, 4)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(self.get_bip9_status(bipName)['status'], 'started')
        assert_equal(self.get_bip9_status(bipName)['since'], 144)
        assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
        assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)

        # Test 1-A
        # check stats after max number of "signalling not" blocks such that LOCKED_IN still possible this period
        coins = get_unspent_coins(self.nodes[0], 46)
        test_blocks = self.generate_blocks(
            coins, 36, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            coins, 10, activated_version)  # 0x20000001 (signalling ready)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(
            self.get_bip9_status(bipName)['statistics']['elapsed'], 46)
        assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
        assert_equal(
            self.get_bip9_status(bipName)['statistics']['possible'], True)

        # Test 1-B
        # check stats after one additional "signalling not" block --  LOCKED_IN no longer possible this period
        test_blocks = self.generate_blocks(
            [tip_coin()], 1, 4, test_blocks)  # 0x00000004 (signalling not)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(
            self.get_bip9_status(bipName)['statistics']['elapsed'], 47)
        assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
        assert_equal(
            self.get_bip9_status(bipName)['statistics']['possible'], False)

        # Test 1-C
        # finish period with "ready" blocks, but soft fork will still fail to advance to LOCKED_IN
        coins = get_unspent_coins(self.nodes[0], 97)
        test_blocks = self.generate_blocks(
            coins, 97, activated_version)  # 0x20000001 (signalling ready)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
        assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
        assert_equal(
            self.get_bip9_status(bipName)['statistics']['possible'], True)
        assert_equal(self.get_bip9_status(bipName)['status'], 'started')

        # Test 2
        # Fail to achieve LOCKED_IN 100 out of 144 signal bit 1
        # using a variety of bits to simulate multiple parallel softforks
        coins = get_unspent_coins(self.nodes[0], 144)
        test_blocks = self.generate_blocks(
            coins, 50, activated_version)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 20, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            coins, 50, activated_version,
            test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 24, 4, test_blocks)  # 0x20010000 (signalling not)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(self.get_bip9_status(bipName)['status'], 'started')
        assert_equal(self.get_bip9_status(bipName)['since'], 144)
        assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
        assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)

        # Test 3
        # 108 out of 144 signal bit 1 to achieve LOCKED_IN
        # using a variety of bits to simulate multiple parallel softforks
        coins = get_unspent_coins(self.nodes[0], 143)
        test_blocks = self.generate_blocks(
            coins, 57, activated_version)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 26, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            coins, 50, activated_version,
            test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 10, 4, test_blocks)  # 0x20010000 (signalling not)
        yield TestInstance(test_blocks, sync_every_block=False)

        # check counting stats and "possible" flag before last block of this period achieves LOCKED_IN...
        assert_equal(
            self.get_bip9_status(bipName)['statistics']['elapsed'], 143)
        assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 107)
        assert_equal(
            self.get_bip9_status(bipName)['statistics']['possible'], True)
        assert_equal(self.get_bip9_status(bipName)['status'], 'started')

        # ...continue with Test 3
        test_blocks = self.generate_blocks(
            [tip_coin()], 1,
            activated_version)  # 0x20000001 (signalling ready)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
        assert_equal(self.get_bip9_status(bipName)['since'], 576)

        # Test 4
        # 143 more version 536870913 blocks (waiting period-1)
        coins = get_unspent_coins(self.nodes[0], 143)
        test_blocks = self.generate_blocks(coins, 143, 4)
        yield TestInstance(test_blocks, sync_every_block=False)

        assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
        assert_equal(self.get_bip9_status(bipName)['since'], 576)

        # Test 5
        # Check that the new rule is enforced
        coins = get_unspent_coins(self.nodes[0], 2)
        spendtx = self.create_transaction(self.nodes[0], coins[0],
                                          self.nodeaddress, 1.0)
        invalidate(spendtx)
        spendtx = self.sign_transaction(self.nodes[0], spendtx)
        spendtx.rehash()
        invalidatePostSignature(spendtx)
        spendtx.rehash()
        coinbase = sign_coinbase(
            self.nodes[0],
            create_coinbase(self.height, coins[1], self.snapshot_meta.hash))
        block = create_block(self.tip, coinbase, self.last_block_time + 1)
        block.nVersion = activated_version
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.last_block_time += 1
        self.tip = block.sha256
        self.height += 1
        yield TestInstance([[block, True]])

        assert_equal(self.get_bip9_status(bipName)['status'], 'active')
        assert_equal(self.get_bip9_status(bipName)['since'], 720)

        self.snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # Test 6
        # Check that the new sequence lock rules are enforced
        coins = get_unspent_coins(self.nodes[0], 2)
        spendtx = self.create_transaction(self.nodes[0], coins.pop(),
                                          self.nodeaddress, 1.0)
        invalidate(spendtx)
        spendtx = self.sign_transaction(self.nodes[0], spendtx)
        spendtx.rehash()
        invalidatePostSignature(spendtx)
        spendtx.rehash()

        coinbase = sign_coinbase(
            self.nodes[0],
            create_coinbase(self.height, coins.pop(), self.snapshot_meta.hash))
        block = create_block(self.tip, coinbase, self.last_block_time + 1)
        block.nVersion = 5
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()
        self.last_block_time += 1
        yield TestInstance([[block, False]])

        # Restart all
        self.test.clear_all_connections()
        self.stop_nodes()
        self.nodes = []
        shutil.rmtree(self.options.tmpdir + "/node0")
        self.setup_chain()
        self.setup_network()
        self.test.add_all_connections(self.nodes)
        network_thread_start()
        self.test.p2p_connections[0].wait_for_verack()
Example #22
0
    def run_test(self):
        self.setup_stake_coins(self.nodes[0])
        self.nodes[0].add_p2p_connection(P2PInterface())

        self.log.info("Mining one block")
        self.coinbase_txids = [
            self.nodes[0].getblock(b)['tx'][0]
            for b in self.nodes[0].generate(1)
        ]
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info(
            "Test that invalid-according-to-cltv transactions cannot appear in a block"
        )

        spendtx = create_transaction(self.nodes[0],
                                     self.coinbase_txids[0],
                                     self.nodeaddress,
                                     amount=1.0)
        cltv_invalidate(spendtx)
        spendtx.rehash()

        # First we show that this tx is valid except for CLTV by getting it
        # rejected from the mempool for exactly that reason.
        assert_equal([{
            'txid':
            spendtx.hash,
            'allowed':
            False,
            'reject-reason':
            '64: non-mandatory-script-verify-flag (Negative locktime)'
        }],
                     self.nodes[0].testmempoolaccept(
                         rawtxs=[bytes_to_hex_str(spendtx.serialize())],
                         allowhighfees=True))

        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        snapshot_hash = get_tip_snapshot_meta(self.nodes[0]).hash
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        coinbase = sign_coinbase(self.nodes[0],
                                 create_coinbase(1, coin, snapshot_hash))
        block = create_block(int(tip, 16), coinbase, block_time)
        block.nVersion = 4
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(self.nodes[0].getbestblockhash(), tip)

        wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(),
                   lock=mininode_lock)
        with mininode_lock:
            assert self.nodes[0].p2p.last_message["reject"].code in [
                REJECT_INVALID, REJECT_NONSTANDARD
            ]
            assert_equal(self.nodes[0].p2p.last_message["reject"].data,
                         block.sha256)
            if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
                # Generic rejection when a block is invalid
                assert_equal(self.nodes[0].p2p.last_message["reject"].reason,
                             b'block-validation-failed')
            else:
                assert b'Negative locktime' in self.nodes[0].p2p.last_message[
                    "reject"].reason

        self.log.info(
            "Test that a version 4 block with a valid-according-to-CLTV transaction is accepted"
        )
        spendtx = cltv_validate(self.nodes[0], spendtx, 0)
        spendtx.rehash()

        block.vtx.pop(1)
        block.vtx.append(spendtx)
        block.compute_merkle_trees()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
Example #23
0
    def run_test(self):
        self.setup_stake_coins(*self.nodes)

        # Setup the p2p connections
        # test_node connects to node0 (not whitelisted)
        test_node = self.nodes[0].add_p2p_connection(P2PInterface())
        # min_work_node connects to node1 (whitelisted)
        min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())

        fork_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        utxo_manager = UTXOManager(self.nodes[0], fork_snapshot_meta)
        genesis_coin = get_unspent_coins(self.nodes[0], 1)[0]
        genesis_txout = CTxOut(
            int(genesis_coin['amount'] * UNIT),
            CScript(hex_str_to_bytes(genesis_coin['scriptPubKey'])))
        genesis_utxo = [
            UTXO(
                0, TxType.COINBASE,
                COutPoint(int(genesis_coin['txid'], 16), genesis_coin['vout']),
                genesis_txout)
        ]
        utxo_manager.available_outputs = genesis_utxo

        self.log.info("1. Have nodes mine a block (leave IBD)")
        [n.generate(1) for n in self.nodes]
        tips = [int("0x" + n.getbestblockhash(), 0) for n in self.nodes]
        tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        self.log.info(
            "2. Send one block that builds on each tip. This should be accepted by node0."
        )
        blocks_h2 = []  # the height 2 blocks on each node's chain
        block_time = int(time.time()) + 1
        coin = get_unspent_coins(self.nodes[0], 1)[0]
        for i in range(2):
            coinbase = sign_coinbase(
                self.nodes[0], create_coinbase(2, coin,
                                               tip_snapshot_meta.hash))
            blocks_h2.append(create_block(tips[i], coinbase, block_time))
            blocks_h2[i].solve()
            block_time += 1
        test_node.send_message(msg_block(blocks_h2[0]))
        min_work_node.send_message(msg_block(blocks_h2[1]))

        for x in [test_node, min_work_node]:
            x.sync_with_ping()
        assert_equal(self.nodes[0].getblockcount(), 2)
        assert_equal(self.nodes[1].getblockcount(), 1)
        self.log.info(
            "First height 2 block accepted by node0; correctly rejected by node1"
        )

        self.log.info("3. Send another block that builds on genesis.")
        coinbase = utxo_manager.get_coinbase(1, n_pieces=300)
        block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0),
                                 coinbase, block_time)
        block_time += 1
        block_h1f.solve()
        test_node.send_message(msg_block(block_h1f))
        utxo_manager.process(coinbase, 1)

        test_node.sync_with_ping()
        tip_entry_found = False
        for x in self.nodes[0].getchaintips():
            if x['hash'] == block_h1f.hash:
                assert_equal(x['status'], "headers-only")
                tip_entry_found = True
        assert tip_entry_found
        assert_raises_rpc_error(-1, "Block not found on disk",
                                self.nodes[0].getblock, block_h1f.hash)

        self.log.info("4. Send another two block that build on the fork.")
        coinbase = utxo_manager.get_coinbase(2)
        block_h2f = create_block(block_h1f.sha256, coinbase, block_time)
        block_time += 1
        block_h2f.solve()
        test_node.send_message(msg_block(block_h2f))

        utxo_manager.process(coinbase, 2)

        test_node.sync_with_ping()
        # Since the earlier block was not processed by node, the new block
        # can't be fully validated.
        tip_entry_found = False
        for x in self.nodes[0].getchaintips():
            if x['hash'] == block_h2f.hash:
                assert_equal(x['status'], "headers-only")
                tip_entry_found = True
        assert tip_entry_found

        # But this block should be accepted by node since it has equal work.
        self.nodes[0].getblock(block_h2f.hash)
        self.log.info("Second height 2 block accepted, but not reorg'ed to")

        self.log.info(
            "4b. Now send another block that builds on the forking chain.")
        coinbase = utxo_manager.get_coinbase(3)
        block_h3 = create_block(block_h2f.sha256, coinbase,
                                block_h2f.nTime + 1)
        block_h3.solve()
        test_node.send_message(msg_block(block_h3))
        utxo_manager.process(coinbase, 3)

        test_node.sync_with_ping()
        # Since the earlier block was not processed by node, the new block
        # can't be fully validated.
        tip_entry_found = False
        for x in self.nodes[0].getchaintips():
            if x['hash'] == block_h3.hash:
                assert_equal(x['status'], "headers-only")
                tip_entry_found = True
        assert tip_entry_found
        self.nodes[0].getblock(block_h3.hash)

        # But this block should be accepted by node since it has more work.
        self.nodes[0].getblock(block_h3.hash)
        self.log.info("Unrequested more-work block accepted")

        self.log.info("4c. Now mine 288 more blocks and deliver")
        # all should be processed but
        # the last (height-too-high) on node (as long as it is not missing any headers)
        tip = block_h3
        all_blocks = []
        for height in range(4, 292):
            coinbase = utxo_manager.get_coinbase(height)
            next_block = create_block(tip.sha256, coinbase, tip.nTime + 1)
            next_block.solve()
            all_blocks.append(next_block)
            tip = next_block
            utxo_manager.process(coinbase, height)

        # Now send the block at height 5 and check that it wasn't accepted (missing header)
        test_node.send_message(msg_block(all_blocks[1]))
        test_node.sync_with_ping()
        assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock,
                                all_blocks[1].hash)
        assert_raises_rpc_error(-5, "Block not found",
                                self.nodes[0].getblockheader,
                                all_blocks[1].hash)

        # The block at height 5 should be accepted if we provide the missing header, though
        headers_message = msg_headers()
        headers_message.headers.append(CBlockHeader(all_blocks[0]))
        test_node.send_message(headers_message)
        test_node.send_message(msg_block(all_blocks[1]))
        test_node.sync_with_ping()
        self.nodes[0].getblock(all_blocks[1].hash)

        # Now send the blocks in all_blocks
        for i in range(288):
            test_node.send_message(msg_block(all_blocks[i]))
        test_node.sync_with_ping()

        # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
        for x in all_blocks[:-1]:
            self.nodes[0].getblock(x.hash)
        assert_raises_rpc_error(-1, "Block not found on disk",
                                self.nodes[0].getblock, all_blocks[-1].hash)

        self.log.info(
            "5. Test handling of unrequested block on the node that didn't process"
        )
        # Should still not be processed (even though it has a child that has more
        # work).

        # The node should have requested the blocks at some point, so
        # disconnect/reconnect first

        self.nodes[0].disconnect_p2ps()
        self.nodes[1].disconnect_p2ps()

        test_node = self.nodes[0].add_p2p_connection(P2PInterface())

        test_node.send_message(msg_block(block_h1f))

        test_node.sync_with_ping()
        assert_equal(self.nodes[0].getblockcount(), 2)
        self.log.info(
            "Unrequested block that would complete more-work chain was ignored"
        )

        self.log.info("6. Try to get node to request the missing block.")
        # Poke the node with an inv for block at height 3 and see if that
        # triggers a getdata on block 2 (it should if block 2 is missing).
        with mininode_lock:
            # Clear state so we can check the getdata request
            test_node.last_message.pop("getdata", None)
            test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))

        test_node.sync_with_ping()
        with mininode_lock:
            getdata = test_node.last_message["getdata"]

        # Check that the getdata includes the right block
        assert_equal(getdata.inv[0].hash, block_h1f.sha256)
        self.log.info("Inv at tip triggered getdata for unprocessed block")

        self.log.info(
            "7. Send the missing block for the third time (now it is requested)"
        )
        test_node.send_message(msg_block(block_h1f))

        test_node.sync_with_ping()
        assert_equal(self.nodes[0].getblockcount(), 290)
        self.nodes[0].getblock(all_blocks[286].hash)
        assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
        assert_raises_rpc_error(-1, "Block not found on disk",
                                self.nodes[0].getblock, all_blocks[287].hash)
        self.log.info(
            "Successfully reorged to longer chain from non-whitelisted peer")

        self.log.info(
            "8. Create a chain which is invalid at a height longer than the")
        # current chain, but which has more blocks on top of that

        # Reset utxo managers to current state
        utxo_fork_manager = UTXOManager(self.nodes[0],
                                        get_tip_snapshot_meta(self.nodes[0]))
        utxo_fork_manager.available_outputs = utxo_manager.available_outputs
        utxo_manager = UTXOManager(self.nodes[0],
                                   get_tip_snapshot_meta(self.nodes[0]))
        utxo_manager.available_outputs = utxo_fork_manager.available_outputs

        # Create one block on top of the valid chain
        coinbase = utxo_manager.get_coinbase(291)
        valid_block = create_block(all_blocks[286].sha256, coinbase,
                                   all_blocks[286].nTime + 1)
        valid_block.solve()
        test_node.send_and_ping(msg_block(valid_block))
        assert_equal(self.nodes[0].getblockcount(), 291)

        # Create three blocks on a fork, but make the second one invalid
        coinbase = utxo_fork_manager.get_coinbase(291)
        block_291f = create_block(all_blocks[286].sha256, coinbase,
                                  all_blocks[286].nTime + 1)
        block_291f.solve()
        utxo_fork_manager.process(coinbase, 291)
        coinbase = utxo_fork_manager.get_coinbase(292)
        block_292f = create_block(block_291f.sha256, coinbase,
                                  block_291f.nTime + 1)
        # block_292f spends a coinbase below maturity!
        block_292f.vtx.append(
            create_tx_with_script(block_291f.vtx[0],
                                  0,
                                  script_sig=b"42",
                                  amount=1))
        block_292f.compute_merkle_trees()
        block_292f.solve()
        utxo_fork_manager.process(coinbase, 292)
        utxo_fork_manager.process(block_292f.vtx[1], 292)
        coinbase = utxo_fork_manager.get_coinbase(293)
        block_293f = create_block(block_292f.sha256, coinbase,
                                  block_292f.nTime + 1)
        block_293f.solve()
        utxo_fork_manager.process(coinbase, 293)

        # Now send all the headers on the chain and enough blocks to trigger reorg
        headers_message = msg_headers()
        headers_message.headers.append(CBlockHeader(block_291f))
        headers_message.headers.append(CBlockHeader(block_292f))
        headers_message.headers.append(CBlockHeader(block_293f))
        test_node.send_message(headers_message)

        test_node.sync_with_ping()
        tip_entry_found = False
        for x in self.nodes[0].getchaintips():
            if x['hash'] == block_293f.hash:
                assert_equal(x['status'], "headers-only")
                tip_entry_found = True
        assert tip_entry_found
        assert_raises_rpc_error(-1, "Block not found on disk",
                                self.nodes[0].getblock, block_293f.hash)

        test_node.send_message(msg_block(block_291f))

        test_node.sync_with_ping()
        self.nodes[0].getblock(block_291f.hash)

        test_node.send_message(msg_block(block_292f))

        # At this point we've sent an obviously-bogus block, wait for full processing
        # without assuming whether we will be disconnected or not
        try:
            # Only wait a short while so the test doesn't take forever if we do get
            # disconnected
            test_node.sync_with_ping(timeout=1)
        except AssertionError:
            test_node.wait_for_disconnect()

            self.nodes[0].disconnect_p2ps()
            test_node = self.nodes[0].add_p2p_connection(P2PInterface())

        # We should have failed reorg and switched back to 290 (but have block 291)
        assert_equal(self.nodes[0].getblockcount(), 291)
        assert_equal(self.nodes[0].getbestblockhash(), valid_block.hash)
        assert_equal(self.nodes[0].getblock(block_292f.hash)["confirmations"],
                     -1)

        # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected
        coinbase = utxo_fork_manager.get_coinbase(294)
        block_294f = create_block(block_293f.sha256, coinbase,
                                  block_293f.nTime + 1)
        block_294f.solve()
        headers_message = msg_headers()
        headers_message.headers.append(CBlockHeader(block_294f))
        test_node.send_message(headers_message)
        test_node.wait_for_disconnect()

        self.log.info(
            "9. Connect node1 to node0 and ensure it is able to sync")
        connect_nodes(self.nodes[0], 1)
        sync_blocks([self.nodes[0], self.nodes[1]])
        self.log.info("Successfully synced nodes 1 and 0")
Example #24
0
    def run_test(self):

        # Create a block with 2500 stakeable outputs
        self.build_coins_to_stake()

        # Propagate it to nodes 1 and 2 and stop them for now
        self.sync_first_block()

        # Key Management for node 0
        keytool = KeyTool.for_node(self.nodes[0])

        # Connect to node0
        p2p0 = self.nodes[0].add_p2p_connection(BaseNode())

        # Build the blockchain
        self.tip = int(self.nodes[0].getbestblockhash(), 16)
        self.block_time = self.nodes[0].getblock(
            self.nodes[0].getbestblockhash())['time'] + 1

        self.blocks = []

        # Get a pubkey for the coinbase TXO
        coinbase_key = keytool.make_privkey()
        coinbase_pubkey = bytes(coinbase_key.get_pubkey())

        keytool.upload_key(coinbase_key)

        self.log.info(
            "Create the first block with a coinbase output to our key")
        height = 2
        snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        coin = self.get_coin_to_stake()
        coinbase = sign_coinbase(
            self.nodes[0],
            create_coinbase(height, coin, snapshot_meta.hash, coinbase_pubkey))
        block = create_block(self.tip, coinbase, self.block_time)
        self.blocks.append(block)
        self.block_time += 1
        block.solve()
        # Save the coinbase for later
        self.block1 = block
        self.tip = block.sha256

        utxo1 = UTXO(height, TxType.COINBASE, COutPoint(coinbase.sha256, 0),
                     coinbase.vout[0])
        snapshot_meta = update_snapshot_with_tx(self.nodes[0], snapshot_meta,
                                                height, coinbase)
        height += 1

        self.log.info(
            "Bury the block 100 deep so the coinbase output is spendable")
        for i in range(100):
            coin = self.get_coin_to_stake()
            coinbase = sign_coinbase(
                self.nodes[0],
                create_coinbase(height, coin, snapshot_meta.hash,
                                coinbase_pubkey))
            block = create_block(self.tip, coinbase, self.block_time)
            block.solve()
            self.blocks.append(block)
            self.tip = block.sha256
            self.block_time += 1
            snapshot_meta = update_snapshot_with_tx(self.nodes[0],
                                                    snapshot_meta, height,
                                                    coinbase)
            height += 1

        self.log.info(
            "Create a transaction spending the coinbase output with an invalid (null) signature"
        )
        tx = CTransaction()
        tx.vin.append(
            CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b""))
        tx.vout.append(
            CTxOut((PROPOSER_REWARD - 1) * 100000000, CScript([OP_TRUE])))
        tx.calc_sha256()

        coin = self.get_coin_to_stake()
        coinbase = sign_coinbase(
            self.nodes[0],
            create_coinbase(height, coin, snapshot_meta.hash, coinbase_pubkey))
        block102 = create_block(self.tip, coinbase, self.block_time)
        self.block_time += 1
        block102.vtx.extend([tx])
        block102.compute_merkle_trees()
        block102.rehash()
        block102.solve()
        self.blocks.append(block102)
        self.tip = block102.sha256
        self.block_time += 1

        snapshot_meta = update_snapshot_with_tx(self.nodes[0], snapshot_meta,
                                                height, coinbase)

        utxo2 = UTXO(height, tx.get_type(), COutPoint(tx.sha256, 0),
                     tx.vout[0])
        snapshot_meta = calc_snapshot_hash(self.nodes[0], snapshot_meta,
                                           height, [utxo1], [utxo2])

        height += 1

        self.log.info("Bury the assumed valid block 2100 deep")
        for i in range(2100):
            coin = self.get_coin_to_stake()
            coinbase = sign_coinbase(
                self.nodes[0],
                create_coinbase(height, coin, snapshot_meta.hash,
                                coinbase_pubkey))
            block = create_block(self.tip, coinbase, self.block_time)
            block.nVersion = 4
            block.solve()
            self.blocks.append(block)
            self.tip = block.sha256
            self.block_time += 1
            snapshot_meta = update_snapshot_with_tx(self.nodes[0],
                                                    snapshot_meta, height,
                                                    coinbase)
            height += 1

        self.nodes[0].disconnect_p2ps()

        self.log.info(
            "Start node1 and node2 with assumevalid so they accept a block with a bad signature."
        )
        self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)])
        self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)])

        p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
        p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
        p2p2 = self.nodes[2].add_p2p_connection(BaseNode())

        # send header lists to all three nodes
        p2p0.send_header_for_blocks(self.blocks[0:2000])
        p2p0.send_header_for_blocks(self.blocks[2000:])
        p2p1.send_header_for_blocks(self.blocks[0:2000])
        p2p1.send_header_for_blocks(self.blocks[2000:])
        p2p2.send_header_for_blocks(self.blocks[0:200])

        self.log.info("Send blocks to node0. Block 103 will be rejected.")
        self.send_blocks_until_disconnected(p2p0)
        self.assert_blockchain_height(self.nodes[0], 102)

        self.log.info("Send all blocks to node1. All blocks will be accepted.")
        for i in range(2202):
            p2p1.send_message(msg_block(self.blocks[i]))
        # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
        p2p1.sync_with_ping(120)
        assert_equal(
            self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'],
            2203)

        self.log.info("Send blocks to node2. Block 102 will be rejected.")
        self.send_blocks_until_disconnected(p2p2)
        self.assert_blockchain_height(self.nodes[2], 102)
Example #25
0
    def get_tests(self):
        # Convenience wrapper
        def tip_coin():
            return get_unspent_coins(self.nodes[0], 1)[0]

        long_past_time = int(
            time.time()
        ) - 600 * 1000  # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
        self.nodes[0].setmocktime(
            long_past_time - 100
        )  # enough so that the generated blocks will still all be before long_past_time
        self.nodes[0].generate(
            1
        )  # split coins to have enough unspent outputs for staking and spending

        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        self.nodes[0].setmocktime(
            0
        )  # set time back to present so yielded blocks aren't in the future as we advance last_block_time
        self.tipheight = self.nodes[0].getblockcount(
        )  # height of the next block to build
        self.last_block_time = long_past_time
        self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
        self.nodeaddress = self.nodes[0].getnewaddress()

        n_blocks_left_to_defined = 143 - self.tipheight
        coins = get_unspent_coins(self.nodes[0], n_blocks_left_to_defined)
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'defined')
        test_blocks = self.generate_blocks(coins, n_blocks_left_to_defined, 4)
        yield TestInstance(test_blocks, sync_every_block=False)  # 1
        # Advanced from DEFINED to STARTED, height = 143
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'started')

        coins = get_unspent_coins(self.nodes[0], 144)

        # Fail to achieve LOCKED_IN 100 out of 144 signal bit 0
        # using a variety of bits to simulate multiple parallel softforks
        test_blocks = self.generate_blocks(
            coins, 50, 536870913)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 20, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            coins, 50, 536871169, test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 24, 536936448, test_blocks)  # 0x20010000 (signalling not)
        yield TestInstance(test_blocks, sync_every_block=False)  # 2
        # Failed to advance past STARTED, height = 287
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'started')

        coins = get_unspent_coins(self.nodes[0], 144)

        # 108 out of 144 signal bit 0 to achieve lock-in
        # using a variety of bits to simulate multiple parallel softforks
        test_blocks = self.generate_blocks(
            coins, 58, 536870913)  # 0x20000001 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 26, 4, test_blocks)  # 0x00000004 (signalling not)
        test_blocks = self.generate_blocks(
            coins, 50, 536871169, test_blocks)  # 0x20000101 (signalling ready)
        test_blocks = self.generate_blocks(
            coins, 10, 536936448, test_blocks)  # 0x20010000 (signalling not)
        yield TestInstance(test_blocks, sync_every_block=False)  # 3
        # Advanced from STARTED to LOCKED_IN, height = 431
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')

        coins = get_unspent_coins(self.nodes[0], 140)

        # 140 more version 4 blocks
        test_blocks = self.generate_blocks(coins, 140, 4)
        yield TestInstance(test_blocks, sync_every_block=False)  # 4

        coins = get_unspent_coins(self.nodes[0], 87)

        ### Inputs at height = 572
        # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block)
        # Note we reuse inputs for v1 and v2 txs so must test these separately
        # 16 normal inputs
        bip68inputs = []
        for i in range(16):
            bip68inputs.append(self.send_generic_input_tx(
                self.nodes[0], coins))
        # 2 sets of 16 inputs with 10 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112basicinputs = []
        for j in range(2):
            inputs = []
            for i in range(16):
                inputs.append(self.send_generic_input_tx(self.nodes[0], coins))
            bip112basicinputs.append(inputs)
        # 2 sets of 16 varied inputs with (relative_lock_time) OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112diverseinputs = []
        for j in range(2):
            inputs = []
            for i in range(16):
                inputs.append(self.send_generic_input_tx(self.nodes[0], coins))
            bip112diverseinputs.append(inputs)
        # 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
        bip112specialinput = self.send_generic_input_tx(self.nodes[0], coins)
        # 1 normal input
        bip113input = self.send_generic_input_tx(self.nodes[0], coins)

        self.nodes[0].setmocktime(self.last_block_time + 600)
        inputblockhash = self.nodes[0].generate(1)[
            0]  # 1 block generated for inputs to be in chain at height 572
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        self.nodes[0].setmocktime(0)
        self.tip = int("0x" + inputblockhash, 0)
        self.tipheight += 1
        self.last_block_time += 600
        assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]),
                     82 + 1)

        # 2 more version 4 blocks
        test_blocks = self.generate_blocks(coins, 2, 4)
        yield TestInstance(test_blocks, sync_every_block=False)  # 5
        # Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)
        assert_equal(
            get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in')

        # Test both version 1 and version 2 transactions for all tests
        # BIP113 test transaction will be modified before each use to put in appropriate block time
        bip113tx_v1 = self.create_transaction(self.nodes[0], bip113input,
                                              self.nodeaddress)
        bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
        bip113tx_v1.nVersion = 1
        bip113tx_v2 = self.create_transaction(self.nodes[0], bip113input,
                                              self.nodeaddress)
        bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
        bip113tx_v2.nVersion = 2

        # For BIP68 test all 16 relative sequence locktimes
        bip68txs_v1 = self.create_bip68txs(bip68inputs, 1)
        bip68txs_v2 = self.create_bip68txs(bip68inputs, 2)

        # For BIP112 test:
        # 16 relative sequence locktimes of 10 against 10 OP_CSV OP_DROP inputs
        bip112txs_vary_nSequence_v1 = self.create_bip112txs(
            bip112basicinputs[0], False, 1)
        bip112txs_vary_nSequence_v2 = self.create_bip112txs(
            bip112basicinputs[0], False, 2)
        # 16 relative sequence locktimes of 9 against 10 OP_CSV OP_DROP inputs
        bip112txs_vary_nSequence_9_v1 = self.create_bip112txs(
            bip112basicinputs[1], False, 1, -1)
        bip112txs_vary_nSequence_9_v2 = self.create_bip112txs(
            bip112basicinputs[1], False, 2, -1)
        # sequence lock time of 10 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
        bip112txs_vary_OP_CSV_v1 = self.create_bip112txs(
            bip112diverseinputs[0], True, 1)
        bip112txs_vary_OP_CSV_v2 = self.create_bip112txs(
            bip112diverseinputs[0], True, 2)
        # sequence lock time of 9 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
        bip112txs_vary_OP_CSV_9_v1 = self.create_bip112txs(
            bip112diverseinputs[1], True, 1, -1)
        bip112txs_vary_OP_CSV_9_v2 = self.create_bip112txs(
            bip112diverseinputs[1], True, 2, -1)
        # -1 OP_CSV OP_DROP input
        bip112tx_special_v1 = self.create_bip112special(bip112specialinput, 1)
        bip112tx_special_v2 = self.create_bip112special(bip112specialinput, 2)

        ### TESTING ###
        ##################################
        ### Before Soft Forks Activate ###
        ##################################
        # All txs should pass
        ### Version 1 txs ###
        success_txs = []
        # add BIP113 tx and -1 CSV tx
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed1 = self.sign_transaction(self.nodes[0], bip113tx_v1)
        success_txs.append(bip113signed1)
        success_txs.append(bip112tx_special_v1)
        # add BIP 68 txs
        success_txs.extend(all_rlt_txs(bip68txs_v1))
        # add BIP 112 with seq=10 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v1))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_v1))
        # try BIP 112 with seq=9 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v1))
        yield TestInstance(
            [[self.create_test_block(coins.pop(), success_txs), True]])  # 6
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        coins = get_unspent_coins(self.nodes[0], 1)

        ### Version 2 txs ###
        success_txs = []
        # add BIP113 tx and -1 CSV tx
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed2 = self.sign_transaction(self.nodes[0], bip113tx_v2)
        success_txs.append(bip113signed2)
        success_txs.append(bip112tx_special_v2)
        # add BIP 68 txs
        success_txs.extend(all_rlt_txs(bip68txs_v2))
        # add BIP 112 with seq=10 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v2))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_v2))
        # try BIP 112 with seq=9 txs
        success_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v2))
        success_txs.extend(all_rlt_txs(bip112txs_vary_OP_CSV_9_v2))
        yield TestInstance(
            [[self.create_test_block(coins.pop(), success_txs), True]])  # 7
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        coins = get_unspent_coins(self.nodes[0], 1)

        # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block
        test_blocks = self.generate_blocks(coins, 1, 4)
        yield TestInstance(test_blocks, sync_every_block=False)  # 8
        assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active')

        coins = get_unspent_coins(self.nodes[0], 8)

        #################################
        ### After Soft Forks Activate ###
        #################################
        ### BIP 113 ###
        # BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed1 = self.sign_transaction(self.nodes[0], bip113tx_v1)
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5  # = MTP of prior block (not <) but < time put on current block
        bip113signed2 = self.sign_transaction(self.nodes[0], bip113tx_v2)
        for bip113tx in [bip113signed1, bip113signed2]:
            yield TestInstance(
                [[self.create_test_block(coins.pop(), [bip113tx]),
                  False]])  # 9,10
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        # BIP 113 tests should now pass if the locktime is < MTP
        bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1  # < MTP of prior block
        bip113signed1 = self.sign_transaction(self.nodes[0], bip113tx_v1)
        bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1  # < MTP of prior block
        bip113signed2 = self.sign_transaction(self.nodes[0], bip113tx_v2)
        for bip113tx in [bip113signed1, bip113signed2]:
            yield TestInstance(
                [[self.create_test_block(coins.pop(), [bip113tx]),
                  True]])  # 11,12
            self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # Next block height = 580 after 4 blocks of random version
        test_blocks = self.generate_blocks(coins, 4, 1234)
        yield TestInstance(test_blocks, sync_every_block=False)  # 13

        ### BIP 68 ###
        ### Version 1 txs ###
        # All still pass
        success_txs = []
        success_txs.extend(all_rlt_txs(bip68txs_v1))
        yield TestInstance(
            [[self.create_test_block(tip_coin(), success_txs), True]])  # 14
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        ### Version 2 txs ###
        bip68success_txs = []
        # All txs with SEQUENCE_LOCKTIME_DISABLE_FLAG set pass
        for b25 in range(2):
            for b22 in range(2):
                for b18 in range(2):
                    bip68success_txs.append(bip68txs_v2[1][b25][b22][b18])
        yield TestInstance(
            [[self.create_test_block(tip_coin(), bip68success_txs),
              True]])  # 15
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        # All txs without flag fail as we are at delta height = 8 < 10 and delta time = 8 * 600 < 10 * 512
        bip68timetxs = []
        for b25 in range(2):
            for b18 in range(2):
                bip68timetxs.append(bip68txs_v2[0][b25][1][b18])
        for tx in bip68timetxs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]), False]])  # 16 - 19
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        bip68heighttxs = []
        for b25 in range(2):
            for b18 in range(2):
                bip68heighttxs.append(bip68txs_v2[0][b25][0][b18])
        for tx in bip68heighttxs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]), False]])  # 20 - 23
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # Advance one block to 581
        test_blocks = self.generate_blocks([tip_coin()], 1, 1234)
        yield TestInstance(test_blocks, sync_every_block=False)  # 24

        # Height txs should fail and time txs should now pass 9 * 600 > 10 * 512
        bip68success_txs.extend(bip68timetxs)
        yield TestInstance(
            [[self.create_test_block(tip_coin(), bip68success_txs),
              True]])  # 25
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        for tx in bip68heighttxs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]), False]])  # 26 - 29
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # Advance one block to 582
        test_blocks = self.generate_blocks([tip_coin()], 1, 1234)
        yield TestInstance(test_blocks, sync_every_block=False)  # 30

        # All BIP 68 txs should pass
        bip68success_txs.extend(bip68heighttxs)
        yield TestInstance(
            [[self.create_test_block(tip_coin(), bip68success_txs),
              True]])  # 31
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        ### BIP 112 ###
        ### Version 1 txs ###
        # -1 OP_CSV tx should fail
        yield TestInstance([[
            self.create_test_block(tip_coin(), [bip112tx_special_v1]), False
        ]])  #32
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])
        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass
        success_txs = []
        for b25 in range(2):
            for b22 in range(2):
                for b18 in range(2):
                    success_txs.append(
                        bip112txs_vary_OP_CSV_v1[1][b25][b22][b18])
                    success_txs.append(
                        bip112txs_vary_OP_CSV_9_v1[1][b25][b22][b18])
        yield TestInstance(
            [[self.create_test_block(tip_coin(), success_txs), True]])  # 33
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV, version 1 txs should now fail
        fail_txs = []
        fail_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_v1))
        fail_txs.extend(all_rlt_txs(bip112txs_vary_nSequence_9_v1))
        for b25 in range(2):
            for b22 in range(2):
                for b18 in range(2):
                    fail_txs.append(bip112txs_vary_OP_CSV_v1[0][b25][b22][b18])
                    fail_txs.append(
                        bip112txs_vary_OP_CSV_9_v1[0][b25][b22][b18])

        for tx in fail_txs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]), False]])  # 34 - 81
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        ### Version 2 txs ###
        # -1 OP_CSV tx should fail
        yield TestInstance([[
            self.create_test_block(tip_coin(), [bip112tx_special_v2]), False
        ]])  #82
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
        success_txs = []
        for b25 in range(2):
            for b22 in range(2):
                for b18 in range(2):
                    success_txs.append(bip112txs_vary_OP_CSV_v2[1][b25][b22]
                                       [b18])  # 8/16 of vary_OP_CSV
                    success_txs.append(bip112txs_vary_OP_CSV_9_v2[1][b25][b22]
                                       [b18])  # 8/16 of vary_OP_CSV_9

        yield TestInstance(
            [[self.create_test_block(tip_coin(), success_txs), True]])  # 83
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        ## SEQUENCE_LOCKTIME_DISABLE_FLAG is unset in argument to OP_CSV for all remaining txs ##
        # All txs with nSequence 9 should fail either due to earlier mismatch or failing the CSV check
        fail_txs = []
        fail_txs.extend(all_rlt_txs(
            bip112txs_vary_nSequence_9_v2))  # 16/16 of vary_nSequence_9
        for b25 in range(2):
            for b22 in range(2):
                for b18 in range(2):
                    fail_txs.append(bip112txs_vary_OP_CSV_9_v2[0][b25][b22]
                                    [b18])  # 16/16 of vary_OP_CSV_9

        for tx in fail_txs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]),
                  False]])  # 84 - 107
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail
        fail_txs = []
        for b25 in range(2):
            for b22 in range(2):
                for b18 in range(2):
                    fail_txs.append(bip112txs_vary_nSequence_v2[1][b25][b22]
                                    [b18])  # 8/16 of vary_nSequence
        for tx in fail_txs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]), False]])  # 108-115
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # If sequencelock types mismatch, tx should fail
        fail_txs = []
        for b25 in range(2):
            for b18 in range(2):
                fail_txs.append(bip112txs_vary_nSequence_v2[0][b25][1]
                                [b18])  # 12/16 of vary_nSequence
                fail_txs.append(bip112txs_vary_OP_CSV_v2[0][b25][1]
                                [b18])  # 12/16 of vary_OP_CSV
        for tx in fail_txs:
            yield TestInstance(
                [[self.create_test_block(tip_coin(), [tx]), False]])  # 116-123
            self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # Remaining txs should pass, just test masking works properly
        success_txs = []
        for b25 in range(2):
            for b18 in range(2):
                success_txs.append(bip112txs_vary_nSequence_v2[0][b25][0]
                                   [b18])  # 16/16 of vary_nSequence
                success_txs.append(bip112txs_vary_OP_CSV_v2[0][b25][0]
                                   [b18])  # 16/16 of vary_OP_CSV
        yield TestInstance(
            [[self.create_test_block(tip_coin(), success_txs), True]])  # 124
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])

        # Additional test, of checking that comparison of two time types works properly
        time_txs = []
        for b25 in range(2):
            for b18 in range(2):
                tx = bip112txs_vary_OP_CSV_v2[0][b25][1][b18]
                tx.vin[0].nSequence = base_relative_locktime | seq_type_flag
                signtx = self.sign_transaction(self.nodes[0], tx)
                time_txs.append(signtx)
        yield TestInstance(
            [[self.create_test_block(tip_coin(), time_txs), True]])  # 125
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.tip_snapshot_meta = get_tip_snapshot_meta(self.nodes[0])