Exemple #1
0
    def __init__(self, tx: SignTx, keychain: seed.Keychain,
                 coin: coininfo.CoinInfo) -> None:
        ensure(coin.decred)
        super().__init__(tx, keychain, coin)

        self.write_tx_header(self.serialized_tx, self.tx, witness_marker=True)
        write_bitcoin_varint(self.serialized_tx, self.tx.inputs_count)
Exemple #2
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
Exemple #3
0
def write_witness_multisig(
    w: Writer,
    multisig: MultisigRedeemScriptType,
    signature: bytes,
    signature_index: int,
    hash_type: int,
) -> None:
    # 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

    # witness program + signatures + redeem script
    num_of_witness_items = 1 + sum(1 for s in signatures if s) + 1
    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:
        if s:
            write_signature_prefixed(w, s,
                                     hash_type)  # size of the witness included

    # redeem script
    pubkeys = multisig_get_pubkeys(multisig)
    write_output_script_multisig(w, pubkeys, multisig.m, prefixed=True)
Exemple #4
0
    async def get_legacy_tx_digest(
        self, index: int, script_pubkey: Optional[bytes] = None
    ) -> Tuple[bytes, TxInputType, Optional[HDNode]]:
        # the transaction digest which gets signed for this input
        h_sign = self.create_hash_writer()
        # should come out the same as h_confirmed, checked before signing the digest
        h_check = self.create_hash_writer()

        self.write_tx_header(h_sign, self.tx, witness_marker=False)
        write_bitcoin_varint(h_sign, self.tx.inputs_count)

        for i in range(self.tx.inputs_count):
            # STAGE_REQUEST_4_INPUT in legacy
            txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
            writers.write_tx_input_check(h_check, txi)
            # Only the previous UTXO's scriptPubKey is included in h_sign.
            if i == index:
                txi_sign = txi
                node = None
                if not script_pubkey:
                    self.wallet_path.check_input(txi)
                    self.multisig_fingerprint.check_input(txi)
                    node = self.keychain.derive(txi.address_n)
                    key_sign_pub = node.public_key()
                    if txi.multisig:
                        # Sanity check to ensure we are signing with a key that is included in the multisig.
                        multisig.multisig_pubkey_index(txi.multisig, key_sign_pub)

                    if txi.script_type == InputScriptType.SPENDMULTISIG:
                        script_pubkey = scripts.output_script_multisig(
                            multisig.multisig_get_pubkeys(txi.multisig), txi.multisig.m,
                        )
                    elif txi.script_type == InputScriptType.SPENDADDRESS:
                        script_pubkey = scripts.output_script_p2pkh(
                            addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin)
                        )
                    else:
                        raise wire.ProcessError("Unknown transaction type")
                self.write_tx_input(h_sign, txi, script_pubkey)
            else:
                self.write_tx_input(h_sign, txi, bytes())

        write_bitcoin_varint(h_sign, self.tx.outputs_count)

        for i in range(self.tx.outputs_count):
            # STAGE_REQUEST_4_OUTPUT in legacy
            txo = await helpers.request_tx_output(self.tx_req, i, self.coin)
            script_pubkey = self.output_derive_script(txo)
            self.write_tx_output(h_check, txo, script_pubkey)
            self.write_tx_output(h_sign, txo, script_pubkey)

        writers.write_uint32(h_sign, self.tx.lock_time)
        writers.write_uint32(h_sign, self.get_sighash_type(txi_sign))

        # check that the inputs were the same as those streamed for confirmation
        if self.h_confirmed.get_digest() != h_check.get_digest():
            raise wire.ProcessError("Transaction has changed during signing")

        tx_digest = writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double)
        return tx_digest, txi_sign, node
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
def message_digest(message):
    h = HashWriter(sha256())
    signed_message_header = "Lisk Signed Message:\n"
    write_bitcoin_varint(h, len(signed_message_header))
    h.extend(signed_message_header)
    write_bitcoin_varint(h, len(message))
    h.extend(message)
    return sha256(h.get_digest()).digest()
Exemple #7
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
Exemple #8
0
def write_signature_prefixed(w: Writer, signature: bytes,
                             sighash_type: SigHashType) -> None:
    length = len(signature)
    if sighash_type != SigHashType.SIGHASH_ALL_TAPROOT:
        length += 1

    write_bitcoin_varint(w, length)
    write_bytes_unchecked(w, signature)
    if sighash_type != SigHashType.SIGHASH_ALL_TAPROOT:
        w.append(sighash_type)
Exemple #9
0
 def write_tx_header(
     self,
     w: writers.Writer,
     tx: Union[SignTx, PrevTx],
     witness_marker: bool,
 ) -> None:
     writers.write_uint32(w, tx.version)  # nVersion
     if witness_marker:
         write_bitcoin_varint(w, 0x00)  # segwit witness marker
         write_bitcoin_varint(w, 0x01)  # segwit witness flag
Exemple #10
0
    async def step4_serialize_inputs(self) -> None:
        self.write_tx_header(self.serialized_tx, self.tx, bool(self.segwit))
        write_bitcoin_varint(self.serialized_tx, self.tx.inputs_count)

        for i in range(self.tx.inputs_count):
            progress.advance()
            if i in self.segwit:
                await self.serialize_segwit_input(i)
            else:
                await self.sign_nonsegwit_input(i)
Exemple #11
0
def write_output_script_p2pkh(w: Writer,
                              pubkeyhash: bytes,
                              prefixed: bool = False) -> None:
    if prefixed:
        write_bitcoin_varint(w, 25)
    w.append(0x76)  # OP_DUP
    w.append(0xA9)  # OP_HASH160
    w.append(0x14)  # OP_DATA_20
    write_bytes_fixed(w, pubkeyhash, 20)
    w.append(0x88)  # OP_EQUALVERIFY
    w.append(0xAC)  # OP_CHECKSIG
Exemple #12
0
    async def step2_approve_outputs(self) -> None:
        write_bitcoin_varint(self.serialized_tx, self.tx_info.tx.outputs_count)
        write_bitcoin_varint(self.h_prefix, self.tx_info.tx.outputs_count)

        if self.tx_info.tx.decred_staking_ticket:
            await self.approve_staking_ticket()
        else:
            await super().step2_approve_outputs()

        self.write_tx_footer(self.serialized_tx, self.tx_info.tx)
        self.write_tx_footer(self.h_prefix, self.tx_info.tx)
Exemple #13
0
def write_input_script_p2wsh_in_p2sh(w: Writer,
                                     script_hash: bytes,
                                     prefixed: bool = False) -> None:
    # 22 00 20 <redeem script hash>
    # Signature is moved to the witness.
    if prefixed:
        write_bitcoin_varint(w, 35)

    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_fixed(w, script_hash, 32)
Exemple #14
0
def write_input_script_p2wpkh_in_p2sh(w: Writer,
                                      pubkeyhash: bytes,
                                      prefixed: bool = False) -> None:
    # 16 00 14 <pubkeyhash>
    # Signature is moved to the witness.
    if prefixed:
        write_bitcoin_varint(w, 23)

    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_fixed(w, pubkeyhash, 20)  # pub key hash
Exemple #15
0
    def __init__(
        self,
        tx: SignTx,
        keychain: Keychain,
        coin: CoinInfo,
        approver: approvers.Approver,
    ) -> None:
        ensure(coin.decred)
        super().__init__(tx, keychain, coin, approver)

        self.write_tx_header(self.serialized_tx, self.tx, witness_marker=True)
        write_bitcoin_varint(self.serialized_tx, self.tx.inputs_count)
Exemple #16
0
 def write_tx_header(
     self,
     w: writers.Writer,
     tx: Union[SignTx, TransactionType],
     witness_marker: bool,
 ) -> None:
     writers.write_uint32(w, tx.version)  # nVersion
     if self.coin.timestamp:
         writers.write_uint32(w, tx.timestamp)
     if witness_marker:
         write_bitcoin_varint(w, 0x00)  # segwit witness marker
         write_bitcoin_varint(w, 0x01)  # segwit witness flag
Exemple #17
0
 def write_tx_header(
     self,
     w: writers.Writer,
     tx: SignTx | PrevTx,
     witness_marker: bool,
 ) -> None:
     writers.write_uint32(w, tx.version)  # nVersion
     if self.coin.timestamp:
         assert tx.timestamp is not None  # checked in sanitize_*
         writers.write_uint32(w, tx.timestamp)
     if witness_marker:
         write_bitcoin_varint(w, 0x00)  # segwit witness marker
         write_bitcoin_varint(w, 0x01)  # segwit witness flag
def message_digest(coin: CoinType, message: bytes) -> bytes:
    if not utils.BITCOIN_ONLY and coin.decred:
        h = utils.HashWriter(blake256())
    else:
        h = utils.HashWriter(sha256())
    write_bitcoin_varint(h, len(coin.signed_message_header))
    h.extend(coin.signed_message_header)
    write_bitcoin_varint(h, len(message))
    h.extend(message)
    ret = h.get_digest()
    if coin.sign_hash_double:
        ret = sha256(ret).digest()
    return ret
Exemple #19
0
def message_digest(coin: CoinInfo, message: bytes) -> bytes:
    if not utils.BITCOIN_ONLY and coin.decred:
        h = utils.HashWriter(blake256())
    else:
        h = utils.HashWriter(sha256())
    if not coin.signed_message_header:
        raise wire.DataError("Empty message header not allowed.")
    write_bitcoin_varint(h, len(coin.signed_message_header))
    h.extend(coin.signed_message_header.encode())
    write_bitcoin_varint(h, len(message))
    h.extend(message)
    ret = h.get_digest()
    if coin.sign_hash_double:
        ret = sha256(ret).digest()
    return ret
Exemple #20
0
    async def get_prevtx_output(self, prev_hash: bytes,
                                prev_index: int) -> tuple[int, bytes]:
        amount_out = 0  # output amount

        # STAGE_REQUEST_3_PREV_META in legacy
        tx = await helpers.request_tx_meta(self.tx_req, self.coin, prev_hash)

        if tx.outputs_count <= prev_index:
            raise wire.ProcessError(
                "Not enough outputs in previous transaction.")

        txh = self.create_hash_writer()

        # witnesses are not included in txid hash
        self.write_tx_header(txh, tx, witness_marker=False)
        write_bitcoin_varint(txh, tx.inputs_count)

        for i in range(tx.inputs_count):
            # STAGE_REQUEST_3_PREV_INPUT in legacy
            txi = await helpers.request_tx_prev_input(self.tx_req, i,
                                                      self.coin, prev_hash)
            self.write_tx_input(txh, txi, txi.script_sig)

        write_bitcoin_varint(txh, tx.outputs_count)

        script_pubkey: bytes | None = None
        for i in range(tx.outputs_count):
            # STAGE_REQUEST_3_PREV_OUTPUT in legacy
            txo_bin = await helpers.request_tx_prev_output(
                self.tx_req, i, self.coin, prev_hash)
            self.write_tx_output(txh, txo_bin, txo_bin.script_pubkey)
            if i == prev_index:
                amount_out = txo_bin.amount
                script_pubkey = txo_bin.script_pubkey
                self.check_prevtx_output(txo_bin)

        assert script_pubkey is not None  # prev_index < tx.outputs_count

        await self.write_prev_tx_footer(txh, tx, prev_hash)

        if (writers.get_tx_hash(txh,
                                double=self.coin.sign_hash_double,
                                reverse=True) != prev_hash):
            raise wire.ProcessError("Encountered invalid prev_hash")

        return amount_out, script_pubkey
Exemple #21
0
    def __init__(
        self,
        tx: SignTx,
        keychain: Keychain,
        coin: CoinInfo,
        approver: approvers.Approver,
    ) -> None:
        ensure(coin.decred)
        self.h_prefix = HashWriter(blake256())

        super().__init__(tx, keychain, coin, approver)

        self.write_tx_header(self.serialized_tx, self.tx_info.tx, witness_marker=True)
        write_bitcoin_varint(self.serialized_tx, self.tx_info.tx.inputs_count)

        writers.write_uint32(
            self.h_prefix, self.tx_info.tx.version | DECRED_SERIALIZE_NO_WITNESS
        )
        write_bitcoin_varint(self.h_prefix, self.tx_info.tx.inputs_count)
Exemple #22
0
def generate_proof(
    node: bip32.HDNode,
    script_type: InputScriptType,
    multisig: MultisigRedeemScriptType | None,
    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 = utils.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)
    if script_type in (
            InputScriptType.SPENDADDRESS,
            InputScriptType.SPENDMULTISIG,
            InputScriptType.SPENDWITNESS,
            InputScriptType.SPENDP2SHWITNESS,
    ):
        signature = common.ecdsa_sign(node, sighash.digest())
    elif script_type == InputScriptType.SPENDTAPROOT:
        signature = common.bip340_sign(node, sighash.digest())
    else:
        raise wire.DataError("Unsupported script type.")
    public_key = node.public_key()
    write_bip322_signature_proof(proof, script_type, multisig, coin,
                                 public_key, signature)

    return proof, signature
Exemple #23
0
def write_output_script_multisig(
    w: Writer,
    pubkeys: Sequence[bytes | memoryview],
    m: int,
    prefixed: bool = False,
) -> None:
    n = len(pubkeys)
    if n < 1 or n > 15 or m < 1 or m > 15 or m > n:
        raise wire.DataError("Invalid multisig parameters")
    for pubkey in pubkeys:
        if len(pubkey) != 33:
            raise wire.DataError("Invalid multisig parameters")

    if prefixed:
        write_bitcoin_varint(w, 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
    async def step8_finish(self) -> None:
        self.write_tx_footer(self.serialized_tx, self.tx)

        if self.tx.version == 3:
            write_bitcoin_varint(self.serialized_tx, 0)  # nJoinSplit
        elif self.tx.version == 4:
            write_uint64(self.serialized_tx, 0)  # valueBalance
            write_bitcoin_varint(self.serialized_tx, 0)  # nShieldedSpend
            write_bitcoin_varint(self.serialized_tx, 0)  # nShieldedOutput
            write_bitcoin_varint(self.serialized_tx, 0)  # nJoinSplit
        else:
            raise wire.DataError("Unsupported version for overwintered transaction")

        await helpers.request_tx_finish(self.tx_req)
Exemple #25
0
    async def step4_serialize_inputs(self) -> None:
        write_bitcoin_varint(self.serialized_tx, self.tx.inputs_count)

        prefix_hash = self.h_prefix.get_digest()

        for i_sign in range(self.tx.inputs_count):
            progress.advance()

            txi_sign = await helpers.request_tx_input(self.tx_req, i_sign,
                                                      self.coin)

            self.wallet_path.check_input(txi_sign)
            self.multisig_fingerprint.check_input(txi_sign)

            key_sign = self.keychain.derive(txi_sign.address_n)
            key_sign_pub = key_sign.public_key()

            if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                assert txi_sign.multisig is not None
                prev_pkscript = scripts.output_script_multisig(
                    multisig.multisig_get_pubkeys(txi_sign.multisig),
                    txi_sign.multisig.m,
                )
            elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                prev_pkscript = scripts.output_script_p2pkh(
                    ecdsa_hash_pubkey(key_sign_pub, self.coin))
            else:
                raise wire.DataError("Unsupported input script type")

            h_witness = self.create_hash_writer()
            writers.write_uint32(
                h_witness, self.tx.version | DECRED_SERIALIZE_WITNESS_SIGNING)
            write_bitcoin_varint(h_witness, self.tx.inputs_count)

            for ii in range(self.tx.inputs_count):
                if ii == i_sign:
                    writers.write_bytes_prefixed(h_witness, prev_pkscript)
                else:
                    write_bitcoin_varint(h_witness, 0)

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

            h_sign = self.create_hash_writer()
            writers.write_uint32(h_sign, DECRED_SIGHASH_ALL)
            writers.write_bytes_fixed(h_sign, prefix_hash,
                                      writers.TX_HASH_SIZE)
            writers.write_bytes_fixed(h_sign, witness_hash,
                                      writers.TX_HASH_SIZE)

            sig_hash = writers.get_tx_hash(h_sign,
                                           double=self.coin.sign_hash_double)
            signature = ecdsa_sign(key_sign, sig_hash)

            # serialize input with correct signature
            script_sig = self.input_derive_script(txi_sign, key_sign_pub,
                                                  signature)
            self.write_tx_input_witness(self.serialized_tx, txi_sign,
                                        script_sig)
            self.set_serialized_signature(i_sign, signature)
Exemple #26
0
    async def step7_finish(self) -> None:
        self.write_tx_footer(self.serialized_tx, self.tx_info.tx)

        write_uint64(self.serialized_tx, 0)  # valueBalance
        write_bitcoin_varint(self.serialized_tx, 0)  # nShieldedSpend
        write_bitcoin_varint(self.serialized_tx, 0)  # nShieldedOutput
        write_bitcoin_varint(self.serialized_tx, 0)  # nJoinSplit

        await helpers.request_tx_finish(self.tx_req)
Exemple #27
0
def write_input_script_multisig_prefixed(
    w: Writer,
    multisig: MultisigRedeemScriptType,
    signature: bytes,
    signature_index: int,
    hash_type: int,
    coin: CoinInfo,
) -> None:
    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 = output_script_multisig_length(pubkeys, multisig.m)

    # length of the result
    total_length = 1  # OP_FALSE
    for s in signatures:
        if s:
            total_length += 1 + len(s) + 1  # length, signature, hash_type
    total_length += op_push_length(redeem_script_length) + redeem_script_length
    write_bitcoin_varint(w, total_length)

    # 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 s:
            append_signature(w, s, hash_type)

    # redeem script
    write_op_push(w, redeem_script_length)
    write_output_script_multisig(w, pubkeys, multisig.m)
Exemple #28
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
Exemple #29
0
def write_bytes_prefixed(w: Writer, b: bytes) -> None:
    write_bitcoin_varint(w, len(b))
    write_bytes_unchecked(w, b)
Exemple #30
0
 async def step5_serialize_outputs(self) -> None:
     write_bitcoin_varint(self.serialized_tx, self.tx_info.tx.outputs_count)
     for i in range(self.tx_info.tx.outputs_count):
         progress.advance()
         await self.serialize_output(i)