def SegwitVersion1SignatureHash_legacy(script, txTo, inIdx, hashtype, amount):
    """
    This method is identical to the regular `SegwitVersion1SignatureHash`
    method, but without support for SIGHASH_RANGEPROOF.
    So basically it's the old version of the method from before the
    new sighash flag was added.
    """

    hashPrevouts = 0
    hashSequence = 0
    hashIssuance = 0
    hashOutputs = 0

    if not (hashtype & SIGHASH_ANYONECANPAY):
        serialize_prevouts = bytes()
        for i in txTo.vin:
            serialize_prevouts += i.prevout.serialize()
        hashPrevouts = uint256_from_str(hash256(serialize_prevouts))

    if (not (hashtype & SIGHASH_ANYONECANPAY)
            and (hashtype & 0x1f) != SIGHASH_SINGLE
            and (hashtype & 0x1f) != SIGHASH_NONE):
        serialize_sequence = bytes()
        for i in txTo.vin:
            serialize_sequence += struct.pack("<I", i.nSequence)
        hashSequence = uint256_from_str(hash256(serialize_sequence))

    if not (hashtype & SIGHASH_ANYONECANPAY):
        serialize_issuance = bytes()
        # TODO actually serialize issuances
        for _ in txTo.vin:
            serialize_issuance += b'\x00'
        hashIssuance = uint256_from_str(hash256(serialize_issuance))

    if ((hashtype & 0x1f) != SIGHASH_SINGLE
            and (hashtype & 0x1f) != SIGHASH_NONE):
        serialize_outputs = bytes()
        for o in txTo.vout:
            serialize_outputs += o.serialize()
        hashOutputs = uint256_from_str(hash256(serialize_outputs))
    elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
        serialize_outputs = txTo.vout[inIdx].serialize()
        hashOutputs = uint256_from_str(hash256(serialize_outputs))

    ss = bytes()
    ss += struct.pack("<i", txTo.nVersion)
    ss += ser_uint256(hashPrevouts)
    ss += ser_uint256(hashSequence)
    ss += ser_uint256(hashIssuance)
    ss += txTo.vin[inIdx].prevout.serialize()
    ss += ser_string(script)
    ss += amount.serialize()
    ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
    ss += ser_uint256(hashOutputs)
    ss += struct.pack("<i", txTo.nLockTime)
    ss += struct.pack("<I", hashtype)

    return hash256(ss)
Exemple #2
0
 def serializeblock_err(self, block):
     r = b""
     r += struct.pack("<i", block.nVersion)
     r += ser_uint256(block.hashPrevBlock)
     r += ser_uint256(block.hashMerkleRoot)
     r += ser_uint256(block.hashImMerkleRoot)
     r += struct.pack("<I", block.nTime)
     r += struct.pack("B", block.xfieldType)
     r += block.xfield
     r += ser_string(block.proof)
     r += ser_vector(block.vtx)
     return r
Exemple #3
0
 def createblockproof(self, block, signblockprivkey):
     # create block proof with xField for all xfieldTypes
     r = b""
     r += struct.pack("<i", block.nVersion)
     r += ser_uint256(block.hashPrevBlock)
     r += ser_uint256(block.hashMerkleRoot)
     r += ser_uint256(block.hashImMerkleRoot)
     r += struct.pack("<I", block.nTime)
     r += struct.pack("B", block.xfieldType)
     r += ser_string(block.xfield)
     sighash = hash256(r)
     block.proof = bytearray(self.signKey.sign(sighash))
     block.rehash()
Exemple #4
0
def serialize_addrman(
    *,
    format=1,
    lowest_compatible=4,
    net_magic="regtest",
    bucket_key=1,
    len_new=None,
    len_tried=None,
    mock_checksum=None,
):
    new = []
    tried = []
    INCOMPATIBILITY_BASE = 32
    r = MAGIC_BYTES[net_magic]
    r += struct.pack("B", format)
    r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible)
    r += ser_uint256(bucket_key)
    r += struct.pack("i", len_new or len(new))
    r += struct.pack("i", len_tried or len(tried))
    ADDRMAN_NEW_BUCKET_COUNT = 1 << 10
    r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30))
    for _ in range(ADDRMAN_NEW_BUCKET_COUNT):
        r += struct.pack("i", 0)
    checksum = hash256(r)
    r += mock_checksum or checksum
    return r
Exemple #5
0
    def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList,
                           expectedDeleted, expectedUpdated):
        d = self.test_getmnlistdiff_base(baseBlockHash, blockHash)

        # Assert that the deletedMNs and mnList fields are what we expected
        assert_equal(set(d.deletedMNs),
                     set([int(e, 16) for e in expectedDeleted]))
        assert_equal(set([e.proRegTxHash for e in d.mnList]),
                     set(int(e, 16) for e in expectedUpdated))

        # Build a new list based on the old list and the info from the diff
        newMNList = baseMNList.copy()
        for e in d.deletedMNs:
            newMNList.pop(format(e, '064x'))
        for e in d.mnList:
            newMNList[format(e.proRegTxHash, '064x')] = e

        # Verify that the merkle root matches what we locally calculate
        hashes = []
        for mn in sorted(newMNList.values(),
                         key=lambda mn: ser_uint256(mn.proRegTxHash)):
            hashes.append(hash256(mn.serialize()))
        merkleRoot = CBlock.get_merkle_root(hashes)
        assert_equal(merkleRoot, d.merkleRootMNList)

        return newMNList
Exemple #6
0
    def assert_tx_format_also_signed(self, utxo, segwit):
        raw = self.nodes[0].createrawtransaction([{
            "txid": utxo["txid"],
            "vout": utxo["vout"]
        }], [{
            self.unknown_addr: "49.9"
        }, {
            "fee": "0.1"
        }])

        unsigned_decoded = self.nodes[0].decoderawtransaction(raw)
        assert_equal(len(unsigned_decoded["vin"]), 1)
        assert ('txinwitness' not in unsigned_decoded["vin"][0])

        # Cross-check python serialization
        tx = CTransaction()
        tx.deserialize(BytesIO(hex_str_to_bytes(raw)))
        assert_equal(tx.vin[0].prevout.hash, int("0x" + utxo["txid"], 0))
        assert_equal(len(tx.vin), len(unsigned_decoded["vin"]))
        assert_equal(len(tx.vout), len(unsigned_decoded["vout"]))
        # assert re-encoding
        serialized = bytes_to_hex_str(tx.serialize())
        assert_equal(serialized, raw)

        # Now sign and repeat tests
        signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"]
        signed_decoded = self.nodes[0].decoderawtransaction(signed_raw)
        assert_equal(len(signed_decoded["vin"]), 1)
        assert (("txinwitness" in signed_decoded["vin"][0]) == segwit)

        # Cross-check python serialization
        tx = CTransaction()
        tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw)))
        assert_equal(tx.vin[0].prevout.hash, int("0x" + utxo["txid"], 0))
        assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig),
                     signed_decoded["vin"][0]["scriptSig"]["hex"])
        # test witness
        if segwit:
            wit_decoded = signed_decoded["vin"][0]["txinwitness"]
            for i in range(len(wit_decoded)):
                assert_equal(
                    bytes_to_hex_str(
                        tx.wit.vtxinwit[0].scriptWitness.stack[i]),
                    wit_decoded[i])
        # assert re-encoding
        serialized = bytes_to_hex_str(tx.serialize())
        assert_equal(serialized, signed_raw)

        txid = self.nodes[0].sendrawtransaction(serialized)
        nodetx = self.nodes[0].getrawtransaction(txid, 1)
        assert_equal(nodetx["txid"], tx.rehash())
        # cross-check wtxid report from node
        wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1])
        assert_equal(nodetx["wtxid"], wtxid)
        assert_equal(nodetx["hash"], wtxid)

        # witness hash stuff
        assert_equal(nodetx["withash"], tx.calc_witness_hash())
        return (txid, wtxid)
Exemple #7
0
def to_jsonable(obj: Any) -> Any:
    if hasattr(obj, "__dict__"):
        return obj.__dict__
    elif hasattr(obj, "__slots__"):
        ret = {}  # type: Any
        for slot in obj.__slots__:
            val = getattr(obj, slot, None)
            if slot in HASH_INTS and isinstance(val, int):
                ret[slot] = ser_uint256(val).hex()
            elif slot in HASH_INT_VECTORS and isinstance(val[0], int):
                ret[slot] = [ser_uint256(a).hex() for a in val]
            else:
                ret[slot] = to_jsonable(val)
        return ret
    elif isinstance(obj, list):
        return [to_jsonable(a) for a in obj]
    elif isinstance(obj, bytes):
        return obj.hex()
    else:
        return obj
Exemple #8
0
    def test_compactblock_reconstruction_multiple_peers(
            self, stalling_peer, delivery_peer):
        node = self.nodes[0]
        assert len(self.utxos)

        def announce_cmpct_block(node, peer):
            utxo = self.utxos.pop(0)
            block = self.build_block_with_transactions(node, utxo, 5)

            cmpct_block = HeaderAndShortIDs()
            cmpct_block.initialize_from_block(block)
            msg = msg_cmpctblock(cmpct_block.to_p2p())
            peer.send_and_ping(msg)
            with mininode_lock:
                assert "getblocktxn" in peer.last_message
            return block, cmpct_block

        block, cmpct_block = announce_cmpct_block(node, stalling_peer)

        for tx in block.vtx[1:]:
            delivery_peer.send_message(msg_tx(tx))
        delivery_peer.sync_with_ping()
        mempool = node.getrawmempool()
        for tx in block.vtx[1:]:
            assert tx.hash in mempool

        delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
        assert_equal(int(node.getbestblockhash(), 16), block.sha256)

        self.utxos.append(
            [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])

        # Now test that delivering an invalid compact block won't break relay

        block, cmpct_block = announce_cmpct_block(node, stalling_peer)
        for tx in block.vtx[1:]:
            delivery_peer.send_message(msg_tx(tx))
        delivery_peer.sync_with_ping()

        cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()]
        cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [
            ser_uint256(0)
        ]

        cmpct_block.use_witness = True
        delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
        assert int(node.getbestblockhash(), 16) != block.sha256

        msg = msg_no_witness_blocktxn()
        msg.block_transactions.blockhash = block.sha256
        msg.block_transactions.transactions = block.vtx[1:]
        stalling_peer.send_and_ping(msg)
        assert_equal(int(node.getbestblockhash(), 16), block.sha256)
Exemple #9
0
    def get_utxo(self, fund_tx, idx):
        spent = None
        # Coin selection
        for utxo in self.nodes[0].listunspent():
            if utxo["txid"] == ser_uint256(
                    fund_tx.vin[idx].prevout.hash)[::-1].hex(
                    ) and utxo["vout"] == fund_tx.vin[idx].prevout.n:
                spent = utxo

        assert (spent is not None)
        assert (len(fund_tx.vin) == 2)
        return spent
    def assert_tx_format_also_signed(self, utxo, segwit):
        raw = self.nodes[0].createrawtransaction(
            [{"txid": utxo["txid"], "vout": utxo["vout"]}],
            [{self.unknown_addr: "49.9"}, {"fee": "0.1"}]
        )

        unsigned_decoded = self.nodes[0].decoderawtransaction(raw)
        assert_equal(len(unsigned_decoded["vin"]), 1)
        assert('txinwitness' not in unsigned_decoded["vin"][0])

        # Cross-check python serialization
        tx = CTransaction()
        tx.deserialize(BytesIO(hex_str_to_bytes(raw)))
        assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0))
        assert_equal(len(tx.vin), len(unsigned_decoded["vin"]))
        assert_equal(len(tx.vout), len(unsigned_decoded["vout"]))
        # assert re-encoding
        serialized = bytes_to_hex_str(tx.serialize())
        assert_equal(serialized, raw)

        # Now sign and repeat tests
        signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"]
        signed_decoded = self.nodes[0].decoderawtransaction(signed_raw)
        assert_equal(len(signed_decoded["vin"]), 1)
        assert(("txinwitness" in signed_decoded["vin"][0]) == segwit)

        # Cross-check python serialization
        tx = CTransaction()
        tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw)))
        assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0))
        assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig), signed_decoded["vin"][0]["scriptSig"]["hex"])
        # test witness
        if segwit:
            wit_decoded = signed_decoded["vin"][0]["txinwitness"]
            for i in range(len(wit_decoded)):
                assert_equal(bytes_to_hex_str(tx.wit.vtxinwit[0].scriptWitness.stack[i]), wit_decoded[i])
        # assert re-encoding
        serialized = bytes_to_hex_str(tx.serialize())
        assert_equal(serialized, signed_raw)

        txid = self.nodes[0].sendrawtransaction(serialized)
        nodetx = self.nodes[0].getrawtransaction(txid, 1)
        assert_equal(nodetx["txid"], tx.rehash())
        # cross-check wtxid report from node
        wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1])
        assert_equal(nodetx["wtxid"], wtxid)
        assert_equal(nodetx["hash"], wtxid)

        # witness hash stuff
        assert_equal(nodetx["withash"], tx.calc_witness_hash())
        return (txid, wtxid)
    def test_compactblock_reconstruction_multiple_peers(self, stalling_peer, delivery_peer):
        node = self.nodes[0]
        assert len(self.utxos)

        def announce_cmpct_block(node, peer):
            utxo = self.utxos.pop(0)
            block = self.build_block_with_transactions(node, utxo, 5)

            cmpct_block = HeaderAndShortIDs()
            cmpct_block.initialize_from_block(block)
            msg = msg_cmpctblock(cmpct_block.to_p2p())
            peer.send_and_ping(msg)
            with mininode_lock:
                assert "getblocktxn" in peer.last_message
            return block, cmpct_block

        block, cmpct_block = announce_cmpct_block(node, stalling_peer)

        for tx in block.vtx[1:]:
            delivery_peer.send_message(msg_tx(tx))
        delivery_peer.sync_with_ping()
        mempool = node.getrawmempool()
        for tx in block.vtx[1:]:
            assert tx.hash in mempool

        delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
        assert_equal(int(node.getbestblockhash(), 16), block.sha256)

        self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])

        # Now test that delivering an invalid compact block won't break relay

        block, cmpct_block = announce_cmpct_block(node, stalling_peer)
        for tx in block.vtx[1:]:
            delivery_peer.send_message(msg_tx(tx))
        delivery_peer.sync_with_ping()

        cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()]
        cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]

        cmpct_block.use_witness = True
        delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p()))
        assert int(node.getbestblockhash(), 16) != block.sha256

        msg = msg_blocktxn()
        msg.block_transactions.blockhash = block.sha256
        msg.block_transactions.transactions = block.vtx[1:]
        stalling_peer.send_and_ping(msg)
        assert_equal(int(node.getbestblockhash(), 16), block.sha256)
Exemple #12
0
def serialize_addrman(*, format=1, lowest_compatible=3):
    new = []
    tried = []
    INCOMPATIBILITY_BASE = 32
    r = MAGIC_BYTES["regtest"]
    r += struct.pack("B", format)
    r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible)
    r += ser_uint256(1)
    r += struct.pack("i", len(new))
    r += struct.pack("i", len(tried))
    ADDRMAN_NEW_BUCKET_COUNT = 1 << 10
    r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30))
    for _ in range(ADDRMAN_NEW_BUCKET_COUNT):
        r += struct.pack("i", 0)
    checksum = hash256(r)
    r += checksum
    return r
    def _check_algorithm_sanity(self):
        ctx = ContextInfoContainer()
        ctx.height = 1337
        ctx.keystone1 = "010203"
        ctx.keystone2 = "040506"
        assert_equal(
            ctx.getHash().hex(),
            "db35aad09a65b667a6c9e09cbd47b8d6b378b9ec705db604a4d5cd489afd2bc6")

        txroot = uint256_from_str(
            bytes.fromhex(
                "bf9fb4901a0d8fc9b0d3bf38546191f77a3f2ea5d543546aac0574290c0a9e83"
            ))
        poproot = EMPTY_POPDATA_ROOT_V1

        tlmr = _calculateTopLevelMerkleRoot(txRoot=txroot,
                                            popDataRoot=poproot,
                                            ctx=ctx)

        tlmr_hex = ser_uint256(tlmr).hex()
        assert_equal(
            tlmr_hex,
            "700c1abb69dd1899796b4cafa81c0eefa7b7d0c5aaa4b2bcb67713b2918edb52")
def compute_last_header(prev_header, hashes):
    """Compute the last filter header from a starting header and a sequence of filter hashes."""
    header = ser_uint256(prev_header)
    for filter_hash in hashes:
        header = hash256(ser_uint256(filter_hash) + header)
    return uint256_from_str(header)
Exemple #15
0
    def commits_test(self, node):
        def check_headers(number):
            wait_until(lambda: node.getblockchaininfo()['headers'] == number, timeout=5)

        def check_reject(err, block):
            wait_until(lambda: node.p2p.has_reject(err, block), timeout=5)

        def getbestblockhash():
            return int(node.getbestblockhash(), 16)

        def make_block(prev=None, secret=None):
            if secret is None:
                secret = "default"
            coinbase_key = CECKey()
            coinbase_key.set_secretbytes(bytes(secret, "utf-8"))
            coinbase_pubkey = coinbase_key.get_pubkey()
            if prev is None:
                block_base_hash = getbestblockhash()
                block_time = int(time.time()) + 1
            else:
                block_base_hash = prev.sha256
                block_time = prev.nTime + 1
            height = prev.height + 1 if prev else 1
            snapshot_hash = 0
            stake = self.nodes[0].listunspent()[0]
            coinbase = create_coinbase(height, stake, snapshot_hash, coinbase_pubkey)
            coinbase.rehash()
            b = create_block(block_base_hash, coinbase, block_time)
            b.solve()
            b.height = height
            return b

        def make_commits_msg(blocks):
            msg = msg_commits(0)
            for b in blocks:
                hc = HeaderAndCommits()
                hc.header = CBlockHeader(b)
                msg.data += [hc]
            return msg

        def send_commits(blocks):
            node.p2p.reset_messages()
            node.p2p.send_message(make_commits_msg(blocks))

        chain = []

        def generate(n):
            tip = chain[-1] if len(chain) > 0 else None
            for i in range(0, n):
                tip = make_block(tip)
                chain.append(tip)

        check_headers(0) # initial state of the node

        # generate 10 blocks and send commits
        generate(10)
        send_commits(chain)
        check_headers(10) # node accepted 10 headers

        # send same commits again
        send_commits(chain)
        check_headers(10)

        # send last 5 commits
        send_commits(chain[-5:])
        check_headers(10)

        # generate next 10 blocks, try to send commits starting from 2nd block
        generate(10)
        send_commits(chain[11:])
        check_reject(b'prev-blk-not-found', 0)  # node rejected orphan headers
        check_headers(10) # must keep old amount of headers

        # send correct commits
        send_commits(chain[10:])
        check_headers(20) # node accepted headers

        # generate next 10 blocks, send whole chain
        generate(10)
        send_commits(chain)
        check_headers(30) # node accepted headers

        # generate next 10 blocks, fool commit in one of them, send them
        generate(10)
        msg = make_commits_msg(chain[-10:])
        malicious_block = copy.deepcopy(chain[-1])
        msg.data[-1].commits = malicious_block.vtx # fool commits with coinbase tx
        tx = malicious_block.vtx[0]
        tx.calc_sha256()
        hashes = [ser_uint256(tx.sha256)]
        malicious_block.hash_finalizer_commits_merkle_root = CBlock.get_merkle_root(hashes)
        malicious_block.rehash()
        msg.data[-1].header.hash_finalizer_commits_merkle_root = malicious_block.hash_finalizer_commits_merkle_root
        node.p2p.send_message(msg)
        check_reject(b'bad-non-commit', malicious_block.sha256) # node rejected commits because of non-commit transaction
        check_headers(30) # must keep old amount of headers

        # send commits with bad merkle root
        msg = make_commits_msg(chain[-10:])
        malicious_block = copy.deepcopy(chain[-2])
        malicious_block.hash_finalizer_commits_merkle_root = 42
        malicious_block.rehash()
        msg.data[-2].header.hash_finalizer_commits_merkle_root = malicious_block.hash_finalizer_commits_merkle_root
        node.p2p.send_message(msg)
        check_reject(b'bad-finalizer-commits-merkle-root', malicious_block.sha256) # node rejected commits because of bad commits merkle root
        check_headers(30) # must keep old amount of headers
Exemple #16
0
 def calcsnapshothash(inputs, outputs, *prev_hash):
     sm = bytes_to_hex_str(ser_uint256(0))
     cw = bytes_to_hex_str(ser_uint256(2))
     return self.nodes[0].calcsnapshothash(ser_utxos(inputs),
                                           ser_utxos(outputs), sm, cw,
                                           *prev_hash)
Exemple #17
0
    def test_witness_block_size(self):
        # TODO: Test that non-witness carrying blocks can't exceed 1MB
        # Skipping this test for now; this is covered in p2p-fullblocktest.py

        # Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB.
        block = self.build_next_block()

        assert len(self.utxo) > 0

        # Create a P2WSH transaction.
        # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE.
        # This should give us plenty of room to tweak the spending tx's
        # virtual size.
        NUM_DROPS = 200  # 201 max ops per script!
        NUM_OUTPUTS = 50

        witness_program = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE])
        witness_hash = uint256_from_str(sha256(witness_program))
        script_pubkey = CScript([OP_0, ser_uint256(witness_hash)])

        prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n)
        value = self.utxo[0].nValue

        parent_tx = CTransaction()
        parent_tx.vin.append(CTxIn(prevout, b""))
        child_value = int(value / NUM_OUTPUTS)
        for i in range(NUM_OUTPUTS):
            parent_tx.vout.append(CTxOut(child_value, script_pubkey))
        parent_tx.vout[0].nValue -= 50000
        assert parent_tx.vout[0].nValue > 0
        parent_tx.rehash()

        filler_size = 3150
        child_tx = CTransaction()
        for i in range(NUM_OUTPUTS):
            child_tx.vin.append(CTxIn(COutPoint(parent_tx.sha256, i), b""))
        child_tx.vout = [CTxOut(value - 100000, CScript([OP_TRUE]))]
        for i in range(NUM_OUTPUTS):
            child_tx.wit.vtxinwit.append(CTxInWitness())
            child_tx.wit.vtxinwit[-1].scriptWitness.stack = [
                b'a' * filler_size
            ] * (2 * NUM_DROPS) + [witness_program]
        child_tx.rehash()
        self.update_witness_block_with_transactions(block,
                                                    [parent_tx, child_tx])

        vsize = get_virtual_size(block)
        assert_greater_than(MAX_BLOCK_BASE_SIZE, vsize)
        additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize) * 4
        i = 0
        while additional_bytes > 0:
            # Add some more bytes to each input until we hit MAX_BLOCK_BASE_SIZE+1
            extra_bytes = min(additional_bytes + 1, 55)
            block.vtx[-1].wit.vtxinwit[int(
                i / (2 * NUM_DROPS))].scriptWitness.stack[
                    i % (2 * NUM_DROPS)] = b'a' * (filler_size + extra_bytes)
            additional_bytes -= extra_bytes
            i += 1

        block.vtx[0].vout.pop()  # Remove old commitment
        add_witness_commitment(block)
        block.solve()
        vsize = get_virtual_size(block)
        assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1)
        # Make sure that our test case would exceed the old max-network-message
        # limit
        assert len(block.serialize()) > 2 * 1024 * 1024

        test_witness_block(self.nodes[0],
                           self.test_node,
                           block,
                           accepted=False)

        # Now resize the second transaction to make the block fit.
        cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0])
        block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (
            cur_length - 1)
        block.vtx[0].vout.pop()
        add_witness_commitment(block)
        block.solve()
        assert get_virtual_size(block) == MAX_BLOCK_BASE_SIZE

        test_witness_block(self.nodes[0], self.test_node, block, accepted=True)

        # Update available utxo's
        self.utxo.pop(0)
        self.utxo.append(
            UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue))
Exemple #18
0
    def run_test(self):
        node = self.nodes[0]
        self.wallet = MiniWallet(node)
        self.mine_chain()

        def assert_submitblock(block, result_str_1, result_str_2=None):
            block.solve()
            result_str_2 = result_str_2 or 'duplicate-invalid'
            assert_equal(result_str_1,
                         node.submitblock(hexdata=block.serialize().hex()))
            assert_equal(result_str_2,
                         node.submitblock(hexdata=block.serialize().hex()))

        self.log.info('getmininginfo')
        mining_info = node.getmininginfo()
        assert_equal(mining_info['blocks'], 200)
        assert_equal(mining_info['chain'], self.chain)
        assert 'currentblocktx' not in mining_info
        assert 'currentblockweight' not in mining_info
        assert 'currentblocksize' not in mining_info
        assert_equal(mining_info['difficulty'],
                     Decimal('4.656542373906925E-10'))
        assert_equal(mining_info['networkhashps'],
                     Decimal('0.003333333333333334'))
        assert_equal(mining_info['pooledtx'], 0)

        self.log.info("getblocktemplate: Test default witness commitment")
        txid = int(self.wallet.send_self_transfer(from_node=node)['wtxid'], 16)
        tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)

        # Check that default_witness_commitment is present.
        assert 'default_witness_commitment' in tmpl
        witness_commitment = tmpl['default_witness_commitment']

        # Check that default_witness_commitment is correct.
        witness_root = CBlock.get_merkle_root(
            [ser_uint256(0), ser_uint256(txid)])
        script = get_witness_script(witness_root, 0)
        assert_equal(witness_commitment, script.hex())

        # Mine a block to leave initial block download and clear the mempool
        self.generatetoaddress(node, 1,
                               node.get_deterministic_priv_key().address)
        tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
        self.log.info("getblocktemplate: Test capability advertised")
        assert 'proposal' in tmpl['capabilities']
        assert 'coinbasetxn' not in tmpl

        next_height = int(tmpl["height"])
        coinbase_tx = create_coinbase(height=next_height)
        # sequence numbers must not be max for nLockTime to have effect
        coinbase_tx.vin[0].nSequence = 2**32 - 2
        coinbase_tx.rehash()

        block = CBlock()
        block.nVersion = tmpl["version"]
        block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
        block.nTime = tmpl["curtime"]
        block.nBits = int(tmpl["bits"], 16)
        block.nNonce = 0
        block.vtx = [coinbase_tx]

        self.log.info("getblocktemplate: segwit rule must be set")
        assert_raises_rpc_error(
            -8, "getblocktemplate must be called with the segwit rule set",
            node.getblocktemplate)

        self.log.info("getblocktemplate: Test valid block")
        assert_template(node, block, None)

        self.log.info("submitblock: Test block decode failure")
        assert_raises_rpc_error(-22, "Block decode failed", node.submitblock,
                                block.serialize()[:-15].hex())

        self.log.info(
            "getblocktemplate: Test bad input hash for coinbase transaction")
        bad_block = copy.deepcopy(block)
        bad_block.vtx[0].vin[0].prevout.hash += 1
        bad_block.vtx[0].rehash()
        assert_template(node, bad_block, 'bad-cb-missing')

        self.log.info("submitblock: Test invalid coinbase transaction")
        assert_raises_rpc_error(-22, "Block does not start with a coinbase",
                                node.submitblock,
                                bad_block.serialize().hex())

        self.log.info("getblocktemplate: Test truncated final transaction")
        assert_raises_rpc_error(
            -22, "Block decode failed", node.getblocktemplate, {
                'data': block.serialize()[:-1].hex(),
                'mode': 'proposal',
                'rules': ['segwit'],
            })

        self.log.info("getblocktemplate: Test duplicate transaction")
        bad_block = copy.deepcopy(block)
        bad_block.vtx.append(bad_block.vtx[0])
        assert_template(node, bad_block, 'bad-txns-duplicate')
        assert_submitblock(bad_block, 'bad-txns-duplicate',
                           'bad-txns-duplicate')

        self.log.info("getblocktemplate: Test invalid transaction")
        bad_block = copy.deepcopy(block)
        bad_tx = copy.deepcopy(bad_block.vtx[0])
        bad_tx.vin[0].prevout.hash = 255
        bad_tx.rehash()
        bad_block.vtx.append(bad_tx)
        assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
        assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent')

        self.log.info("getblocktemplate: Test nonfinal transaction")
        bad_block = copy.deepcopy(block)
        bad_block.vtx[0].nLockTime = 2**32 - 1
        bad_block.vtx[0].rehash()
        assert_template(node, bad_block, 'bad-txns-nonfinal')
        assert_submitblock(bad_block, 'bad-txns-nonfinal')

        self.log.info("getblocktemplate: Test bad tx count")
        # The tx count is immediately after the block header
        bad_block_sn = bytearray(block.serialize())
        assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
        bad_block_sn[BLOCK_HEADER_SIZE] += 1
        assert_raises_rpc_error(-22, "Block decode failed",
                                node.getblocktemplate, {
                                    'data': bad_block_sn.hex(),
                                    'mode': 'proposal',
                                    'rules': ['segwit'],
                                })

        self.log.info("getblocktemplate: Test bad bits")
        bad_block = copy.deepcopy(block)
        bad_block.nBits = 469762303  # impossible in the real world
        assert_template(node, bad_block, 'bad-diffbits')

        self.log.info("getblocktemplate: Test bad merkle root")
        bad_block = copy.deepcopy(block)
        bad_block.hashMerkleRoot += 1
        assert_template(node, bad_block, 'bad-txnmrklroot', False)
        assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot')

        self.log.info("getblocktemplate: Test bad timestamps")
        bad_block = copy.deepcopy(block)
        bad_block.nTime = 2**31 - 1
        assert_template(node, bad_block, 'time-too-new')
        assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
        bad_block.nTime = 0
        assert_template(node, bad_block, 'time-too-old')
        assert_submitblock(bad_block, 'time-too-old', 'time-too-old')

        self.log.info("getblocktemplate: Test not best block")
        bad_block = copy.deepcopy(block)
        bad_block.hashPrevBlock = 123
        assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
        assert_submitblock(bad_block, 'prev-blk-not-found',
                           'prev-blk-not-found')

        self.log.info('submitheader tests')
        assert_raises_rpc_error(
            -22, 'Block header decode failed',
            lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
        assert_raises_rpc_error(
            -22, 'Block header decode failed',
            lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE - 2)))
        assert_raises_rpc_error(
            -25, 'Must submit previous header', lambda: node.submitheader(
                hexdata=super(CBlock, bad_block).serialize().hex()))

        block.nTime += 1
        block.solve()

        def chain_tip(b_hash, *, status='headers-only', branchlen=1):
            return {
                'hash': b_hash,
                'height': 202,
                'branchlen': branchlen,
                'status': status
            }

        assert chain_tip(block.hash) not in node.getchaintips()
        node.submitheader(hexdata=block.serialize().hex())
        assert chain_tip(block.hash) in node.getchaintips()
        node.submitheader(
            hexdata=CBlockHeader(block).serialize().hex())  # Noop
        assert chain_tip(block.hash) in node.getchaintips()

        bad_block_root = copy.deepcopy(block)
        bad_block_root.hashMerkleRoot += 2
        bad_block_root.solve()
        assert chain_tip(bad_block_root.hash) not in node.getchaintips()
        node.submitheader(
            hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert chain_tip(bad_block_root.hash) in node.getchaintips()
        # Should still reject invalid blocks, even if we have the header:
        assert_equal(
            node.submitblock(hexdata=bad_block_root.serialize().hex()),
            'bad-txnmrklroot')
        assert_equal(
            node.submitblock(hexdata=bad_block_root.serialize().hex()),
            'bad-txnmrklroot')
        assert chain_tip(bad_block_root.hash) in node.getchaintips()
        # We know the header for this invalid block, so should just return early without error:
        node.submitheader(
            hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert chain_tip(bad_block_root.hash) in node.getchaintips()

        bad_block_lock = copy.deepcopy(block)
        bad_block_lock.vtx[0].nLockTime = 2**32 - 1
        bad_block_lock.vtx[0].rehash()
        bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root()
        bad_block_lock.solve()
        assert_equal(
            node.submitblock(hexdata=bad_block_lock.serialize().hex()),
            'bad-txns-nonfinal')
        assert_equal(
            node.submitblock(hexdata=bad_block_lock.serialize().hex()),
            'duplicate-invalid')
        # Build a "good" block on top of the submitted bad block
        bad_block2 = copy.deepcopy(block)
        bad_block2.hashPrevBlock = bad_block_lock.sha256
        bad_block2.solve()
        assert_raises_rpc_error(
            -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(
                bad_block2).serialize().hex()))

        # Should reject invalid header right away
        bad_block_time = copy.deepcopy(block)
        bad_block_time.nTime = 1
        bad_block_time.solve()
        assert_raises_rpc_error(
            -25, 'time-too-old', lambda: node.submitheader(
                hexdata=CBlockHeader(bad_block_time).serialize().hex()))

        # Should ask for the block from a p2p node, if they announce the header as well:
        peer = node.add_p2p_connection(P2PDataStore())
        peer.wait_for_getheaders(timeout=5)  # Drop the first getheaders
        peer.send_blocks_and_test(blocks=[block], node=node)
        # Must be active now:
        assert chain_tip(block.hash, status='active',
                         branchlen=0) in node.getchaintips()

        # Building a few blocks should give the same results
        self.generatetoaddress(node, 10,
                               node.get_deterministic_priv_key().address)
        assert_raises_rpc_error(
            -25, 'time-too-old', lambda: node.submitheader(
                hexdata=CBlockHeader(bad_block_time).serialize().hex()))
        assert_raises_rpc_error(
            -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(
                bad_block2).serialize().hex()))
        node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
        node.submitheader(
            hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert_equal(node.submitblock(hexdata=block.serialize().hex()),
                     'duplicate')  # valid
Exemple #19
0
    def run_test(self):
        bitno = 1
        activated_version = 0x20000000 | (1 << bitno)

        node = self.nodes[0]  # convenience reference to the node

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

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

        self.log.info(
            "Generate some blocks to get the chain going and un-stick the mining RPCs"
        )
        node.generate(2)
        assert_equal(node.getblockcount(), 2)
        self.height = 3  # height of the next block to build
        self.tip = int("0x" + node.getbestblockhash(), 0)
        self.nodeaddress = node.getnewaddress()
        self.last_block_time = int(time.time())

        self.log.info("\'finaltx\' begins in DEFINED state")
        assert_equal(self.get_bip9_status('finaltx')['status'], 'defined')
        assert_equal(self.get_bip9_status('finaltx')['since'], 0)
        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' not in tmpl['rules'])
        assert ('finaltx' not in tmpl['vbavailable'])
        assert ('finaltx' not in tmpl)
        assert_equal(tmpl['vbrequired'], 0)
        assert_equal(tmpl['version'] & activated_version, 0x20000000)

        self.log.info("Test 1: Advance from DEFINED to STARTED")
        test_blocks = self.generate_blocks(141, 4)  # height = 143

        assert_equal(self.get_bip9_status('finaltx')['status'], 'started')
        assert_equal(self.get_bip9_status('finaltx')['since'], 144)
        assert_equal(
            self.get_bip9_status('finaltx')['statistics']['elapsed'], 0)
        assert_equal(self.get_bip9_status('finaltx')['statistics']['count'], 0)
        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' not in tmpl['rules'])
        assert_equal(tmpl['vbavailable']['finaltx'], bitno)
        assert_equal(tmpl['vbrequired'], 0)
        assert ('finaltx' not in tmpl)
        assert_equal(tmpl['version'] & activated_version, activated_version)

        self.log.info(
            "Save one of the anyone-can-spend coinbase outputs for later.")
        assert_equal(test_blocks[-1][0].vtx[0].vout[0].nValue, 5000000000)
        assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey,
                     CScript([OP_TRUE]))
        early_coin = COutPoint(test_blocks[-1][0].vtx[0].sha256, 0)

        self.log.info(
            "Test 2: Check stats after max number of \"not signalling\" blocks such that LOCKED_IN still possible this period"
        )
        self.generate_blocks(36, 4)  # 0x00000004 (not signalling)
        self.generate_blocks(10,
                             activated_version)  # 0x20000001 (not signalling)

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

        self.log.info(
            "Test 3: Check stats after one additional \"not signalling\" block -- LOCKED_IN no longer possible this period"
        )
        self.generate_blocks(1, 4)  # 0x00000004 (not signalling)

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

        self.log.info(
            "Test 4: Finish period with \"ready\" blocks, but soft fork will still fail to advance to LOCKED_IN"
        )
        self.generate_blocks(
            97, activated_version)  # 0x20000001 (signalling ready)

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

        self.log.info(
            "Test 5: Fail to achieve LOCKED_IN 100 out of 144 signal bit 1 using a variety of bits to simulate multiple parallel softforks"
        )
        self.generate_blocks(
            50, activated_version)  # 0x20000001 (signalling ready)
        self.generate_blocks(20, 4)  # 0x00000004 (not signalling)
        self.generate_blocks(
            50, activated_version)  # 0x20000101 (signalling ready)
        self.generate_blocks(24, 4)  # 0x20010000 (not signalling)

        assert_equal(self.get_bip9_status('finaltx')['status'], 'started')
        assert_equal(self.get_bip9_status('finaltx')['since'], 144)
        assert_equal(
            self.get_bip9_status('finaltx')['statistics']['elapsed'], 0)
        assert_equal(self.get_bip9_status('finaltx')['statistics']['count'], 0)
        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' not in tmpl['rules'])
        assert_equal(tmpl['vbavailable']['finaltx'], bitno)
        assert_equal(tmpl['vbrequired'], 0)
        assert_equal(tmpl['version'] & activated_version, activated_version)

        self.log.info(
            "Test 6: 108 out of 144 signal bit 1 to achieve LOCKED_IN using a variety of bits to simulate multiple parallel softforks"
        )
        self.generate_blocks(
            57, activated_version)  # 0x20000001 (signalling ready)
        self.generate_blocks(26, 4)  # 0x00000004 (not signalling)
        self.generate_blocks(
            50, activated_version)  # 0x20000101 (signalling ready)
        self.generate_blocks(10, 4)  # 0x20010000 (not signalling)

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

        self.log.info("Test 7: ...continue with Test 6")
        self.generate_blocks(
            1, activated_version)  # 0x20000001 (signalling ready)

        assert_equal(self.get_bip9_status('finaltx')['status'], 'locked_in')
        assert_equal(self.get_bip9_status('finaltx')['since'], 576)
        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' not in tmpl['rules'])

        self.log.info(
            "Test 8: 143 more version 536870913 blocks (waiting period-1)")
        self.generate_blocks(143, 4)

        assert_equal(self.get_bip9_status('finaltx')['status'], 'locked_in')
        assert_equal(self.get_bip9_status('finaltx')['since'], 576)
        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' not in tmpl['rules'])
        assert ('finaltx' in tmpl['vbavailable'])
        assert_equal(tmpl['vbrequired'], 0)
        assert_equal(tmpl['version'] & activated_version, activated_version)

        self.log.info(
            "Test 9: Generate a block without any spendable outputs, which should be allowed under normal circumstances."
        )
        test_blocks = self.generate_blocks(1, 4, sync=False)
        for txout in test_blocks[-1][0].vtx[0].vout:
            txout.scriptPubKey = CScript([OP_FALSE])
        test_blocks[-1][0].vtx[0].rehash()
        test_blocks[-1][0].hashMerkleRoot = test_blocks[-1][
            0].calc_merkle_root()
        test_blocks[-1][0].rehash()
        test_blocks[-1][0].solve()
        node.submitblock(ToHex(test_blocks[-1][0]))
        assert_equal(node.getbestblockhash(), test_blocks[-1][0].hash)
        self.tip = test_blocks[-1][0].sha256  # Hash has changed

        assert_equal(self.get_bip9_status('finaltx')['status'], 'active')
        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' in tmpl['rules'])
        assert ('finaltx' not in tmpl['vbavailable'])
        assert_equal(tmpl['vbrequired'], 0)
        assert (not (tmpl['version'] & (1 << bitno)))

        self.log.info(
            "Test 10: Attempt to do the same thing: generate a block with no spendable outputs in the coinbase. This fails because the next block needs at least one trivially spendable output to start the block-final transaction chain."
        )
        block = create_block(self.tip, create_coinbase(self.height),
                             self.last_block_time + 1)
        block.nVersion = 5
        for txout in block.vtx[0].vout:
            txout.scriptPubKey = CScript([OP_FALSE])
        block.vtx[0].rehash()
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        self.log.info(
            "Test 11: Generate the first block with block-final-tx rules enforced, which reuires the coinbase to have a trivially-spendable output."
        )
        self.generate_blocks(1, 4)
        assert (any(out.scriptPubKey == CScript([OP_TRUE])
                    for out in test_blocks[-1][0].vtx[0].vout))
        for n, txout in enumerate(test_blocks[-1][0].vtx[0].vout):
            non_protected_output = COutPoint(test_blocks[-1][0].vtx[0].sha256,
                                             n)
            assert_equal(txout.nValue, 312500000)

        self.log.info("Test 12: Generate 98 blocks (maturity period - 2)")
        self.generate_blocks(98, 4)

        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' not in tmpl)

        self.log.info(
            "Test 13: Generate one more block to allow non_protected_output to mature, which causes the block-final transaction to be required in the next block."
        )
        self.generate_blocks(1, 4)

        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' in tmpl)
        assert_equal(len(tmpl['finaltx']['prevout']), 1)
        assert_equal(
            tmpl['finaltx']['prevout'][0]['txid'],
            encode(ser_uint256(non_protected_output.hash)[::-1],
                   'hex_codec').decode('ascii'))
        assert_equal(tmpl['finaltx']['prevout'][0]['vout'],
                     non_protected_output.n)
        assert_equal(tmpl['finaltx']['prevout'][0]['amount'], 312470199)

        self.log.info(
            "Extra pass-through value is not included in the coinbasevalue field."
        )
        assert_equal(tmpl['coinbasevalue'],
                     5000000000 // 2**(self.height // 150))

        self.log.info(
            "The transactions field does not contain the block-final transaction."
        )
        assert_equal(len(tmpl['transactions']), 0)

        self.log.info(
            "Test 14: Attempt to create a block without the block-final transaction, which fails."
        )
        block = create_block(self.tip, create_coinbase(self.height),
                             self.last_block_time + 1)
        block.nVersion = 4
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        self.log.info(
            "Test 15: Add the block-final transaction, and it passes.")
        tx_final = CTransaction()
        tx_final.nVersion = 2
        tx_final.vin.append(
            CTxIn(non_protected_output, CScript([]), 0xffffffff))
        tx_final.vout.append(CTxOut(312470199, CScript([OP_TRUE])))
        tx_final.nLockTime = block.vtx[0].nLockTime
        tx_final.lock_height = block.vtx[0].lock_height
        tx_final.rehash()
        block.vtx.append(tx_final)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(), block.hash)
        prev_final_tx = block.vtx[-1]
        self.last_block_time += 1
        self.tip = block.sha256
        self.height += 1

        tmpl = node.getblocktemplate(
            {'rules': ['segwit', 'finaltx', 'auxpow']})
        assert ('finaltx' in tmpl)
        assert_equal(len(tmpl['finaltx']['prevout']), 1)
        assert_equal(
            tmpl['finaltx']['prevout'][0]['txid'],
            encode(ser_uint256(tx_final.sha256)[::-1],
                   'hex_codec').decode('ascii'))
        assert_equal(tmpl['finaltx']['prevout'][0]['vout'], 0)
        assert_equal(tmpl['finaltx']['prevout'][0]['amount'], 312469901)

        self.log.info(
            "Test 16: Create a block-final transaction with multiple outputs, which doesn't work because the number of outputs is restricted to be no greater than the number of inputs."
        )
        block = create_block(self.tip, create_coinbase(self.height),
                             self.last_block_time + 1)
        block.nVersion = 4
        tx_final = CTransaction()
        tx_final.nVersion = 2
        tx_final.vin.append(
            CTxIn(COutPoint(prev_final_tx.sha256, 0), CScript([]), 0xffffffff))
        tx_final.vout.append(CTxOut(156234951, CScript([OP_TRUE])))
        tx_final.vout.append(CTxOut(156234950, CScript([OP_TRUE])))
        tx_final.nLockTime = block.vtx[0].nLockTime
        tx_final.lock_height = block.vtx[0].lock_height
        tx_final.rehash()
        block.vtx.append(tx_final)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        self.log.info(
            "Test 17: Try increasing the number of inputs by using an old one doesn't work, because the block-final transaction must source its user inputs from the same block."
        )
        utxo = node.gettxout(
            encode(ser_uint256(early_coin.hash)[::-1],
                   'hex_codec').decode('ascii'), early_coin.n)
        assert ('amount' in utxo)
        utxo_amount = int(100000000 * utxo['amount'])
        block.vtx[-1].vin.append(CTxIn(early_coin, CScript([]), 0xffffffff))
        block.vtx[-1].rehash()
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        self.log.info(
            "Test 18: But spend it via a user transaction instead, and it can be captured and sent to the coinbase as fee."
        )

        # Insert spending transaction
        spend_tx = CTransaction()
        spend_tx.nVersion = 2
        spend_tx.vin.append(CTxIn(early_coin, CScript([]), 0xffffffff))
        spend_tx.vout.append(CTxOut(utxo_amount, CScript([OP_TRUE])))
        spend_tx.nLockTime = 0
        spend_tx.lock_height = block.vtx[0].lock_height
        spend_tx.rehash()
        block.vtx.insert(1, spend_tx)
        # Capture output of spend_tx in block-final tx (but don't update the
        # outputs--the value passes on to the coinbase as fee).
        block.vtx[-1].vin[-1].prevout = COutPoint(spend_tx.sha256, 0)
        block.vtx[-1].rehash()
        # Add the captured value to the block reward.
        block.vtx[0].vout[0].nValue += utxo_amount
        block.vtx[0].rehash()
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(), block.hash)
        prev_final_tx = block.vtx[-1]
        self.last_block_time += 1
        self.tip = block.sha256
        self.height += 1

        self.log.info(
            "Test 19: Spending only one of the prior outputs is insufficient.  ALL prior block-final outputs must be spent."
        )
        block = create_block(self.tip, create_coinbase(self.height),
                             self.last_block_time + 1)
        block.nVersion = 4
        tx_final = CTransaction()
        tx_final.nVersion = 2
        tx_final.vin.append(
            CTxIn(COutPoint(prev_final_tx.sha256, 0), CScript([]), 0xffffffff))
        tx_final.vout.append(CTxOut(156234801, CScript([OP_TRUE])))
        tx_final.nLockTime = block.vtx[0].nLockTime
        tx_final.lock_height = block.vtx[0].lock_height
        tx_final.rehash()
        block.vtx.append(tx_final)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        self.log.info(
            "Test 20: But spend all the prior outputs and it goes through.")
        block.vtx[-1].vin.append(
            CTxIn(COutPoint(prev_final_tx.sha256, 1), CScript([]), 0xffffffff))
        block.vtx[-1].vout[0].nValue *= 2
        block.vtx[-1].rehash()
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(), block.hash)
        prev_final_tx = block.vtx[-1]
        self.last_block_time += 1
        self.tip = block.sha256
        self.height += 1

        self.log.info(
            "Test 21: Now that the rules have activated, transactions spending the previous block-final transaction's outputs are rejected from the mempool."
        )
        self.log.info(
            "First we do this with a non-block-final input to demonstrate the test would otherwise work."
        )
        height = node.getblockcount() - 99
        while True:
            blk = node.getblock(node.getblockhash(height))
            txid = blk['tx'][0]
            utxo = node.gettxout(txid, 0)
            if utxo is not None and utxo['scriptPubKey']['hex'] == "51":
                break
            height -= 1

        spend_tx = CTransaction()
        spend_tx.nVersion = 2
        spend_tx.vin.append(
            CTxIn(COutPoint(uint256_from_str(unhexlify(txid)[::-1]), 0),
                  CScript([]), 0xffffffff))
        spend_tx.vout.append(
            CTxOut(
                int(utxo['amount'] * 100000000) - 10000, CScript([b'a' * 100]))
        )  # Make transaction large enough to avoid tx-size-small standardness check
        spend_tx.nLockTime = 0
        spend_tx.lock_height = utxo['refheight']
        spend_tx.rehash()
        node.sendrawtransaction(ToHex(spend_tx))
        mempool = node.getrawmempool()
        assert (spend_tx.hash in mempool)

        self.log.info(
            "Now we do the same exact thing with the last block-final transaction's outputs. It should not enter the mempool."
        )
        spend_tx = CTransaction()
        spend_tx.nVersion = 2
        spend_tx.vin.append(
            CTxIn(COutPoint(prev_final_tx.sha256, 0), CScript([]), 0xffffffff))
        spend_tx.vout.append(
            CTxOut(int(utxo['amount'] * 100000000), CScript([b'a' * 100]))
        )  # Make transaction large enough to avoid tx-size-small standardness check
        spend_tx.nLockTime = 0
        spend_tx.lock_height = utxo['refheight']
        spend_tx.rehash()
        try:
            node.sendrawtransaction(ToHex(spend_tx))
        except JSONRPCException as e:
            assert ("spend-block-final-txn" in e.error['message'])
        else:
            assert (False)
        mempool = node.getrawmempool()
        assert (spend_tx.hash not in mempool)

        self.log.info(
            "Test 22: Invalidate the tip, then malleate and re-solve the same block. This is a fast way of testing test that the block-final txid is restored on a reorg."
        )
        height = node.getblockcount()
        node.invalidateblock(block.hash)
        assert_equal(node.getblockcount(), height - 1)
        block.nVersion ^= 2
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getblockcount(), height)
        assert_equal(node.getbestblockhash(), block.hash)

        self.tip = block.sha256
        self.finaltx_vin = [
            CTxIn(COutPoint(block.vtx[-1].sha256, 0), CScript([]), 0xffffffff)
        ]

        self.log.info(
            "Test 22-25: Mine two blocks with trivially-spendable coinbase outputs, then test that the one that is exactly 100 blocks old is allowed to be spent in a block-final transaction, but the older one cannot."
        )
        self.generate_blocks(1, 4, finaltx=True)
        assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey,
                     CScript([OP_TRUE]))
        txin1 = CTxIn(COutPoint(test_blocks[-1][0].vtx[0].sha256, 0),
                      CScript([]), 0xffffffff)

        self.generate_blocks(1, 4, finaltx=True)
        assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey,
                     CScript([OP_TRUE]))
        txin2 = CTxIn(COutPoint(test_blocks[-1][0].vtx[0].sha256, 0),
                      CScript([]), 0xffffffff)

        self.generate_blocks(1, 4, finaltx=True)
        assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey,
                     CScript([OP_TRUE]))
        txin3 = CTxIn(COutPoint(test_blocks[-1][0].vtx[0].sha256, 0),
                      CScript([]), 0xffffffff)

        self.generate_blocks(98, 4, finaltx=True)

        # txin1 is too old -- it should have been collected on the last block
        block = create_block(self.tip, create_coinbase(self.height),
                             self.last_block_time + 1)
        block.nVersion = 4
        tx_final = CTransaction()
        tx_final.nVersion = 2
        tx_final.vin.extend(self.finaltx_vin)
        tx_final.vin.append(txin1)
        tx_final.vout.append(CTxOut(0, CScript([OP_TRUE])))
        tx_final.nLockTime = block.vtx[0].nLockTime
        tx_final.lock_height = block.vtx[0].lock_height
        tx_final.rehash()
        block.vtx.append(tx_final)
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        # txin3 is too young -- it hasn't matured
        block.vtx[-1].vin.pop()
        block.vtx[-1].vin.append(txin3)
        block.vtx[-1].rehash()
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(),
                     ser_uint256(self.tip)[::-1].hex())

        # txin2 is just right
        block.vtx[-1].vin.pop()
        block.vtx[-1].vin.append(txin2)
        block.vtx[-1].rehash()
        block.hashMerkleRoot = block.calc_merkle_root()
        block.rehash()
        block.solve()
        node.submitblock(ToHex(block))
        assert_equal(node.getbestblockhash(), block.hash)

        self.last_block_time += 1
        self.tip = block.sha256
        self.height += 1
        self.finaltx_vin = [
            CTxIn(COutPoint(block.vtx[-1].sha256, 0), CScript([]), 0xffffffff)
        ]
Exemple #20
0
 def on_inv(self, message):
     for inv in message.inv:
         hashhex = ser_uint256(inv.hash)[::-1].hex()
         self.seen_invs[hashhex] = time.time()
     super().on_inv(message)
Exemple #21
0
    def tapscript_satisfy_test(self,
                               script,
                               inputs=[],
                               add_issuance=False,
                               add_pegin=False,
                               fail=None,
                               add_prevout=False,
                               add_asset=False,
                               add_value=False,
                               add_spk=False,
                               seq=0,
                               add_out_spk=None,
                               add_out_asset=None,
                               add_out_value=None,
                               add_out_nonce=None,
                               ver=2,
                               locktime=0,
                               add_num_outputs=False,
                               add_weight=False,
                               blind=False):
        # Create a taproot utxo
        scripts = [("s0", script)]
        prev_tx, prev_vout, spk, sec, pub, tap = self.create_taproot_utxo(
            scripts)

        if add_pegin:
            fund_info = self.nodes[0].getpeginaddress()
            peg_id = self.nodes[0].sendtoaddress(
                fund_info["mainchain_address"], 1)
            raw_peg_tx = self.nodes[0].gettransaction(peg_id)["hex"]
            peg_txid = self.nodes[0].sendrawtransaction(raw_peg_tx)
            self.nodes[0].generate(101)
            peg_prf = self.nodes[0].gettxoutproof([peg_txid])
            claim_script = fund_info["claim_script"]

            raw_claim = self.nodes[0].createrawpegin(raw_peg_tx, peg_prf,
                                                     claim_script)
            tx = FromHex(CTransaction(), raw_claim['hex'])
        else:
            tx = CTransaction()

        tx.nVersion = ver
        tx.nLockTime = locktime
        # Spend the pegin and taproot tx together
        in_total = prev_tx.vout[prev_vout].nValue.getAmount()
        fees = 1000
        tap_in_pos = 0

        if blind:
            # Add an unrelated output
            key = ECKey()
            key.generate()
            tx.vout.append(
                CTxOut(nValue=CTxOutValue(10000),
                       scriptPubKey=spk,
                       nNonce=CTxOutNonce(key.get_pubkey().get_bytes())))

            tx_hex = self.nodes[0].fundrawtransaction(tx.serialize().hex())
            tx = FromHex(CTransaction(), tx_hex['hex'])

        tx.vin.append(
            CTxIn(COutPoint(prev_tx.sha256, prev_vout), nSequence=seq))
        tx.vout.append(
            CTxOut(nValue=CTxOutValue(in_total - fees),
                   scriptPubKey=spk))  # send back to self
        tx.vout.append(CTxOut(CTxOutValue(fees)))

        if add_issuance:
            blind_addr = self.nodes[0].getnewaddress()
            issue_addr = self.nodes[0].validateaddress(
                blind_addr)['unconfidential']
            # Issuances only require one fee output and that output must the last
            # one. However the way, the current code is structured, it is not possible
            # to this in a super clean without special casing.
            if add_pegin:
                tx.vout.pop()
                tx.vout.pop()
                tx.vout.insert(0,
                               CTxOut(nValue=CTxOutValue(in_total),
                                      scriptPubKey=spk))  # send back to self)
            issued_tx = self.nodes[0].rawissueasset(
                tx.serialize().hex(), [{
                    "asset_amount": 2,
                    "asset_address": issue_addr,
                    "blind": False
                }])[0]["hex"]
            tx = FromHex(CTransaction(), issued_tx)
        # Sign inputs
        if add_pegin:
            signed = self.nodes[0].signrawtransactionwithwallet(
                tx.serialize().hex())
            tx = FromHex(CTransaction(), signed['hex'])
            tap_in_pos += 1
        else:
            # Need to create empty witness when not deserializing from rpc
            tx.wit.vtxinwit.append(CTxInWitness())

        if blind:
            tx.vin[0], tx.vin[1] = tx.vin[1], tx.vin[0]
            utxo = self.get_utxo(tx, 1)
            zero_str = "0" * 64
            blinded_raw = self.nodes[0].rawblindrawtransaction(
                tx.serialize().hex(), [zero_str, utxo["amountblinder"]],
                [1.2, utxo['amount']], [utxo['asset'], utxo['asset']],
                [zero_str, utxo['assetblinder']])
            tx = FromHex(CTransaction(), blinded_raw)
            signed_raw_tx = self.nodes[0].signrawtransactionwithwallet(
                tx.serialize().hex())
            tx = FromHex(CTransaction(), signed_raw_tx['hex'])

        suffix_annex = []
        control_block = bytes([
            tap.leaves["s0"].version + tap.negflag
        ]) + tap.inner_pubkey + tap.leaves["s0"].merklebranch
        # Add the prevout to the top of inputs. The witness script will check for equality.
        if add_prevout:
            inputs = [
                prev_vout.to_bytes(4, 'little'),
                ser_uint256(prev_tx.sha256)
            ]
        if add_asset:
            assert blind  # only used with blinding in testing
            utxo = self.nodes[0].gettxout(
                ser_uint256(tx.vin[1].prevout.hash)[::-1].hex(),
                tx.vin[1].prevout.n)
            if "assetcommitment" in utxo:
                asset = bytes.fromhex(utxo["assetcommitment"])
            else:
                asset = b"\x01" + bytes.fromhex(utxo["asset"])[::-1]
            inputs = [asset[0:1], asset[1:33]]
        if add_value:
            utxo = self.nodes[0].gettxout(
                ser_uint256(tx.vin[1].prevout.hash)[::-1].hex(),
                tx.vin[1].prevout.n)
            if "valuecommitment" in utxo:
                value = bytes.fromhex(utxo["valuecommitment"])
                inputs = [value[0:1], value[1:33]]
            else:
                value = b"\x01" + int(
                    satoshi_round(utxo["value"]) * COIN).to_bytes(8, 'little')
                inputs = [value[0:1], value[1:9]]
        if add_spk:
            ver = CScriptOp.decode_op_n(int.from_bytes(spk[0:1], 'little'))
            inputs = [CScriptNum.encode(CScriptNum(ver))[1:],
                      spk[2:len(spk)]]  # always segwit

        # Add witness for outputs
        if add_out_asset is not None:
            asset = tx.vout[add_out_asset].nAsset.vchCommitment
            inputs = [asset[0:1], asset[1:33]]
        if add_out_value is not None:
            value = tx.vout[add_out_value].nValue.vchCommitment
            if len(value) == 9:
                inputs = [value[0:1], value[1:9][::-1]]
            else:
                inputs = [value[0:1], value[1:33]]
        if add_out_nonce is not None:
            nonce = tx.vout[add_out_nonce].nNonce.vchCommitment
            if len(nonce) == 1:
                inputs = [b'']
            else:
                inputs = [nonce]
        if add_out_spk is not None:
            out_spk = tx.vout[add_out_spk].scriptPubKey
            if len(out_spk) == 0:
                # Python upstream encoding CScriptNum interesting behaviour where it also encodes the length
                # This assumes the implicit wallet behaviour of using segwit outputs.
                # This is useful while sending scripts, but not while using CScriptNums in constructing scripts
                inputs = [
                    CScriptNum.encode(CScriptNum(-1))[1:],
                    sha256(out_spk)
                ]
            else:
                ver = CScriptOp.decode_op_n(
                    int.from_bytes(out_spk[0:1], 'little'))
                inputs = [
                    CScriptNum.encode(CScriptNum(ver))[1:],
                    out_spk[2:len(out_spk)]
                ]  # always segwit
        if add_num_outputs:
            num_outs = len(tx.vout)
            inputs = [CScriptNum.encode(CScriptNum(num_outs))[1:]]
        if add_weight:
            # Add a dummy input and check the overall weight
            inputs = [int(5).to_bytes(8, 'little')]
            wit = inputs + [bytes(tap.leaves["s0"].script), control_block
                            ] + suffix_annex
            tx.wit.vtxinwit[tap_in_pos].scriptWitness.stack = wit

            exp_weight = self.nodes[0].decoderawtransaction(
                tx.serialize().hex())["weight"]
            inputs = [exp_weight.to_bytes(8, 'little')]
        wit = inputs + [bytes(tap.leaves["s0"].script), control_block
                        ] + suffix_annex
        tx.wit.vtxinwit[tap_in_pos].scriptWitness.stack = wit

        if fail:
            assert_raises_rpc_error(-26, fail,
                                    self.nodes[0].sendrawtransaction,
                                    tx.serialize().hex())
            return

        self.nodes[0].sendrawtransaction(hexstring=tx.serialize().hex())
        self.nodes[0].generate(1)
        last_blk = self.nodes[0].getblock(self.nodes[0].getbestblockhash())
        tx.rehash()
        assert (tx.hash in last_blk['tx'])