Пример #1
0
def serialise_p2sh_pretx(inputs, source, source_value, data_output, change_output=None, pubkey=None, multisig_pubkeys=None, multisig_pubkeys_required=None):
    assert data_output  # we don't do this unless there's data

    data_array, data_value = data_output

    s  = (1).to_bytes(4, byteorder='little')  # Version

    # Number of inputs.
    s += var_int(int(len(inputs)))

    # List of Inputs.
    for i in range(len(inputs)):
        txin = inputs[i]
        s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1]  # TxOutHash
        s += txin['vout'].to_bytes(4, byteorder='little')            # TxOutIndex

        tx_script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8'))
        s += var_int(int(len(tx_script)))  # Script length
        s += tx_script                     # Script
        s += b'\xff' * 4                   # Sequence

    # Number of outputs.
    n = len(data_array)
    if change_output:
        n += 1

    # encode number of outputs
    s += var_int(n)

    # P2SH for data encodeded inputs
    for n, data_chunk in enumerate(data_array):
        data_chunk = config.PREFIX + data_chunk  # prefix the data_chunk

        # get the scripts
        _, _, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(data_chunk, n, pubkey, multisig_pubkeys, multisig_pubkeys_required)

        s += data_value.to_bytes(8, byteorder='little')  # Value
        s += var_int(int(len(outputScript)))             # Script length
        s += outputScript                                # Script

    # Change output.
    if change_output:
        change_address, change_value = change_output
        tx_script, witness_script = get_script(change_address)

        s += change_value.to_bytes(8, byteorder='little')  # Value
        s += var_int(int(len(tx_script)))                  # Script length
        s += tx_script                                     # Script

    s += (0).to_bytes(4, byteorder='little')  # LockTime

    return s
Пример #2
0
def serialise_p2sh_datatx(txid,
                          source,
                          source_input,
                          destination_outputs,
                          data_output,
                          pubkey=None,
                          multisig_pubkeys=None,
                          multisig_pubkeys_required=None):
    assert data_output  # we don't do this unless there's data

    txhash = bitcoinlib.core.lx(bitcoinlib.core.b2x(txid))  # reverse txId
    data_array, value = data_output

    # version
    s = (1).to_bytes(4, byteorder='little')

    # number of inputs is the length of data_array (+1 if a source_input exists)
    number_of_inputs = len(data_array)
    if source_input is not None:
        number_of_inputs += 1
    s += var_int(number_of_inputs)

    # Handle a source input here for a P2SH source
    if source_input is not None:
        s += binascii.unhexlify(bytes(source_input['txid'],
                                      'utf-8'))[::-1]  # TxOutHash
        s += source_input['vout'].to_bytes(4, byteorder='little')  # TxOutIndex

        # since pubkey is not returned from indexd, add it from bitcoind
        source_inputs = backend.ensure_script_pub_key_for_inputs(
            [source_input])
        source_input = source_inputs[0]
        tx_script = binascii.unhexlify(
            bytes(source_input['scriptPubKey'], 'utf-8'))
        s += var_int(int(len(tx_script)))  # Script length
        s += tx_script  # Script
        s += b'\xff' * 4  # Sequence

    # list of inputs
    for n, data_chunk in enumerate(data_array):
        data_chunk = config.PREFIX + data_chunk  # prefix the data_chunk

        # get the scripts
        scriptSig, redeemScript, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(
            data_chunk, n, pubkey, multisig_pubkeys, multisig_pubkeys_required)
        substituteScript = scriptSig + outputScript

        s += txhash  # TxOutHash
        s += (n).to_bytes(4,
                          byteorder='little')  # TxOutIndex (assumes 0-based)
        s += var_int(len(substituteScript))  # Script length
        s += substituteScript  # Script
        s += b'\xff' * 4  # Sequence

    # number of outputs, always 1 for the opreturn
    n = 1
    n += len(destination_outputs)

    # encode output length
    s += var_int(n)

    # destination outputs
    for destination, value in destination_outputs:
        tx_script, witness_script = get_script(destination)

        s += value.to_bytes(8, byteorder='little')  # Value
        s += var_int(int(len(tx_script)))  # Script length
        s += tx_script  # Script

    # opreturn to signal P2SH encoding
    key = arc4.init_arc4(txid)
    data_chunk = config.PREFIX + b'P2SH'
    data_chunk = key.encrypt(data_chunk)
    tx_script = OP_RETURN  # OP_RETURN
    tx_script += op_push(len(data_chunk))  # Push bytes of data chunk
    tx_script += data_chunk  # Data

    # add opreturn
    s += (0).to_bytes(8, byteorder='little')  # Value
    s += var_int(int(len(tx_script)))  # Script length
    s += tx_script  # Script

    s += (0).to_bytes(4, byteorder='little')  # LockTime

    return s
def test_p2sh_encoding_manual_multisig_transaction(server_db):
    source = P2SH_ADDR[0]
    destination = ADDR[1]

    with util_test.ConfigContext(
            OLD_STYLE_API=True), util_test.MockProtocolChangesContext(
                enhanced_sends=True, p2sh_encoding=True):
        p2sh_source_multisig_pubkeys_binary = [
            binascii.unhexlify(p) for p in [
                DP['pubkey'][ADDR[0]], DP['pubkey'][ADDR[1]], DP['pubkey'][
                    ADDR[2]]
            ]
        ]
        scriptSig, redeemScript, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(
            b'deadbeef01',
            n=0,
            pubKey=None,
            multisig_pubkeys=p2sh_source_multisig_pubkeys_binary,
            multisig_pubkeys_required=2)
        redeemScript = bitcoinlib.core.script.CScript(redeemScript)
        assert repr(
            redeemScript
        ) == "CScript([OP_DROP, 2, x('{}'), x('{}'), x('{}'), 3, OP_CHECKMULTISIGVERIFY, 0, OP_DROP, OP_DEPTH, 0, OP_EQUAL])".format(
            DP['pubkey'][ADDR[0]], DP['pubkey'][ADDR[1]],
            DP['pubkey'][ADDR[2]])

        # setup transaction
        fee = 20000
        fee_per_kb = 50000
        pretxhex = api.compose_transaction(
            server_db,
            'send', {
                'source': source,
                'destination': destination,
                'asset': 'XCP',
                'quantity': 100,
            },
            p2sh_source_multisig_pubkeys=[
                DP['pubkey'][ADDR[0]], DP['pubkey'][ADDR[1]],
                DP['pubkey'][ADDR[2]]
            ],
            p2sh_source_multisig_pubkeys_required=2,
            encoding='p2sh',
            fee_per_kb=fee_per_kb,
            fee=fee)
        # debugTransaction = bitcoinlib.core.CTransaction.deserialize(binascii.unhexlify(pretxhex))

        # store transaction
        pretxid, _ = util_test.insert_raw_transaction(pretxhex, server_db)
        logger.debug('pretxid %s' % (pretxid))

        # now compose the data transaction
        result = api.compose_transaction(
            server_db,
            'send',
            {
                'source': source,
                'destination': destination,
                'asset': 'XCP',
                'quantity': 100
            },
            p2sh_source_multisig_pubkeys=[
                DP['pubkey'][ADDR[0]], DP['pubkey'][ADDR[1]],
                DP['pubkey'][ADDR[2]]
            ],
            p2sh_source_multisig_pubkeys_required=2,
            p2sh_pretx_txid=pretxid,  # pass the pretxid
            encoding='p2sh',
            fee_per_kb=fee_per_kb)
        assert not isinstance(result, list)
        datatxhex = result

        datatx = bitcoinlib.core.CTransaction.deserialize(
            binascii.unhexlify(datatxhex))

        # parse the transaction
        parsed_source, parsed_destination, parsed_btc_amount, parsed_fee, parsed_data, extra = blocks._get_tx_info(
            datatxhex)
        assert parsed_source == source
        assert parsed_data == binascii.unhexlify(
            "00000002"
            "0000000000000001"
            "0000000000000064"
            "6f8d6ae8a3b381663118b4e1eff4cfc7d0954dd6ec"
        )  # ID=enhanced_send(0x02) ASSET=XCP(0x01) VALUE=100(0x64) destination_pubkey(0x6f8d...d6ec)
        assert parsed_btc_amount == 0