Esempio n. 1
0
    def next_block(self, number, transactions=None, nTime=None):
        if self.tip == None:
            base_block_hash = self.genesis_hash
            block_time = int(time.time()) + 1
        else:
            base_block_hash = self.tip.sha256
            block_time = self.tip.nTime + 1
        if nTime:
            block_time = nTime
        # First create the coinbase
        height = self.block_heights[base_block_hash] + 1
        coinbase = create_coinbase(height)
        coinbase.rehash()
        block = create_block(base_block_hash, coinbase, block_time)

        # add in transactions
        if transactions:
            block.vtx.extend(transactions)
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()

        # Do PoW, which is cheap on regnet
        block.solve()
        self.tip = block
        self.block_heights[block.sha256] = height
        assert number not in self.blocks
        self.blocks[number] = block
        return block
Esempio n. 2
0
 def create_test_block_spend_utxos(self, node, txs, version=536870912):
     block = self.create_test_block(txs, version)
     block.vtx.extend([self.spend_tx(node, tx) for tx in txs])
     make_conform_to_ctor(block)
     block.hashMerkleRoot = block.calc_merkle_root()
     block.rehash()
     block.solve()
     return block
Esempio n. 3
0
 def create_test_block(self, txs, version=536870912):
     block = create_block(self.tip, create_coinbase(self.tipheight + 1),
                          self.last_block_time + 600)
     block.nVersion = version
     block.vtx.extend(txs)
     make_conform_to_ctor(block)
     block.hashMerkleRoot = block.calc_merkle_root()
     block.rehash()
     block.solve()
     return block
Esempio n. 4
0
 def create_block_and_send(self, txs, node):
     block = create_block_with_txns(self.tip, txs, self.block_time)
     make_conform_to_ctor(block)
     block.hashMerkleRoot = block.calc_merkle_root()
     block.calc_sha256()
     block.solve()
     node.send_message(msg_block(block))
     self.tip = block.sha256
     self.block_time += 1
     self.height += 1
Esempio n. 5
0
 def update_block(block_number, new_transactions):
     block = self.blocks[block_number]
     block.vtx.extend(new_transactions)
     old_sha256 = block.sha256
     make_conform_to_ctor(block)
     block.hashMerkleRoot = block.calc_merkle_root()
     block.solve()
     # Update the internal state just like in next_block
     self.tip = block
     if block.sha256 != old_sha256:
         self.block_heights[
             block.sha256] = self.block_heights[old_sha256]
         del self.block_heights[old_sha256]
     self.blocks[block_number] = block
     return block
 def update_block(self, block_number, new_transactions, reorder=True):
     block = self.blocks[block_number]
     self.add_transactions_to_block(block, new_transactions)
     old_sha256 = block.sha256
     if reorder:
         make_conform_to_ctor(block)
     block.hashMerkleRoot = block.calc_merkle_root()
     block.solve()
     # Update the internal state just like in next_block
     self.tip = block
     if block.sha256 != old_sha256:
         self.block_heights[block.sha256] = self.block_heights[old_sha256]
         del self.block_heights[old_sha256]
     self.blocks[block_number] = block
     return block
Esempio n. 7
0
    def build_block(self, parent, transactions=(), nTime=None):
        """Make a new block with an OP_1 coinbase output.

        Requires parent to have its height registered."""
        parent.calc_sha256()
        block_height = self.block_heights[parent.sha256] + 1
        block_time = (parent.nTime + 1) if nTime is None else nTime

        block = create_block(parent.sha256, create_coinbase(block_height),
                             block_time)
        block.vtx.extend(transactions)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()
        self.block_heights[block.sha256] = block_height
        return block
    def build_block(self, parent, transactions=(), n_time=None):
        """Make a new block with an OP_1 coinbase output.

        Requires parent to have its height registered."""
        parent.calc_sha256()
        block_height = self.block_heights[parent.sha256] + 1
        block_time = (parent.nTime + 1) if n_time is None else n_time

        # the script in create_coinbase differs for BU and ABC
        # you need to let coinbase script be CScript([OP_TRUE])
        block = create_block(
            parent.sha256, create_coinbase(block_height, scriptPubKey = CScript([OP_TRUE])), block_time)
        block.vtx.extend(transactions)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()
        self.block_heights[block.sha256] = block_height
        return block
Esempio n. 9
0
    def run_test(self):
        self.nodes[0].add_p2p_connection(P2PInterface())

        self.log.info("Mining {} blocks".format(CLTV_HEIGHT - 2))
        self.coinbase_txids = [
            self.nodes[0].getblock(b)['tx'][0]
            for b in self.generate(self.nodes[0], CLTV_HEIGHT - 2)
        ]
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info(
            "Test that an invalid-according-to-CLTV transaction can still appear in a block"
        )

        fundtx = create_transaction(self.nodes[0], self.coinbase_txids[0],
                                    self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98)

        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1),
                             block_time)
        block.nVersion = 3
        block.vtx.append(fundtx)
        # include the -1 CLTV in block
        block.vtx.append(spendtx)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        self.log.info("Test that blocks must now be at least version 4")
        tip = block.sha256
        block_time += 1
        block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time)
        block.nVersion = 3
        block.solve()

        with self.nodes[0].assert_debug_log(expected_msgs=[
                '{}, bad-version(0x00000003)'.format(block.hash)
        ]):
            self.nodes[0].p2p.send_and_ping(msg_block(block))
            assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
            self.nodes[0].p2p.sync_with_ping()

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

        fundtx = create_transaction(self.nodes[0], self.coinbase_txids[1],
                                    self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98)

        # The funding tx only has unexecuted bad CLTV, in scriptpubkey; this is
        # valid.
        self.nodes[0].p2p.send_and_ping(msg_tx(fundtx))
        assert fundtx.hash in self.nodes[0].getrawmempool()

        # Mine a block containing the funding transaction
        block.vtx.append(fundtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        # We show that this tx is invalid due to 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=[spendtx.serialize().hex()],
                                            allowhighfees=True))

        rejectedtx_signed = self.nodes[0].signrawtransactionwithwallet(
            ToHex(spendtx))

        # Couldn't complete signature due to CLTV
        assert rejectedtx_signed['errors'][0]['error'] == 'Negative locktime'

        tip = block.hash
        block_time += 1
        block = create_block(block.sha256, create_coinbase(CLTV_HEIGHT + 1),
                             block_time)
        block.nVersion = 4
        block.vtx.append(spendtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        with self.nodes[0].assert_debug_log(expected_msgs=[
                'ConnectBlock {} failed, blk-bad-inputs'.format(block.hash)
        ]):
            self.nodes[0].p2p.send_and_ping(msg_block(block))
            assert_equal(self.nodes[0].getbestblockhash(), tip)
            self.nodes[0].p2p.sync_with_ping()

        self.log.info(
            "Test that a version 4 block with a valid-according-to-CLTV transaction is accepted"
        )
        fundtx = create_transaction(self.nodes[0], self.coinbase_txids[2],
                                    self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98,
                                              CLTV_HEIGHT)

        # make sure sequence is nonfinal and locktime is good
        spendtx.vin[0].nSequence = 0xfffffffe
        spendtx.nLockTime = CLTV_HEIGHT

        # both transactions are fully valid
        self.nodes[0].sendrawtransaction(ToHex(fundtx))
        self.nodes[0].sendrawtransaction(ToHex(spendtx))

        # Modify the transactions in the block to be valid against CLTV
        block.vtx.pop(1)
        block.vtx.append(fundtx)
        block.vtx.append(spendtx)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is now valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)
    def next_block(self, number, spend=None, script=CScript(
            [OP_TRUE]), block_size=0, extra_txns=0):
        if self.tip is None:
            base_block_hash = self.genesis_hash
            block_time = int(time.time()) + 1
        else:
            base_block_hash = self.tip.sha256
            block_time = self.tip.nTime + 1
        # First create the coinbase
        height = self.block_heights[base_block_hash] + 1
        coinbase = create_coinbase(height)
        coinbase.rehash()
        if spend is None:
            # We need to have something to spend to fill the block.
            assert_equal(block_size, 0)
            block = create_block(base_block_hash, coinbase, block_time)
        else:
            # all but one satoshi to fees
            coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1
            coinbase.rehash()
            block = create_block(base_block_hash, coinbase, block_time)

            # Make sure we have plenty enough to spend going forward.
            spendable_outputs = deque([spend])

            def get_base_transaction():
                # Create the new transaction
                tx = CTransaction()
                # Spend from one of the spendable outputs
                spend = spendable_outputs.popleft()
                tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n)))
                # Add spendable outputs
                for i in range(4):
                    tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
                    spendable_outputs.append(PreviousSpendableOutput(tx, i))
                pad_tx(tx)
                return tx

            tx = get_base_transaction()

            # Make it the same format as transaction added for padding and save the size.
            # It's missing the padding output, so we add a constant to account
            # for it.
            tx.rehash()

            # If a specific script is required, add it.
            if script is not None:
                tx.vout.append(CTxOut(1, script))

            # Put some random data into the first transaction of the chain to
            # randomize ids.
            tx.vout.append(
                CTxOut(0, CScript([random.randint(0, 256), OP_RETURN])))

            # Add the transaction to the block
            self.add_transactions_to_block(block, [tx])

            # Add transaction until we reach the expected transaction count
            for _ in range(extra_txns):
                self.add_transactions_to_block(block, [get_base_transaction()])

            # If we have a block size requirement, just fill
            # the block until we get there
            current_block_size = len(block.serialize())
            overage_bytes = 0
            while current_block_size < block_size:
                # We will add a new transaction. That means the size of
                # the field enumerating how many transaction go in the block
                # may change.
                current_block_size -= len(ser_compact_size(len(block.vtx)))
                current_block_size += len(ser_compact_size(len(block.vtx) + 1))

                # Add padding to fill the block.
                left_to_fill = block_size - current_block_size

                # Don't go over the 1 mb limit for a txn
                if left_to_fill > 500000:
                    # Make sure we eat up non-divisible by 100 amounts quickly
                    # Also keep transaction less than 1 MB
                    left_to_fill = 500000 + left_to_fill % 100

                # Create the new transaction
                tx = get_base_transaction()
                pad_tx(tx, left_to_fill - overage_bytes)
                if len(tx.serialize()) + current_block_size > block_size:
                    # Our padding was too big try again
                    overage_bytes += 1
                    continue

                # Add the tx to the list of transactions to be included
                # in the block.
                self.add_transactions_to_block(block, [tx])
                current_block_size += len(tx.serialize())

            # Now that we added a bunch of transaction, we need to recompute
            # the merkle root.
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()

        # Check that the block size is what's expected
        if block_size > 0:
            assert_equal(len(block.serialize()), block_size)

        # Do PoW, which is cheap on regnet
        block.solve()
        self.tip = block
        self.block_heights[block.sha256] = height
        assert number not in self.blocks
        self.blocks[number] = block
        return block
Esempio n. 11
0
    def run_test(self):
        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 {} blocks".format(CLTV_HEIGHT - 2))
        self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2)
        self.nodeaddress = self.nodes[0].getnewaddress()

        self.log.info(
            "Test that an invalid-according-to-CLTV transaction can still appear in a block"
        )

        fundtx = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[0],
                                     self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98)

        tip = self.nodes[0].getbestblockhash()
        block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
        block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1),
                             block_time)
        block.nVersion = 3
        block.vtx.append(fundtx)
        # include the -1 CLTV in block
        block.vtx.append(spendtx)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        self.log.info("Test that blocks must now be at least version 4")
        tip = block.sha256
        block_time += 1
        block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time)
        block.nVersion = 3
        block.solve()
        self.nodes[0].p2p.send_and_ping(msg_block(block))
        assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)

        wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(),
                   lock=mininode_lock)
        with mininode_lock:
            assert_equal(self.nodes[0].p2p.last_message["reject"].code,
                         REJECT_OBSOLETE)
            assert_equal(self.nodes[0].p2p.last_message["reject"].reason,
                         b'bad-version(0x00000003)')
            assert_equal(self.nodes[0].p2p.last_message["reject"].data,
                         block.sha256)
            del self.nodes[0].p2p.last_message["reject"]

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

        fundtx = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[1],
                                     self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98)

        # The funding tx only has unexecuted bad CLTV, in scriptpubkey; this is valid.
        self.nodes[0].p2p.send_and_ping(msg_tx(fundtx))
        assert fundtx.hash in self.nodes[0].getrawmempool()

        # Mine a block containing the funding transaction
        block.vtx.append(fundtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)

        # We show that this tx is invalid due to 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=[spendtx.serialize().hex()],
                                            allowhighfees=True))

        rejectedtx_signed = self.nodes[0].signrawtransactionwithwallet(
            ToHex(spendtx))

        # Couldn't complete signature due to CLTV
        assert (rejectedtx_signed['errors'][0]['error'] == 'Negative locktime')

        tip = block.hash
        block_time += 1
        block = create_block(block.sha256, create_coinbase(CLTV_HEIGHT + 1),
                             block_time)
        block.nVersion = 4
        block.vtx.append(spendtx)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is invalid
        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'blk-bad-inputs')
            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"
        )
        fundtx = spend_from_coinbase(self.nodes[0], self.coinbase_blocks[2],
                                     self.nodeaddress, 49.99)
        fundtx, spendtx = cltv_lock_to_height(self.nodes[0], fundtx,
                                              self.nodeaddress, 49.98,
                                              CLTV_HEIGHT)

        # make sure sequence is nonfinal and locktime is good
        spendtx.vin[0].nSequence = 0xfffffffe
        spendtx.nLockTime = CLTV_HEIGHT

        # both transactions are fully valid
        self.nodes[0].sendrawtransaction(ToHex(fundtx))
        self.nodes[0].sendrawtransaction(ToHex(spendtx))

        # Modify the transactions in the block to be valid against CLTV
        block.vtx.pop(1)
        block.vtx.append(fundtx)
        block.vtx.append(spendtx)
        make_conform_to_ctor(block)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.solve()

        self.nodes[0].p2p.send_and_ping(msg_block(block))
        # This block is now valid
        assert_equal(self.nodes[0].getbestblockhash(), block.hash)
 def ordered_block(block_number, spend):
     b = block(block_number, spend=spend, tx_count=16)
     make_conform_to_ctor(b)
     update_block(block_number)
     return b
Esempio n. 13
0
    def run_test(self):
        # Add p2p connection to node0
        node = self.nodes[0]  # convenience reference to the node
        node.add_p2p_connection(P2PDataStore())

        best_block = node.getblock(node.getbestblockhash())
        tip = int(node.getbestblockhash(), 16)
        height = best_block["height"] + 1
        block_time = best_block["time"] + 1

        self.log.info("Create a new block with an anyone-can-spend coinbase")

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

        self.log.info("Mature the block.")
        node.generatetoaddress(100, node.get_deterministic_priv_key().address)

        best_block = node.getblock(node.getbestblockhash())
        tip = int(node.getbestblockhash(), 16)
        height = best_block["height"] + 1
        block_time = best_block["time"] + 1

        # Use merkle-root malleability to generate an invalid block with
        # same blockheader (CVE-2012-2459).
        # Manufacture a block with 3 transactions (coinbase, spend of prior
        # coinbase, spend of that spend).  Duplicate the 3rd transaction to
        # leave merkle root and blockheader unchanged but invalidate the block.
        # For more information on merkle-root malleability see
        # src/consensus/merkle.cpp.
        self.log.info("Test merkle root malleability.")

        block2 = create_block(tip, create_coinbase(height), block_time)
        block_time += 1

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

        block2.vtx.extend([tx1, tx2])
        block2.vtx = [block2.vtx[0]] + \
            sorted(block2.vtx[1:], key=lambda tx: tx.get_id())
        block2.hashMerkleRoot = block2.calc_merkle_root()
        block2.rehash()
        block2.solve()
        orig_hash = block2.sha256
        block2_orig = copy.deepcopy(block2)

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

        node.p2p.send_blocks_and_test([block2],
                                      node,
                                      success=False,
                                      reject_reason='bad-txns-duplicate')

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

        block2_dup = copy.deepcopy(block2_orig)
        block2_dup.vtx[2].vin.append(block2_dup.vtx[2].vin[0])
        block2_dup.vtx[2].rehash()
        make_conform_to_ctor(block2_dup)
        block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
        block2_dup.rehash()
        block2_dup.solve()
        node.p2p.send_blocks_and_test(
            [block2_dup],
            node,
            success=False,
            reject_reason='bad-txns-inputs-duplicate')

        self.log.info("Test very broken block.")

        block3 = create_block(tip, create_coinbase(height), block_time)
        block_time += 1
        block3.vtx[0].vout[0].nValue = 100 * COIN  # Too high!
        block3.vtx[0].sha256 = None
        block3.vtx[0].calc_sha256()
        block3.hashMerkleRoot = block3.calc_merkle_root()
        block3.rehash()
        block3.solve()

        node.p2p.send_blocks_and_test([block3],
                                      node,
                                      success=False,
                                      reject_reason='bad-cb-amount')

        # Complete testing of CVE-2012-2459 by sending the original block.
        # It should be accepted even though it has the same hash as the mutated
        # one.

        self.log.info("Test accepting original block after rejecting its"
                      " mutated version.")
        node.p2p.send_blocks_and_test([block2_orig],
                                      node,
                                      success=True,
                                      timeout=5)

        # Update tip info
        height += 1
        block_time += 1
        tip = int(block2_orig.hash, 16)

        # Complete testing of CVE-2018-17144, by checking for the inflation bug.
        # Create a block that spends the output of a tx in a previous block.
        block4 = create_block(tip, create_coinbase(height), block_time)
        tx3 = create_tx_with_script(tx2,
                                    0,
                                    script_sig=b'\x51',
                                    amount=50 * COIN)

        # Duplicates input
        tx3.vin.append(tx3.vin[0])
        tx3.rehash()
        block4.vtx.append(tx3)
        make_conform_to_ctor(block4)
        block4.hashMerkleRoot = block4.calc_merkle_root()
        block4.rehash()
        block4.solve()
        self.log.info("Test inflation by duplicating input")
        node.p2p.send_blocks_and_test(
            [block4],
            node,
            success=False,
            reject_reason='bad-txns-inputs-duplicate')
    def next_block(self,
                   number,
                   spend=None,
                   script=CScript([OP_TRUE]),
                   block_size=0,
                   extra_sigops=0):
        if self.tip == None:
            base_block_hash = self.genesis_hash
            block_time = int(time.time()) + 1
        else:
            base_block_hash = self.tip.sha256
            block_time = self.tip.nTime + 1
        # First create the coinbase
        height = self.block_heights[base_block_hash] + 1
        coinbase = create_coinbase(height)
        coinbase.rehash()
        if spend == None:
            # We need to have something to spend to fill the block.
            assert_equal(block_size, 0)
            block = create_block(base_block_hash, coinbase, block_time)
        else:
            # all but one satoshi to fees
            coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1
            coinbase.rehash()
            block = create_block(base_block_hash, coinbase, block_time)

            # Make sure we have plenty engough to spend going forward.
            spendable_outputs = deque([spend])

            def get_base_transaction():
                # Create the new transaction
                tx = CTransaction()
                # Spend from one of the spendable outputs
                spend = spendable_outputs.popleft()
                tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n)))
                # Add spendable outputs
                for i in range(4):
                    tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
                    spendable_outputs.append(PreviousSpendableOutput(tx, i))
                return tx

            tx = get_base_transaction()

            # Make it the same format as transaction added for padding and save the size.
            # It's missing the padding output, so we add a constant to account for it.
            tx.rehash()
            base_tx_size = len(tx.serialize()) + 18

            # If a specific script is required, add it.
            if script != None:
                tx.vout.append(CTxOut(1, script))

            # Put some random data into the first transaction of the chain to randomize ids.
            tx.vout.append(
                CTxOut(0, CScript([random.randint(0, 256), OP_RETURN])))

            # Add the transaction to the block
            self.add_transactions_to_block(block, [tx])

            # If we have a block size requirement, just fill
            # the block until we get there
            current_block_size = len(block.serialize())
            while current_block_size < block_size:
                # We will add a new transaction. That means the size of
                # the field enumerating how many transaction go in the block
                # may change.
                current_block_size -= len(ser_compact_size(len(block.vtx)))
                current_block_size += len(ser_compact_size(len(block.vtx) + 1))

                # Create the new transaction
                tx = get_base_transaction()

                # Add padding to fill the block.
                script_length = block_size - current_block_size - base_tx_size
                if script_length > 510000:
                    if script_length < 1000000:
                        # Make sure we don't find ourselves in a position where we
                        # need to generate a transaction smaller than what we expected.
                        script_length = script_length // 2
                    else:
                        script_length = 500000
                tx_sigops = min(extra_sigops, script_length,
                                MAX_TX_SIGOPS_COUNT)
                extra_sigops -= tx_sigops
                script_pad_len = script_length - tx_sigops
                script_output = CScript([b'\x00' * script_pad_len] +
                                        [OP_CHECKSIG] * tx_sigops)
                tx.vout.append(CTxOut(0, script_output))

                # Add the tx to the list of transactions to be included
                # in the block.
                self.add_transactions_to_block(block, [tx])
                current_block_size += len(tx.serialize())

            # Now that we added a bunch of transaction, we need to recompute
            # the merkle root.
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()

        # Check that the block size is what's expected
        if block_size > 0:
            assert_equal(len(block.serialize()), block_size)

        # Do PoW, which is cheap on regnet
        block.solve()
        self.tip = block
        self.block_heights[block.sha256] = height
        assert number not in self.blocks
        self.blocks[number] = block
        return block
Esempio n. 15
0
    def test_sequence(self):
        """
        Sequence zmq notifications give every blockhash and txhash in order
        of processing, regardless of IBD, re-orgs, etc.
        Format of messages:
        <32-byte hash>C :                 Blockhash connected
        <32-byte hash>D :                 Blockhash disconnected
        <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool
                                          for non-block inclusion reason
        <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool
        """
        self.log.info("Testing 'sequence' publisher")
        [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")])
        self.disconnect_nodes(0, 1)

        # Mempool sequence number starts at 1
        seq_num = 1

        # Generate 1 block in nodes[0] and receive all notifications
        dc_block = self.nodes[0].generatetoaddress(
            1, ADDRESS_ECREG_UNSPENDABLE)[0]

        # Note: We are not notified of any block transactions, coinbase or
        # mined
        assert_equal((self.nodes[0].getbestblockhash(), "C", None),
                     seq.receive_sequence())

        # Generate 2 blocks in nodes[1] to a different address to ensure
        # a chain split
        self.nodes[1].generatetoaddress(2, ADDRESS_ECREG_P2SH_OP_TRUE)

        # nodes[0] will reorg chain after connecting back nodes[1]
        self.connect_nodes(0, 1)

        # Then we receive all block (dis)connect notifications for the
        # 2 block reorg
        assert_equal((dc_block, "D", None), seq.receive_sequence())
        block_count = self.nodes[1].getblockcount()
        assert_equal((self.nodes[1].getblockhash(block_count - 1), "C", None),
                     seq.receive_sequence())
        assert_equal((self.nodes[1].getblockhash(block_count), "C", None),
                     seq.receive_sequence())

        # Rest of test requires wallet functionality
        if self.is_wallet_compiled():
            (block_hash, txid_to_be_replaced,
             replacement_txid) = self.create_conflicting_tx()
            self.log.info(
                "Testing sequence notifications with mempool sequence values")
            # Should receive the initially broadcasted txid.
            assert_equal((txid_to_be_replaced, "A", seq_num),
                         seq.receive_sequence())
            seq_num += 1

            self.log.info("Testing a tx removal notification")
            # Next we receive a notification for the transaction removal
            assert_equal((txid_to_be_replaced, "R", seq_num),
                         seq.receive_sequence())
            seq_num += 1
            # Then we see the block notification
            assert_equal((block_hash, "C", None), seq.receive_sequence())
            # There is no sequence notification for the transaction that was
            # never in node0's mempool, but it can be found in the block.
            assert replacement_txid in self.nodes[0].getblock(block_hash)["tx"]

            self.log.info("Wait for tx from second node")
            payment_txid = self.nodes[1].sendtoaddress(
                address=self.nodes[0].getnewaddress(), amount=5_000_000)
            self.sync_all()
            assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
            seq_num += 1

            # Doesn't get published when mined, make a block and tx to "flush"
            # the possibility though the mempool sequence number does go up by
            # the number of transactions removed from the mempool by the block
            # mining it.
            mempool_size = len(self.nodes[0].getrawmempool())
            c_block = self.nodes[0].generatetoaddress(
                1, ADDRESS_ECREG_UNSPENDABLE)[0]
            self.sync_all()
            # Make sure the number of mined transactions matches the number of
            # txs out of mempool
            mempool_size_delta = mempool_size - \
                len(self.nodes[0].getrawmempool())
            assert_equal(
                len(self.nodes[0].getblock(c_block)["tx"]) - 1,
                mempool_size_delta)
            seq_num += mempool_size_delta
            payment_txid_2 = self.nodes[1].sendtoaddress(
                self.nodes[0].getnewaddress(), 1_000_000)
            self.sync_all()
            assert_equal((c_block, "C", None), seq.receive_sequence())
            assert_equal((payment_txid_2, "A", seq_num),
                         seq.receive_sequence())
            seq_num += 1

            # Spot check getrawmempool results that they only show up when
            # asked for
            assert isinstance(self.nodes[0].getrawmempool(), list)
            assert isinstance(
                self.nodes[0].getrawmempool(mempool_sequence=False), list)
            assert "mempool_sequence" not in self.nodes[0].getrawmempool(
                verbose=True)
            assert_raises_rpc_error(
                -8, "Verbose results cannot contain mempool sequence values.",
                self.nodes[0].getrawmempool, True, True)
            assert_equal(
                self.nodes[0].getrawmempool(
                    mempool_sequence=True)["mempool_sequence"], seq_num)

            self.log.info("Testing reorg notifications")
            # Manually invalidate the last block to test mempool re-entry
            # N.B. This part could be made more lenient in exact ordering
            # since it greatly depends on inner-workings of blocks/mempool
            # during "deep" re-orgs. Probably should "re-construct"
            # blockchain/mempool state from notifications instead.
            block_count = self.nodes[0].getblockcount()
            best_hash = self.nodes[0].getbestblockhash()
            self.nodes[0].invalidateblock(best_hash)
            # Bit of room to make sure transaction things happened
            sleep(2)

            # Make sure getrawmempool mempool_sequence results aren't "queued"
            # but immediately reflective of the time they were gathered.
            assert self.nodes[0].getrawmempool(
                mempool_sequence=True)["mempool_sequence"] > seq_num

            assert_equal((best_hash, "D", None), seq.receive_sequence())
            assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
            seq_num += 1

            # Other things may happen but aren't wallet-deterministic so we
            # don't test for them currently
            self.nodes[0].reconsiderblock(best_hash)
            self.nodes[1].generatetoaddress(1, ADDRESS_ECREG_UNSPENDABLE)
            self.sync_all()

            self.log.info("Evict mempool transaction by block conflict")
            orig_txid = self.nodes[0].sendtoaddress(
                address=self.nodes[0].getnewaddress(), amount=1_000_000)

            # More to be simply mined
            more_tx = []
            for _ in range(5):
                more_tx.append(self.nodes[0].sendtoaddress(
                    self.nodes[0].getnewaddress(), 100_000))

            raw_tx = self.nodes[0].getrawtransaction(orig_txid)
            block = create_block(
                int(self.nodes[0].getbestblockhash(), 16),
                create_coinbase(self.nodes[0].getblockcount() + 1))
            tx = FromHex(CTransaction(), raw_tx)
            block.vtx.append(tx)
            for txid in more_tx:
                tx = FromHex(CTransaction(),
                             self.nodes[0].getrawtransaction(txid))
                block.vtx.append(tx)
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            assert_equal(self.nodes[0].submitblock(block.serialize().hex()),
                         None)
            tip = self.nodes[0].getbestblockhash()
            assert_equal(int(tip, 16), block.sha256)
            orig_txid_2 = self.nodes[0].sendtoaddress(
                address=self.nodes[0].getnewaddress(), amount=1_000_000)

            # Flush old notifications until evicted tx original entry
            (hash_str, label, mempool_seq) = seq.receive_sequence()
            while hash_str != orig_txid:
                (hash_str, label, mempool_seq) = seq.receive_sequence()
            mempool_seq += 1

            # Added original tx
            assert_equal(label, "A")
            # More transactions to be simply mined
            for i in range(len(more_tx)):
                assert_equal((more_tx[i], "A", mempool_seq),
                             seq.receive_sequence())
                mempool_seq += 1

            # Removed RBF tests

            mempool_seq += 1
            assert_equal((tip, "C", None), seq.receive_sequence())
            mempool_seq += len(more_tx)
            # Last tx
            assert_equal((orig_txid_2, "A", mempool_seq),
                         seq.receive_sequence())
            mempool_seq += 1
            self.nodes[0].generatetoaddress(1, ADDRESS_ECREG_UNSPENDABLE)
            # want to make sure we didn't break "consensus" for other tests
            self.sync_all()
Esempio n. 16
0
 def update_block(block: CBlock,
                  new_transactions: Sequence[CTransaction]):
     block.vtx.extend(new_transactions)
     make_conform_to_ctor(block)
     block.hashMerkleRoot = block.calc_merkle_root()
     block.solve()