示例#1
0
def input_script_multisig(
    multisig: MultisigRedeemScriptType,
    signature: bytes,
    signature_index: int,
    hash_type: int,
    coin: CoinInfo,
) -> bytearray:
    signatures = multisig.signatures  # other signatures
    if len(signatures[signature_index]) > 0:
        raise wire.DataError("Invalid multisig parameters")
    signatures[signature_index] = signature  # our signature

    # length of the redeem script
    pubkeys = multisig_get_pubkeys(multisig)
    redeem_script_length = scripts.output_script_multisig_length(
        pubkeys, multisig.m)

    # length of the result
    total_length = 0
    for s in signatures:
        total_length += 1 + len(s) + 1  # length, signature, hash_type
    total_length += 1 + redeem_script_length  # length, script

    w = empty_bytearray(total_length)

    for s in signatures:
        if len(s):
            scripts.append_signature(w, s, hash_type)

    # redeem script
    write_op_push(w, redeem_script_length)
    scripts.write_output_script_multisig(w, pubkeys, multisig.m)

    return w
示例#2
0
def generate_proof(
    node: bip32.HDNode,
    script_type: EnumTypeInputScriptType,
    multisig: Optional[MultisigRedeemScriptType],
    coin: CoinInfo,
    user_confirmed: bool,
    ownership_ids: List[bytes],
    script_pubkey: bytes,
    commitment_data: bytes,
) -> Tuple[bytes, bytes]:
    flags = 0
    if user_confirmed:
        flags |= _FLAG_USER_CONFIRMED

    proof = empty_bytearray(4 + 1 + 1 + len(ownership_ids) * _OWNERSHIP_ID_LEN)

    write_bytes_fixed(proof, _VERSION_MAGIC, 4)
    write_uint8(proof, flags)
    write_bitcoin_varint(proof, len(ownership_ids))
    for ownership_id in ownership_ids:
        write_bytes_fixed(proof, ownership_id, _OWNERSHIP_ID_LEN)

    sighash = hashlib.sha256(proof)
    sighash.update(script_pubkey)
    sighash.update(commitment_data)
    signature = common.ecdsa_sign(node, sighash.digest())
    public_key = node.public_key()
    write_bip322_signature_proof(proof, script_type, multisig, coin,
                                 public_key, signature)

    return proof, signature
示例#3
0
def input_script_p2pkh_or_p2sh(
    pubkey: bytes, signature: bytes, sighash: int
) -> bytearray:
    w = empty_bytearray(5 + len(signature) + 1 + 5 + len(pubkey))
    append_signature(w, signature, sighash)
    append_pubkey(w, pubkey)
    return w
示例#4
0
def witness_p2wpkh(signature: bytes, pubkey: bytes, sighash: int) -> bytearray:
    w = empty_bytearray(1 + 5 + len(signature) + 1 + 5 + len(pubkey))
    write_bitcoin_varint(w,
                         0x02)  # num of segwit items, in P2WPKH it's always 2
    append_signature(w, signature, sighash)
    append_pubkey(w, pubkey)
    return w
示例#5
0
def witness_p2wpkh(signature: bytes, pubkey: bytes,
                   hash_type: int) -> bytearray:
    w = empty_bytearray(1 + 5 + len(signature) + 1 + 5 + len(pubkey))
    write_bitcoin_varint(w,
                         0x02)  # num of segwit items, in P2WPKH it's always 2
    write_signature_prefixed(w, signature, hash_type)
    write_bytes_prefixed(w, pubkey)
    return w
示例#6
0
def input_script_p2wpkh_in_p2sh(pubkeyhash: bytes) -> bytearray:
    # 16 00 14 <pubkeyhash>
    # Signature is moved to the witness.

    w = empty_bytearray(3 + len(pubkeyhash))
    w.append(0x16)  # length of the data
    w.append(0x00)  # witness version byte
    w.append(0x14)  # P2WPKH witness program (pub key hash length)
    write_bytes(w, pubkeyhash)  # pub key hash
    return w
示例#7
0
def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray:
    # Either:
    # 00 14 <20-byte-key-hash>
    # 00 20 <32-byte-script-hash>

    w = empty_bytearray(3 + len(witprog))
    w.append(0x00)  # witness version byte
    w.append(len(witprog))  # pub key hash length is 20 (P2WPKH) or 32 (P2WSH) bytes
    write_bytes(w, witprog)  # pub key hash
    return w
示例#8
0
def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray:
    # Either:
    # 00 14 <20-byte-key-hash>
    # 00 20 <32-byte-script-hash>
    length = len(witprog)
    utils.ensure(length == 20 or length == 32)

    w = empty_bytearray(3 + length)
    w.append(0x00)  # witness version byte
    w.append(length)  # pub key hash length is 20 (P2WPKH) or 32 (P2WSH) bytes
    write_bytes_fixed(w, witprog, length)  # pub key hash
    return w
示例#9
0
def input_script_p2wsh_in_p2sh(script_hash: bytes) -> bytearray:
    # 22 00 20 <redeem script hash>
    # Signature is moved to the witness.

    if len(script_hash) != 32:
        raise ScriptsError("Redeem script hash should be 32 bytes long")

    w = empty_bytearray(3 + len(script_hash))
    w.append(0x22)  # length of the data
    w.append(0x00)  # witness version byte
    w.append(0x20)  # P2WSH witness program (redeem script hash length)
    write_bytes(w, script_hash)
    return w
示例#10
0
def output_script_sstxchange(addr: str) -> bytearray:
    try:
        raw_address = base58.decode_check(addr, blake256d_32)
    except ValueError:
        raise wire.DataError("Invalid address")

    w = empty_bytearray(26)
    w.append(0xBD)  # OP_SSTXCHANGE
    w.append(0x76)  # OP_DUP
    w.append(0xA9)  # OP_HASH160
    w.append(0x14)  # OP_DATA_20
    write_bytes_fixed(w, raw_address[2:], 20)
    w.append(0x88)  # OP_EQUALVERIFY
    w.append(0xAC)  # OP_CHECKSIG
    return w
示例#11
0
def output_script_multisig(pubkeys, m: int, w: bytearray = None) -> bytearray:
    n = len(pubkeys)
    if n < 1 or n > 15 or m < 1 or m > 15:
        raise ScriptsError("Invalid multisig parameters")
    for pubkey in pubkeys:
        if len(pubkey) != 33:
            raise ScriptsError("Invalid multisig parameters")

    if w is None:
        w = empty_bytearray(output_script_multisig_length(pubkeys, m))
    w.append(0x50 + m)  # numbers 1 to 16 are pushed as 0x50 + value
    for p in pubkeys:
        append_pubkey(w, p)
    w.append(0x50 + n)
    w.append(0xAE)  # OP_CHECKMULTISIG
    return w
示例#12
0
def witness_multisig(
    multisig: MultisigRedeemScriptType,
    signature: bytes,
    signature_index: int,
    hash_type: int,
) -> bytearray:
    # get other signatures, stretch with empty bytes to the number of the pubkeys
    signatures = multisig.signatures + [b""] * (
        multisig_get_pubkey_count(multisig) - len(multisig.signatures))
    # fill in our signature
    if signatures[signature_index]:
        raise wire.DataError("Invalid multisig parameters")
    signatures[signature_index] = signature

    # filter empty
    signatures = [s for s in signatures if s]

    # witness program + signatures + redeem script
    num_of_witness_items = 1 + len(signatures) + 1

    # length of the redeem script
    pubkeys = multisig_get_pubkeys(multisig)
    redeem_script_length = output_script_multisig_length(pubkeys, multisig.m)

    # length of the result
    total_length = 1 + 1  # number of items, OP_FALSE
    for s in signatures:
        total_length += 1 + len(s) + 1  # length, signature, hash_type
    total_length += 1 + redeem_script_length  # length, script

    w = empty_bytearray(total_length)

    write_bitcoin_varint(w, num_of_witness_items)
    # Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which
    # consumes one additional item on the stack:
    # https://bitcoin.org/en/developer-guide#standard-transactions
    write_bitcoin_varint(w, 0)

    for s in signatures:
        write_signature_prefixed(w, s,
                                 hash_type)  # size of the witness included

    # redeem script
    write_bitcoin_varint(w, redeem_script_length)
    write_output_script_multisig(w, pubkeys, multisig.m)

    return w
示例#13
0
def witness_p2wsh(
    multisig: MultisigRedeemScriptType,
    signature: bytes,
    signature_index: int,
    sighash: int,
):
    # get other signatures, stretch with None to the number of the pubkeys
    signatures = multisig.signatures + [None] * (
        multisig_get_pubkey_count(multisig) - len(multisig.signatures)
    )
    # fill in our signature
    if signatures[signature_index]:
        raise ScriptsError("Invalid multisig parameters")
    signatures[signature_index] = signature

    # filter empty
    signatures = [s for s in signatures if s]

    # witness program + signatures + redeem script
    num_of_witness_items = 1 + len(signatures) + 1

    # length of the redeem script
    pubkeys = multisig_get_pubkeys(multisig)
    redeem_script_length = output_script_multisig_length(pubkeys, multisig.m)

    # length of the result
    total_length = 1 + 1  # number of items, version
    for s in signatures:
        total_length += 1 + len(s) + 1  # length, signature, sighash
    total_length += 1 + redeem_script_length  # length, script

    w = empty_bytearray(total_length)

    write_varint(w, num_of_witness_items)
    write_varint(w, 0)  # version 0 witness program

    for s in signatures:
        append_signature(w, s, sighash)  # size of the witness included

    # redeem script
    write_varint(w, redeem_script_length)
    output_script_multisig(pubkeys, multisig.m, w)

    return w
示例#14
0
def input_script_multisig(
    multisig: MultisigRedeemScriptType,
    signature: bytes,
    signature_index: int,
    sighash: int,
    coin: CoinInfo,
) -> bytearray:
    signatures = multisig.signatures  # other signatures
    if len(signatures[signature_index]) > 0:
        raise ScriptsError("Invalid multisig parameters")
    signatures[signature_index] = signature  # our signature

    # length of the redeem script
    pubkeys = multisig_get_pubkeys(multisig)
    redeem_script_length = output_script_multisig_length(pubkeys, multisig.m)

    # length of the result
    total_length = 0
    if utils.BITCOIN_ONLY or not coin.decred:
        total_length += 1  # OP_FALSE
    for s in signatures:
        total_length += 1 + len(s) + 1  # length, signature, sighash
    total_length += 1 + redeem_script_length  # length, script

    w = empty_bytearray(total_length)

    if utils.BITCOIN_ONLY or not coin.decred:
        # Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which
        # consumes one additional item on the stack:
        # https://bitcoin.org/en/developer-guide#standard-transactions
        w.append(0x00)

    for s in signatures:
        if len(s):
            append_signature(w, s, sighash)

    # redeem script
    write_op_push(w, redeem_script_length)
    write_output_script_multisig(w, pubkeys, multisig.m)

    return w
示例#15
0
async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
    coin = coins.by_name(tx.coin_name)

    # h_first is used to make sure the inputs and outputs streamed in Phase 1
    # are the same as in Phase 2.  it is thus not required to fully hash the
    # tx, as the SignTx info is streamed only once
    h_first = HashWriter(sha256())  # not a real tx hash

    if coin.decred:
        hash143 = DecredPrefixHasher(tx)  # pseudo bip143 prefix hashing
        tx_ser = TxRequestSerializedType()
    elif tx.overwintered:
        if tx.version == 3:
            hash143 = Zip143()  # ZIP-0143 transaction hashing
        elif tx.version == 4:
            hash143 = Zip243()  # ZIP-0243 transaction hashing
        else:
            raise SigningError(
                FailureType.DataError,
                "Unsupported version for overwintered transaction",
            )
    else:
        hash143 = Bip143()  # BIP-0143 transaction hashing

    multifp = MultisigFingerprint()  # control checksum of multisig inputs
    weight = TxWeightCalculator(tx.inputs_count, tx.outputs_count)

    total_in = 0  # sum of input amounts
    segwit_in = 0  # sum of segwit input amounts
    total_out = 0  # sum of output amounts
    change_out = 0  # change output amount
    wallet_path = []  # common prefix of input paths
    segwit = {}  # dict of booleans stating if input is segwit

    # output structures
    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()

    for i in range(tx.inputs_count):
        progress.advance()
        # STAGE_REQUEST_1_INPUT
        txi = await request_tx_input(tx_req, i)
        wallet_path = input_extract_wallet_path(txi, wallet_path)
        write_tx_input_check(h_first, txi)
        weight.add_input(txi)
        hash143.add_prevouts(
            txi)  # all inputs are included (non-segwit as well)
        hash143.add_sequence(txi)

        if not address_n_matches_coin(txi.address_n, coin):
            await confirm_foreign_address(txi.address_n, coin)

        if txi.multisig:
            multifp.add(txi.multisig)

        if txi.script_type in (
                InputScriptType.SPENDWITNESS,
                InputScriptType.SPENDP2SHWITNESS,
        ):
            if not coin.segwit:
                raise SigningError(FailureType.DataError,
                                   "Segwit not enabled on this coin")
            if not txi.amount:
                raise SigningError(FailureType.DataError,
                                   "Segwit input without amount")
            segwit[i] = True
            segwit_in += txi.amount
            total_in += txi.amount

        elif txi.script_type in (
                InputScriptType.SPENDADDRESS,
                InputScriptType.SPENDMULTISIG,
        ):
            if coin.force_bip143 or tx.overwintered:
                if not txi.amount:
                    raise SigningError(FailureType.DataError,
                                       "Expected input with amount")
                segwit[i] = False
                segwit_in += txi.amount
                total_in += txi.amount
            else:
                segwit[i] = False
                total_in += await get_prevtx_output_value(
                    coin, tx_req, txi.prev_hash, txi.prev_index)

        else:
            raise SigningError(FailureType.DataError,
                               "Wrong input script type")

        if coin.decred:
            w_txi = empty_bytearray(8 if i == 0 else 0 + 9 +
                                    len(txi.prev_hash))
            if i == 0:  # serializing first input => prepend headers
                write_bytes(w_txi, get_tx_header(coin, tx))
            write_tx_input_decred(w_txi, txi)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

    if coin.decred:
        hash143.add_output_count(tx)

    for o in range(tx.outputs_count):
        # STAGE_REQUEST_3_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)
        weight.add_output(txo_bin.script_pubkey)

        if change_out == 0 and is_change(txo, wallet_path, segwit_in, multifp):
            # output is change and does not need confirmation
            change_out = txo.amount
        elif not await confirm_output(txo, coin):
            raise SigningError(FailureType.ActionCancelled, "Output cancelled")

        if coin.decred:
            if txo.decred_script_version is not None and txo.decred_script_version != 0:
                raise SigningError(
                    FailureType.ActionCancelled,
                    "Cannot send to output with script version != 0",
                )
            txo_bin.decred_script_version = txo.decred_script_version

            w_txo_bin = empty_bytearray(4 + 8 + 2 + 4 +
                                        len(txo_bin.script_pubkey))
            if o == 0:  # serializing first output => prepend outputs count
                write_varint(w_txo_bin, tx.outputs_count)
            write_tx_output(w_txo_bin, txo_bin)
            tx_ser.serialized_tx = w_txo_bin
            tx_req.serialized = tx_ser
            hash143.set_last_output_bytes(w_txo_bin)

        write_tx_output(h_first, txo_bin)
        hash143.add_output(txo_bin)
        total_out += txo_bin.amount

    fee = total_in - total_out
    if fee < 0:
        raise SigningError(FailureType.NotEnoughFunds, "Not enough funds")

    # fee > (coin.maxfee per byte * tx size)
    if fee > (coin.maxfee_kb / 1000) * (weight.get_total() / 4):
        if not await confirm_feeoverthreshold(fee, coin):
            raise SigningError(FailureType.ActionCancelled,
                               "Signing cancelled")

    if not await confirm_total(total_in - change_out, fee, coin):
        raise SigningError(FailureType.ActionCancelled, "Total cancelled")

    if coin.decred:
        hash143.add_locktime_expiry(tx)

    return h_first, hash143, segwit, total_in, wallet_path
示例#16
0
async def sign_tx(tx: SignTx, root: bip32.HDNode):
    tx = sanitize_sign_tx(tx)

    progress.init(tx.inputs_count, tx.outputs_count)

    # Phase 1

    h_first, hash143, segwit, authorized_in, wallet_path = await check_tx_fee(
        tx, root)

    # Phase 2
    # - sign inputs
    # - check that nothing changed

    coin = coins.by_name(tx.coin_name)
    tx_ser = TxRequestSerializedType()

    txo_bin = TxOutputBinType()
    tx_req = TxRequest()
    tx_req.details = TxRequestDetailsType()
    tx_req.serialized = None

    if coin.decred:
        prefix_hash = hash143.prefix_hash()

    for i_sign in range(tx.inputs_count):
        progress.advance()
        txi_sign = None
        key_sign = None
        key_sign_pub = None

        if segwit[i_sign]:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)

            is_segwit = (txi_sign.script_type == InputScriptType.SPENDWITNESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit:
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")
            input_check_wallet_path(txi_sign, wallet_path)

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub)

            w_txi = empty_bytearray(7 + len(txi_sign.prev_hash) + 4 +
                                    len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi, get_tx_header(coin, tx, True))
            write_tx_input(w_txi, txi_sign)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

        elif coin.force_bip143 or tx.overwintered:
            # STAGE_REQUEST_SEGWIT_INPUT
            txi_sign = await request_tx_input(tx_req, i_sign)
            input_check_wallet_path(txi_sign, wallet_path)

            is_bip143 = (txi_sign.script_type == InputScriptType.SPENDADDRESS
                         or txi_sign.script_type
                         == InputScriptType.SPENDMULTISIG)
            if not is_bip143 or txi_sign.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")
            authorized_in -= txi_sign.amount

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()
            hash143_hash = hash143.preimage_hash(
                coin,
                tx,
                txi_sign,
                ecdsa_hash_pubkey(key_sign_pub, coin),
                get_hash_type(coin),
            )

            # if multisig, check if singing with a key that is included in multisig
            if txi_sign.multisig:
                multisig_pubkey_index(txi_sign.multisig, key_sign_pub)

            signature = ecdsa_sign(key_sign, hash143_hash)
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = empty_bytearray(5 + len(txi_sign.prev_hash) + 4 +
                                         len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(coin, tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

        elif coin.decred:
            txi_sign = await request_tx_input(tx_req, i_sign)

            input_check_wallet_path(txi_sign, wallet_path)

            key_sign = node_derive(root, txi_sign.address_n)
            key_sign_pub = key_sign.public_key()

            if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                prev_pkscript = output_script_multisig(
                    multisig_get_pubkeys(txi_sign.multisig),
                    txi_sign.multisig.m)
            elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                prev_pkscript = output_script_p2pkh(
                    ecdsa_hash_pubkey(key_sign_pub, coin))
            else:
                raise ValueError("Unknown input script type")

            h_witness = HashWriter(blake256())
            write_uint32(h_witness,
                         tx.version | DECRED_SERIALIZE_WITNESS_SIGNING)
            write_varint(h_witness, tx.inputs_count)

            for ii in range(tx.inputs_count):
                if ii == i_sign:
                    write_varint(h_witness, len(prev_pkscript))
                    write_bytes(h_witness, prev_pkscript)
                else:
                    write_varint(h_witness, 0)

            witness_hash = get_tx_hash(h_witness,
                                       double=coin.sign_hash_double,
                                       reverse=False)

            h_sign = HashWriter(blake256())
            write_uint32(h_sign, DECRED_SIGHASHALL)
            write_bytes(h_sign, prefix_hash)
            write_bytes(h_sign, witness_hash)

            sig_hash = get_tx_hash(h_sign, double=coin.sign_hash_double)
            signature = ecdsa_sign(key_sign, sig_hash)
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = empty_bytearray(
                8 + 4 +
                len(hash143.get_last_output_bytes()) if i_sign == 0 else 0 +
                16 + 4 + len(txi_sign.script_sig))

            if i_sign == 0:
                write_bytes(w_txi_sign, hash143.get_last_output_bytes())
                write_uint32(w_txi_sign, tx.lock_time)
                write_uint32(w_txi_sign, tx.expiry)
                write_varint(w_txi_sign, tx.inputs_count)

            write_tx_input_decred_witness(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign
            tx_req.serialized = tx_ser

        else:
            # hash of what we are signing with this input
            h_sign = HashWriter(sha256())
            # same as h_first, checked before signing the digest
            h_second = HashWriter(sha256())

            if tx.overwintered:
                write_uint32(h_sign, tx.version
                             | OVERWINTERED)  # nVersion | fOverwintered
                write_uint32(h_sign, tx.version_group_id)  # nVersionGroupId
            else:
                write_uint32(h_sign, tx.version)  # nVersion

            write_varint(h_sign, tx.inputs_count)

            for i in range(tx.inputs_count):
                # STAGE_REQUEST_4_INPUT
                txi = await request_tx_input(tx_req, i)
                input_check_wallet_path(txi, wallet_path)
                write_tx_input_check(h_second, txi)
                if i == i_sign:
                    txi_sign = txi
                    key_sign = node_derive(root, txi.address_n)
                    key_sign_pub = key_sign.public_key()
                    # for the signing process the script_sig is equal
                    # to the previous tx's scriptPubKey (P2PKH) or a redeem script (P2SH)
                    if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                        txi_sign.script_sig = output_script_multisig(
                            multisig_get_pubkeys(txi_sign.multisig),
                            txi_sign.multisig.m)
                    elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                        txi_sign.script_sig = output_script_p2pkh(
                            ecdsa_hash_pubkey(key_sign_pub, coin))
                        if coin.bip115:
                            txi_sign.script_sig += script_replay_protection_bip115(
                                txi_sign.prev_block_hash_bip115,
                                txi_sign.prev_block_height_bip115,
                            )
                    else:
                        raise SigningError(FailureType.ProcessError,
                                           "Unknown transaction type")
                else:
                    txi.script_sig = bytes()
                write_tx_input(h_sign, txi)

            write_varint(h_sign, tx.outputs_count)

            for o in range(tx.outputs_count):
                # STAGE_REQUEST_4_OUTPUT
                txo = await request_tx_output(tx_req, o)
                txo_bin.amount = txo.amount
                txo_bin.script_pubkey = output_derive_script(txo, coin, root)
                write_tx_output(h_second, txo_bin)
                write_tx_output(h_sign, txo_bin)

            write_uint32(h_sign, tx.lock_time)
            if tx.overwintered:
                write_uint32(h_sign, tx.expiry)  # expiryHeight
                write_varint(h_sign, 0)  # nJoinSplit

            write_uint32(h_sign, get_hash_type(coin))

            # check the control digests
            if get_tx_hash(h_first, False) != get_tx_hash(h_second):
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")

            # if multisig, check if singing with a key that is included in multisig
            if txi_sign.multisig:
                multisig_pubkey_index(txi_sign.multisig, key_sign_pub)

            # compute the signature from the tx digest
            signature = ecdsa_sign(
                key_sign, get_tx_hash(h_sign, double=coin.sign_hash_double))
            tx_ser.signature_index = i_sign
            tx_ser.signature = signature

            # serialize input with correct signature
            txi_sign.script_sig = input_derive_script(coin, txi_sign,
                                                      key_sign_pub, signature)
            w_txi_sign = empty_bytearray(5 + len(txi_sign.prev_hash) + 4 +
                                         len(txi_sign.script_sig) + 4)
            if i_sign == 0:  # serializing first input => prepend headers
                write_bytes(w_txi_sign, get_tx_header(coin, tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

    if coin.decred:
        return await request_tx_finish(tx_req)

    for o in range(tx.outputs_count):
        progress.advance()
        # STAGE_REQUEST_5_OUTPUT
        txo = await request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, root)

        # serialize output
        w_txo_bin = empty_bytearray(5 + 8 + 5 + len(txo_bin.script_pubkey) + 4)
        if o == 0:  # serializing first output => prepend outputs count
            write_varint(w_txo_bin, tx.outputs_count)
        write_tx_output(w_txo_bin, txo_bin)

        tx_ser.signature_index = None
        tx_ser.signature = None
        tx_ser.serialized_tx = w_txo_bin

        tx_req.serialized = tx_ser

    any_segwit = True in segwit.values()

    for i in range(tx.inputs_count):
        progress.advance()
        if segwit[i]:
            # STAGE_REQUEST_SEGWIT_WITNESS
            txi = await request_tx_input(tx_req, i)
            input_check_wallet_path(txi, wallet_path)

            is_segwit = (txi.script_type == InputScriptType.SPENDWITNESS or
                         txi.script_type == InputScriptType.SPENDP2SHWITNESS)
            if not is_segwit or txi.amount > authorized_in:
                raise SigningError(FailureType.ProcessError,
                                   "Transaction has changed during signing")
            authorized_in -= txi.amount

            key_sign = node_derive(root, txi.address_n)
            key_sign_pub = key_sign.public_key()
            hash143_hash = hash143.preimage_hash(
                coin,
                tx,
                txi,
                ecdsa_hash_pubkey(key_sign_pub, coin),
                get_hash_type(coin),
            )

            signature = ecdsa_sign(key_sign, hash143_hash)
            if txi.multisig:
                # find out place of our signature based on the pubkey
                signature_index = multisig_pubkey_index(
                    txi.multisig, key_sign_pub)
                witness = witness_p2wsh(txi.multisig, signature,
                                        signature_index, get_hash_type(coin))
            else:
                witness = witness_p2wpkh(signature, key_sign_pub,
                                         get_hash_type(coin))

            tx_ser.serialized_tx = witness
            tx_ser.signature_index = i
            tx_ser.signature = signature
        elif any_segwit:
            tx_ser.serialized_tx = bytearray(
                1)  # empty witness for non-segwit inputs
            tx_ser.signature_index = None
            tx_ser.signature = None

        tx_req.serialized = tx_ser

    write_uint32(tx_ser.serialized_tx, tx.lock_time)

    if tx.overwintered:
        if tx.version == 3:
            write_uint32(tx_ser.serialized_tx, tx.expiry)  # expiryHeight
            write_varint(tx_ser.serialized_tx, 0)  # nJoinSplit
        elif tx.version == 4:
            write_uint32(tx_ser.serialized_tx, tx.expiry)  # expiryHeight
            write_uint64(tx_ser.serialized_tx, 0)  # valueBalance
            write_varint(tx_ser.serialized_tx, 0)  # nShieldedSpend
            write_varint(tx_ser.serialized_tx, 0)  # nShieldedOutput
            write_varint(tx_ser.serialized_tx, 0)  # nJoinSplit
        else:
            raise SigningError(
                FailureType.DataError,
                "Unsupported version for overwintered transaction",
            )

    await request_tx_finish(tx_req)
示例#17
0
def output_script_paytoopreturn(data: bytes) -> bytearray:
    w = empty_bytearray(1 + 5 + len(data))
    w.append(0x6A)  # OP_RETURN
    write_op_push(w, len(data))
    w.extend(data)
    return w
示例#18
0
def sstxcommitment_pkh(pkh: bytes, amount: int) -> bytes:
    w = empty_bytearray(30)
    write_bytes_fixed(w, pkh, 20)
    write_uint64_le(w, amount)
    write_bytes_fixed(w, b"\x00\x58", 2)  # standard fee limits
    return w
示例#19
0
def output_script_multisig(pubkeys: List[bytes], m: int) -> bytearray:
    w = empty_bytearray(output_script_multisig_length(pubkeys, m))
    write_output_script_multisig(w, pubkeys, m)
    return w