Beispiel #1
0
def cltv_validate(node, tx, height):
    '''Modify the signature in vin 0 of the tx to pass CLTV
    Prepends <height> CLTV DROP in the scriptSig, and sets
    the locktime to height'''
    tx.vin[0].nSequence = 0
    tx.nLockTime = height

    # Need to re-sign, since nSequence and nLockTime changed
    signed_result = node.signrawtransaction(ToHex(tx))
    new_tx = CTransaction()
    new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex'])))

    new_tx.vin[0].scriptSig = CScript(
        [CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
        list(CScript(new_tx.vin[0].scriptSig)))
    return new_tx
Beispiel #2
0
def cltv_lock_to_height(node, tx, to_address, amount, height=-1):
    '''Modify the scriptPubKey to add an OP_CHECKLOCKTIMEVERIFY, and make
    a transaction that spends it.

    This transforms the output script to anyone can spend (OP_TRUE) if the
    lock time condition is valid.

    Default height is -1 which leads CLTV to fail

    TODO: test more ways that transactions using CLTV could be invalid (eg
    locktime requirements fail, sequence time requirements fail, etc).
    '''
    height_op = OP_1NEGATE
    if(height > 0):
        tx.vin[0].nSequence = 0
        tx.nLockTime = height
        height_op = CScriptNum(height)

    tx.vout[0].scriptPubKey = CScript(
        [height_op, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_TRUE])

    pad_tx(tx)
    fundtx_raw = node.signrawtransactionwithwallet(ToHex(tx))['hex']

    fundtx = FromHex(CTransaction(), fundtx_raw)
    fundtx.rehash()

    # make spending tx
    from_txid = fundtx.hash
    inputs = [{
        "txid": fundtx.hash,
        "vout": 0
    }]
    output = {to_address: amount}

    spendtx_raw = node.createrawtransaction(inputs, output)

    spendtx = FromHex(CTransaction(), spendtx_raw)
    pad_tx(spendtx)

    return fundtx, spendtx
Beispiel #3
0
    def run_test(self):
        self.mine_chain()
        node = self.nodes[0]

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

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

        # Mine a block to leave initial block download
        node.generatetoaddress(1, node.get_deterministic_priv_key().address)
        tmpl = node.getblocktemplate({'rules': ['segwit']})
        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()

        # round-trip the encoded bip34 block height commitment
        assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig),
                     next_height)
        # round-trip negative and multi-byte CScriptNums to catch python regression
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))),
                     1500)
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))),
                     -1500)
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1)

        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,
                                b2x(block.serialize()[:-15]))

        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, b2x(bad_block.serialize()))

        self.log.info("getblocktemplate: Test truncated final transaction")
        assert_raises_rpc_error(
            -22, "Block decode failed", node.getblocktemplate, {
                'data': b2x(block.serialize()[:-1]),
                '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': b2x(bad_block_sn),
                                    '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=b2x(block.serialize()))
        assert chain_tip(block.hash) in node.getchaintips()
        node.submitheader(hexdata=b2x(CBlockHeader(block).serialize()))  # 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=b2x(CBlockHeader(bad_block_root).serialize()))
        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=b2x(bad_block_root.serialize())),
                     'bad-txnmrklroot')
        assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())),
                     '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=b2x(CBlockHeader(bad_block_root).serialize()))
        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=b2x(bad_block_lock.serialize())),
                     'bad-txns-nonfinal')
        assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())),
                     '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=b2x(
                CBlockHeader(bad_block2).serialize())))

        # 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=b2x(
                CBlockHeader(bad_block_time).serialize())))

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

        # Building a few blocks should give the same results
        node.generatetoaddress(10, node.get_deterministic_priv_key().address)
        assert_raises_rpc_error(
            -25, 'time-too-old', lambda: node.submitheader(hexdata=b2x(
                CBlockHeader(bad_block_time).serialize())))
        assert_raises_rpc_error(
            -25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x(
                CBlockHeader(bad_block2).serialize())))
        node.submitheader(hexdata=b2x(CBlockHeader(block).serialize()))
        node.submitheader(
            hexdata=b2x(CBlockHeader(bad_block_root).serialize()))
        assert_equal(node.submitblock(hexdata=b2x(block.serialize())),
                     'duplicate')  # valid
Beispiel #4
0
def cltv_validate(node, tx, height):
    # Modify the signature in vin 0 and nSequence/nLockTime of the tx to pass CLTV
    scheme = [[CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, height]

    return cltv_modify_tx(node, tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
Beispiel #5
0
    def run_test(self):
        self.mine_chain()
        node = self.nodes[0]

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

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

        # Mine a block to leave initial block download
        node.generatetoaddress(1, node.get_deterministic_priv_key().address)
        tmpl = node.getblocktemplate({'rules': ['segwit']})
        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()

        # round-trip the encoded bip34 block height commitment
        assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig), next_height)
        # round-trip negative and multi-byte CScriptNums to catch python regression
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))), 1500)
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))), -1500)
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1)

        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:
        node.add_p2p_connection(P2PDataStore())
        node.p2p.wait_for_getheaders(timeout=5)  # Drop the first getheaders
        node.p2p.send_blocks_and_test(blocks=[block], node=node)
        # Must be active now:
        assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()

        # Building a few blocks should give the same results
        node.generatetoaddress(10, node.get_deterministic_priv_key().address)
        assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
        assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(bad_block2).serialize().hex()))
        node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
        node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate')  # valid
Beispiel #6
0
    def run_test(self):
        node = self.nodes[0]

        self.bootstrap_p2p()

        self.log.info("Create some blocks with OP_1 coinbase for spending.")
        tip = self.getbestblock(node)
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        node.p2p.send_blocks_and_test(blocks, node, success=True)
        spendable_txns = [block.vtx[0] for block in blocks]

        self.log.info("Mature the blocks and get out of IBD.")
        self.generatetoaddress(node, 100, node.get_deterministic_priv_key().address)

        self.log.info("Setting up spends to test and mining the fundings")

        # Generate a key pair
        privkeybytes = b"INT64!!!" * 4
        private_key = ECKey()
        private_key.set(privkeybytes, True)
        # get uncompressed public key serialization
        public_key = private_key.get_pubkey().get_bytes()

        def create_fund_and_spend_tx(scriptsigextra, redeemextra) -> Tuple[CTransaction, CTransaction]:
            spendfrom = spendable_txns.pop()

            redeem_script = CScript(redeemextra + [OP_1, public_key, OP_1, OP_CHECKMULTISIG])
            script_pubkey = CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL])

            value = spendfrom.vout[0].nValue
            value1 = value - 500

            # Fund transaction
            txfund = create_tx_with_script(spendfrom, 0, b'', value1, script_pubkey)
            txfund.rehash()

            p2sh = script_to_p2sh(redeem_script)
            self.log.info(f"scriptPubKey {script_pubkey!r}")
            self.log.info(f"redeemScript {redeem_script!r} -> p2sh address {p2sh}")

            # Spend transaction
            value2 = value1 - 500
            txspend = CTransaction()
            txspend.vout.append(
                CTxOut(value2, CScript([OP_TRUE])))
            txspend.vin.append(
                CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            sighashtype = SIGHASH_ALL | SIGHASH_FORKID
            hashbyte = bytes([sighashtype & 0xff])
            sighash = SignatureHashForkId(
                redeem_script, txspend, 0, sighashtype, value1)
            txsig = schnorr.sign(privkeybytes, sighash) + hashbyte
            dummy = OP_1  # Required for 1-of-1 schnorr sig
            txspend.vin[0].scriptSig = ss = CScript([dummy, txsig] + scriptsigextra + [redeem_script])
            self.log.info(f"scriptSig: {ss!r}")
            txspend.rehash()

            return txfund, txspend

        mempool = []

        # Basic test of OP_MUL 2 * 3 = 6
        tx0, tx = create_fund_and_spend_tx([OP_2, OP_3], [OP_MUL, OP_6, OP_EQUALVERIFY])
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle the output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Basic test of OP_DIV 6 / 3 = 2
        tx0, tx = create_fund_and_spend_tx([OP_6, OP_3], [OP_DIV, OP_2, OP_EQUALVERIFY])
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide 2^63-1 by 1 -- This should be 100% ok
        ssextra = [CScriptNum(int(2**63 - 1)), OP_1]
        rsextra = [OP_DIV, CScriptNum(int(2**63 - 1) // 1), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide -2^63-1 / -2^63-1 -- This should be 100% ok
        ssextra = [CScriptNum(-int(2**63 - 1)), CScriptNum(-int(2**63 - 1))]
        rsextra = [OP_DIV, OP_1, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide -2^63-1 / 2^63-1 -- This should be 100% ok
        ssextra = [CScriptNum(-int(2**63 - 1)), CScriptNum(int(2**63 - 1))]
        rsextra = [OP_DIV, OP_1NEGATE, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide 2^63-1 / -2^63-1 -- This should be 100% ok
        ssextra = [CScriptNum(int(2**63 - 1)), CScriptNum(-int(2**63 - 1))]
        rsextra = [OP_DIV, OP_1NEGATE, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide 2^63-1 / 2^63-1 -- This should be 100% ok
        ssextra = [CScriptNum(int(2**63 - 1)), CScriptNum(int(2**63 - 1))]
        rsextra = [OP_DIV, OP_1, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Multiply past 2^32 -- should work
        ssextra = [CScriptNum(int(2**31)), CScriptNum(int(2**31))]
        rsextra = [OP_MUL, CScriptNum(int(2**62)), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Add past (2^31 - 1) -- should work
        ssextra = [CScriptNum(int(2**31)), CScriptNum(int(2**31))]
        rsextra = [OP_ADD, CScriptNum(int(2**32)), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Sub below -(2^31 - 1) -- should work
        ssextra = [CScriptNum(-int(2**31 - 1)), CScriptNum(int(2**31 - 1))]
        rsextra = [OP_SUB, CScriptNum(-int(2**32 - 2)), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide/multiply mixed: -2^60 * 3 / 6 == -2^59
        ssextra = [CScriptNum(-int(2**60)), OP_3]
        rsextra = [OP_MUL, OP_6, OP_DIV, CScriptNum(-int(2**59)), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide: -2^31 * 3 / -6 == 2^30 (intermediate value outside of 32-bit range)
        ssextra = [CScriptNum(-int(2**31)), OP_3]
        rsextra = [OP_MUL, CScriptNum(-6), OP_DIV, CScriptNum(int(2**30)), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Divide: -2^32 * 3 / -6 == 2^31 (1 operand & intermediate value > 2^31 - 1)
        ssextra = [CScriptNum(-int(2**32)), OP_3]
        rsextra = [OP_MUL, CScriptNum(-6), OP_DIV, CScriptNum(int(2**31)), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Multiply past 2^63 - 1 -- funding tx is ok, spending should not be accepted due to out-of-range operand
        ssextra = [CScriptNum(int((2**63) - 1)), OP_3]
        rsextra = [OP_MUL, OP_DROP, OP_1, OP_1, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=OVERFLOW_ERROR_BAD_OPERAND)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # Add past 2^63 - 1 -- funding tx is ok, spending should not be accepted due to bad operand
        ssextra = [CScriptNum(int((2**63) - 1)), OP_1]
        rsextra = [OP_ADD, OP_DROP, OP_1, OP_1, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=OVERFLOW_ERROR_BAD_OPERAND)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # Sub below -2^63 - 1 -- funding tx is ok, spending should not be accepted due to bad operand
        ssextra = [CScriptNum(-int((2**63) - 1)), OP_10]
        rsextra = [OP_SUB, OP_DROP, OP_1, OP_1, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=OVERFLOW_ERROR_BAD_OPERAND)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # Modulo: -(2^63 - 1) % -1. Should not overflow, but yield 0
        ssextra = [CScriptNum(-int((2**63) - 1)), OP_1NEGATE]
        rsextra = [OP_MOD, OP_0, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Modulo: -(2^63 - 1) % -(2^63 - 1). Should not overflow, but yield 0
        ssextra = [CScriptNum(-int((2**63) - 1)), CScriptNum(-int((2**63) - 1))]
        rsextra = [OP_MOD, OP_0, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Attempt to create the forbidden: -(2^63), but don't use it as a number
        ssextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7)]
        rsextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # Attempt to create the forbidden: -(2^63), use it as a number
        # Note: This generates an overflow exception when deserializing, hence the "unknown error" -- known issue
        #       with the interpreter.
        ssextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7)]
        rsextra = [bytes((0x80,)) + bytes((0x7f,)) + bytes((0xff,) * 7), OP_SUB, OP_0, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=OVERFLOW_ERROR_UNK)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # OP_NUM2BIN - {2^63 - 1} -> 8-byte BIN should succeed
        ssextra = [CScriptNum(int(2**63 - 1)), OP_8]
        rsextra = [OP_NUM2BIN, bytes.fromhex('ffffffffffffff7f'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_NUM2BIN - {2^63 - 1} -> 16-byte BIN should succeed
        ssextra = [CScriptNum(int(2**63 - 1)), OP_16]
        rsextra = [OP_NUM2BIN, bytes.fromhex('ffffffffffffff7f0000000000000000'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_NUM2BIN - {-2^63 + 1} -> 8-byte BIN should succeed
        ssextra = [CScriptNum(int(-2**63 + 1)), OP_8]
        rsextra = [OP_NUM2BIN, bytes.fromhex('ffffffffffffffff'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_NUM2BIN - {2^63 - 1} -> 7-byte BIN should fail because it won't fit within requested size
        ssextra = [CScriptNum(int(2**63 - 1)), OP_7]
        rsextra = [OP_NUM2BIN, OP_DROP]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=IMPOSSIBLE_ENCODING_ERROR)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # OP_NUM2BIN - {2^31 - 1} -> 8-byte BIN should succeed (check that old functionality still works)
        ssextra = [CScriptNum(int(2**31 - 1)), OP_8]
        rsextra = [OP_NUM2BIN, bytes.fromhex('ffffff7f00000000'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_NUM2BIN - {2^31 - 1} -> 4-byte BIN should succeed (check that old functionality still works)
        ssextra = [CScriptNum(int(2**31 - 1)), OP_4]
        rsextra = [OP_NUM2BIN, bytes.fromhex('ffffff7f'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_BIN2NUM - {BIN 2^63 - 1} should succeed
        ssextra = [CScriptNum(int(2**63 - 1))]
        rsextra = [OP_BIN2NUM, bytes.fromhex('ffffffffffffff7f'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_BIN2NUM - {BIN 2^63-1 padded with 8 extra bytes of zeroes} should succeed
        ssextra = [CScriptNum.encode(CScriptNum(int(2**63 - 1)))[1:] + bytes.fromhex('00') * 8]
        rsextra = [OP_BIN2NUM, bytes.fromhex('ffffffffffffff7f'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_BIN2NUM - {BIN -2^63+1} should succeed
        ssextra = [CScriptNum(int(-2**63 + 1))]
        rsextra = [OP_BIN2NUM, bytes.fromhex('ffffffffffffffff'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_BIN2NUM - {BIN 2^63} when crammed into 8 bytes will be treated as '-0'.. which, oddly, ends up as
        #              0 when using BIN2NUM. This is the same quirky behavior as before this feature was added,
        #              e.g.: 0x80 BIN2NUM -> 0
        ssextra = [bytes.fromhex('0000000000000080')]
        rsextra = [OP_BIN2NUM, OP_0, OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        assert_equal(node.getrawmempool(), mempool)

        # OP_BIN2NUM - {2^63} -> When encoding as not '-0', but what 2^63 would encode as (9-byte value), it should fail
        #                        because it's out of range.
        ssextra = [bytes.fromhex('000000000000008000')]
        rsextra = [OP_BIN2NUM, OP_DROP]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=OVERFLOW_ERROR_BAD_OPERAND)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # OP_BIN2NUM - {BIN 2^31-1} should succeed (check that old functionality still works)
        ssextra = [CScriptNum(int(2**31 - 1))]
        rsextra = [OP_BIN2NUM, bytes.fromhex('ffffff7f'), OP_EQUALVERIFY]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0, tx], node)
        mempool += [tx0.hash, tx.hash]
        spendable_txns.insert(0, tx)  # Recycle te output from this tx
        assert_equal(node.getrawmempool(), mempool)

        # OP_PICK - {4294967297 OP_PICK} should FAIL on both 64-bit and 32-bit systems
        ssextra = [OP_16, OP_15, CScriptNum(4294967297)]
        rsextra = [OP_PICK, OP_2, OP_PICK, OP_EQUALVERIFY, OP_DROP, OP_DROP]
        tx0, tx = create_fund_and_spend_tx(ssextra, rsextra)
        node.p2p.send_txs_and_test([tx0], node)
        node.p2p.send_txs_and_test([tx], node, success=False, expect_disconnect=True,
                                   reject_reason=INVALID_STACK_OPERATION)
        mempool += [tx0.hash]
        assert_equal(node.getrawmempool(), mempool)
        self.reconnect_p2p()  # we lost the connection from above bad tx, reconnect

        # Finally, mine the mempool and ensure that all txns made it into a block
        prevtiphash = node.getbestblockhash()
        tiphash = self.generatetoaddress(node, 1, node.get_deterministic_priv_key().address)[0]
        assert prevtiphash != tiphash
        assert_equal(node.getrawmempool(), [])
        blockinfo = node.getblock(tiphash, 1)
        assert all(txid in blockinfo['tx'] for txid in mempool)

        # --------------------------------------------------------------------
        # Test that scripts fail evaluation if bigint64 feature is disabled
        # --------------------------------------------------------------------

        # 1. Restart the node with -reindex-chainstate (to be paranoid)
        self.restart_node(0, self.extra_args[0] + ["-reindex-chainstate=1"])
        assert_equal(node.getbestblockhash(), tiphash)

        # The below tests have been disabled since BCHN no longer has the -upgrade8activationtime argument
        # (it has activated already and the height has been hard-coded for all chains).
        """
Beispiel #7
0
def script_BIP34_coinbase_height(height):
    if height <= 16:
        res = CScriptOp.encode_op_n(height)
        # Append dummy to increase scriptSig size above 2 (see bad-cb-length consensus rule)
        return CScript([res, OP_1])
    return CScript([CScriptNum(height)])
Beispiel #8
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'])
Beispiel #9
0
    def run_test(self):
        node = self.nodes[0]

        self.log.info('getmininginfo')
        mining_info = node.getmininginfo()
        assert_equal(mining_info['blocks'], 200)
        assert_equal(mining_info['chain'], 'regtest')
        assert_equal(mining_info['currentblocktx'], 0)
        assert_equal(mining_info['currentblockweight'], 0)
        # assert_equal(mining_info['difficulty'], Decimal('4.159690237829E-9'))
        # assert_equal(mining_info['networkhashps'], Decimal('0.1841805555555555'))
        assert_equal(mining_info['pooledtx'], 0)

        # Mine a block to leave initial block download
        node.generate(1)
        tmpl = node.getblocktemplate()
        self.log.info("getblocktemplate: Test capability advertised")
        assert 'proposal' in tmpl['capabilities']
        assert 'coinbasetxn' not in tmpl

        newaddress = node.getnewaddress()
        pubkey = node.validateaddress(newaddress)["pubkey"]
        next_height = int(tmpl["height"])
        coinbase_key = CECKey()
        coinbase_key.set_secretbytes(b"horsebattery")
        coinbase_pubkey = coinbase_key.get_pubkey()
        coinbase_tx = create_coinbase(
            height=next_height,
            pubkey=coinbase_pubkey)  #bytes(pubkey,encoding='utf-8'))
        # sequence numbers must not be max for nLockTime to have effect
        coinbase_tx.vin[0].nSequence = 2**32 - 2
        coinbase_tx.rehash()

        # round-trip the encoded bip34 block height commitment
        assert_equal(CScriptNum.decode(coinbase_tx.vin[0].scriptSig),
                     next_height)
        # round-trip negative and multi-byte CScriptNums to catch python regression
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(1500))),
                     1500)
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1500))),
                     -1500)
        assert_equal(CScriptNum.decode(CScriptNum.encode(CScriptNum(-1))), -1)

        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: 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,
                                b2x(block.serialize()[:-15]))

        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, b2x(bad_block.serialize()))

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

        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')

        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')

        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')

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

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

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

        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')
        bad_block.nTime = 0
        assert_template(node, bad_block, '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')
Beispiel #10
0
    def run_test(self):
        node = self.nodes[0]

        # Generate 6 keys.
        rawkeys = []
        pubkeys = []
        for i in range(6):
            raw_key = CECKey()
            raw_key.set_secretbytes(('privkey%d' % i).encode('ascii'))
            rawkeys.append(raw_key)
        pubkeys = [CPubKey(key.get_pubkey()) for key in rawkeys]

        # Create a 4-of-6 multi-sig wallet with CLTV.
        height = 210
        redeem_script = CScript(
            [CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP
             ]  # CLTV (lock_time >= 210)
            + [OP_4] + pubkeys + [OP_6, OP_CHECKMULTISIG])  # multi-sig
        hex_redeem_script = bytes_to_hex_str(redeem_script)
        p2sh_address = script_to_p2sh(redeem_script, main=False)

        # Send 1 coin to the mult-sig wallet.
        txid = node.sendtoaddress(p2sh_address, 1.0)
        raw_tx = node.getrawtransaction(txid, True)
        try:
            node.importaddress(hex_redeem_script, 'cltv', True, True)
        except Exception:
            pass
        assert_equal(
            sig(node.getreceivedbyaddress(p2sh_address, 0) - Decimal(1.0)), 0)

        # Mine one block to confirm the transaction.
        node.generate(1)  # block 201
        assert_equal(
            sig(node.getreceivedbyaddress(p2sh_address, 1) - Decimal(1.0)), 0)

        # Try to spend the coin.
        addr_to = node.getnewaddress('')

        # (1) Find the UTXO
        for vout in raw_tx['vout']:
            if vout['scriptPubKey']['addresses'] == [p2sh_address]:
                vout_n = vout['n']
        hex_script_pubkey = raw_tx['vout'][vout_n]['scriptPubKey']['hex']
        value = raw_tx['vout'][vout_n]['value']

        # (2) Create a tx
        inputs = [{
            "txid": txid,
            "vout": vout_n,
            "scriptPubKey": hex_script_pubkey,
            "redeemScript": hex_redeem_script,
            "amount": value,
        }]
        outputs = {addr_to: 0.999}
        lock_time = height
        hex_spend_raw_tx = node.createrawtransaction(inputs, outputs,
                                                     lock_time)
        hex_funding_raw_tx = node.getrawtransaction(txid, False)

        # (3) Try to sign the spending tx.
        tx0 = CTransaction()
        tx0.deserialize(io.BytesIO(hex_str_to_bytes(hex_funding_raw_tx)))
        tx1 = CTransaction()
        tx1.deserialize(io.BytesIO(hex_str_to_bytes(hex_spend_raw_tx)))
        self.sign_tx(tx1, tx0, vout_n, redeem_script, 0,
                     rawkeys[:4])  # Sign with key[0:4]

        # Mine some blocks to pass the lock time.
        node.generate(10)

        # Spend the CLTV multi-sig coins.
        raw_tx1 = tx1.serialize()
        hex_raw_tx1 = bytes_to_hex_str(raw_tx1)
        node.sendrawtransaction(hex_raw_tx1)

        # Check the tx is accepted by mempool but not confirmed.
        assert_equal(
            sig(node.getreceivedbyaddress(addr_to, 0) - Decimal(0.999)), 0)
        assert_equal(sig(node.getreceivedbyaddress(addr_to, 1)), 0)

        # Mine a block to confirm the tx.
        node.generate(1)
        assert_equal(
            sig(node.getreceivedbyaddress(addr_to, 1) - Decimal(0.999)), 0)