예제 #1
0
    def hash143_preimage_hash(
        self, txi: TxInputType, public_keys: List[bytes], threshold: int
    ) -> bytes:
        h_preimage = HashWriter(sha256())

        # nVersion
        writers.write_uint32(h_preimage, self.tx.version)

        # hashPrevouts
        prevouts_hash = writers.get_tx_hash(
            self.h_prevouts, double=self.coin.sign_hash_double
        )
        writers.write_bytes_fixed(h_preimage, prevouts_hash, writers.TX_HASH_SIZE)

        # hashSequence
        sequence_hash = writers.get_tx_hash(
            self.h_sequence, double=self.coin.sign_hash_double
        )
        writers.write_bytes_fixed(h_preimage, sequence_hash, writers.TX_HASH_SIZE)

        # outpoint
        writers.write_bytes_reversed(h_preimage, txi.prev_hash, writers.TX_HASH_SIZE)
        writers.write_uint32(h_preimage, txi.prev_index)

        # scriptCode
        script_code = scripts.bip143_derive_script_code(
            txi, public_keys, threshold, self.coin
        )
        writers.write_bytes_prefixed(h_preimage, script_code)

        # amount
        writers.write_uint64(h_preimage, txi.amount)

        # nSequence
        writers.write_uint32(h_preimage, txi.sequence)

        # hashOutputs
        outputs_hash = writers.get_tx_hash(
            self.h_outputs, double=self.coin.sign_hash_double
        )
        writers.write_bytes_fixed(h_preimage, outputs_hash, writers.TX_HASH_SIZE)

        # nLockTime
        writers.write_uint32(h_preimage, self.tx.lock_time)

        # nHashType
        writers.write_uint32(h_preimage, self.get_sighash_type(txi))

        return writers.get_tx_hash(h_preimage, double=self.coin.sign_hash_double)
예제 #2
0
async def get_prevtx_output_value(coin: CoinInfo, tx_req: TxRequest,
                                  prev_hash: bytes, prev_index: int) -> int:
    total_out = 0  # sum of output amounts

    # STAGE_REQUEST_2_PREV_META
    tx = await request_tx_meta(tx_req, prev_hash)

    txh = HashWriter(sha256)

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

    write_varint(txh, tx.inputs_cnt)

    for i in range(tx.inputs_cnt):
        # STAGE_REQUEST_2_PREV_INPUT
        txi = await request_tx_input(tx_req, i, prev_hash)
        write_tx_input(txh, txi)

    write_varint(txh, tx.outputs_cnt)

    for o in range(tx.outputs_cnt):
        # STAGE_REQUEST_2_PREV_OUTPUT
        txo_bin = await request_tx_output(tx_req, o, prev_hash)
        write_tx_output(txh, txo_bin)
        if o == prev_index:
            total_out += txo_bin.amount

    write_uint32(txh, tx.lock_time)

    if tx.overwintered:
        write_uint32(txh, tx.expiry)

    ofs = 0
    while ofs < tx.extra_data_len:
        size = min(1024, tx.extra_data_len - ofs)
        data = await request_tx_extra_data(tx_req, ofs, size, prev_hash)
        write_bytes(txh, data)
        ofs += len(data)

    if get_tx_hash(txh, double=True, reverse=True) != prev_hash:
        raise SigningError(FailureType.ProcessError,
                           'Encountered invalid prev_hash')

    return total_out
예제 #3
0
async def sign_tx(ctx: wire.Context, msg: EosSignTx,
                  keychain: Keychain) -> EosSignedTx:
    if msg.chain_id is None:
        raise wire.DataError("No chain id")
    if msg.header is None:
        raise wire.DataError("No header")
    if msg.num_actions is None or msg.num_actions == 0:
        raise wire.DataError("No actions")

    await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n,
                              CURVE)

    node = keychain.derive(msg.address_n)
    sha = HashWriter(sha256())
    await _init(ctx, sha, msg)
    await _actions(ctx, sha, msg.num_actions)
    writers.write_variant32(sha, 0)
    writers.write_bytes_unchecked(sha, bytearray(32))

    digest = sha.get_digest()
    signature = secp256k1.sign(node.private_key(), digest, True,
                               secp256k1.CANONICAL_SIG_EOS)

    return EosSignedTx(signature=base58_encode("SIG_", "K1", signature))
예제 #4
0
    async def step1_process_inputs(self) -> None:
        h_external_inputs_check = HashWriter(sha256())

        for i in range(self.tx_info.tx.inputs_count):
            # STAGE_REQUEST_1_INPUT in legacy
            txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
            script_pubkey = self.input_derive_script(txi)
            self.tx_info.add_input(txi, script_pubkey)
            if txi.script_type not in (
                InputScriptType.SPENDTAPROOT,
                InputScriptType.EXTERNAL,
            ):
                self.taproot_only = False

            if input_is_segwit(txi):
                self.segwit.add(i)

            if input_is_external(txi):
                self.external.add(i)
                writers.write_tx_input_check(h_external_inputs_check, txi)
                await self.process_external_input(txi)
            else:
                await self.process_internal_input(txi)

            if txi.orig_hash:
                await self.process_original_input(txi, script_pubkey)

        self.h_inputs = self.tx_info.get_tx_check_digest()
        self.h_external_inputs = h_external_inputs_check.get_digest()

        # Finalize original inputs.
        for orig in self.orig_txs:
            if orig.index != orig.tx.inputs_count:
                raise wire.ProcessError("Removal of original inputs is not supported.")

            orig.index = 0  # Reset counter for outputs.
예제 #5
0
    def preimage_hash(
        self,
        coin: CoinInfo,
        tx: SignTx,
        txi: TxInputType,
        pubkeyhash: bytes,
        sighash: int,
    ) -> bytes:
        h_preimage = HashWriter(
            blake2b(outlen=32,
                    personal=b"ZcashSigHash" +
                    struct.pack("<I", self.branch_id)))

        ensure(coin.overwintered)
        ensure(tx.version == 3)

        write_uint32(h_preimage,
                     tx.version | OVERWINTERED)  # 1. nVersion | fOverwintered
        write_uint32(h_preimage, tx.version_group_id)  # 2. nVersionGroupId
        # 3. hashPrevouts
        write_bytes_fixed(h_preimage, bytearray(self.get_prevouts_hash()),
                          TX_HASH_SIZE)
        # 4. hashSequence
        write_bytes_fixed(h_preimage, bytearray(self.get_sequence_hash()),
                          TX_HASH_SIZE)
        # 5. hashOutputs
        write_bytes_fixed(h_preimage, bytearray(self.get_outputs_hash()),
                          TX_HASH_SIZE)
        # 6. hashJoinSplits
        write_bytes_fixed(h_preimage, b"\x00" * TX_HASH_SIZE, TX_HASH_SIZE)
        write_uint32(h_preimage, tx.lock_time)  # 7. nLockTime
        write_uint32(h_preimage, tx.expiry)  # 8. expiryHeight
        write_uint32(h_preimage, sighash)  # 9. nHashType

        write_bytes_reversed(h_preimage, txi.prev_hash,
                             TX_HASH_SIZE)  # 10a. outpoint
        write_uint32(h_preimage, txi.prev_index)

        script_code = derive_script_code(txi, pubkeyhash)  # 10b. scriptCode
        write_bytes_prefixed(h_preimage, script_code)

        write_uint64(h_preimage, txi.amount)  # 10c. value

        write_uint32(h_preimage, txi.sequence)  # 10d. nSequence

        return get_tx_hash(h_preimage)
예제 #6
0
    def preimage_hash(
        self,
        coin: CoinInfo,
        tx: SignTx,
        txi: TxInputType,
        pubkeyhash: bytes,
        sighash: int,
    ) -> bytes:
        h_preimage = HashWriter(
            blake2b(outlen=32, personal=b"ZcashSigHash\xbb\x09\xb8\x76")
        )  # BRANCH_ID = 0x76b809bb / Sapling

        ensure(tx.overwintered)
        ensure(tx.version == 4)

        write_uint32(h_preimage,
                     tx.version | OVERWINTERED)  # 1. nVersion | fOverwintered
        write_uint32(h_preimage, tx.version_group_id)  # 2. nVersionGroupId
        write_bytes(h_preimage,
                    bytearray(self.get_prevouts_hash()))  # 3. hashPrevouts
        write_bytes(h_preimage,
                    bytearray(self.get_sequence_hash()))  # 4. hashSequence
        write_bytes(h_preimage,
                    bytearray(self.get_outputs_hash()))  # 5. hashOutputs
        write_bytes(h_preimage, b"\x00" * 32)  # 6. hashJoinSplits
        write_bytes(h_preimage, b"\x00" * 32)  # 7. hashShieldedSpends
        write_bytes(h_preimage, b"\x00" * 32)  # 8. hashShieldedOutputs
        write_uint32(h_preimage,
                     tx.lock_time - self.hash_lock_offset)  # 9. nLockTime
        write_uint32(h_preimage, tx.expiry)  # 10. expiryHeight
        write_uint64(h_preimage, 0)  # 11. valueBalance
        write_uint32(h_preimage, sighash)  # 12. nHashType

        write_bytes_reversed(h_preimage, txi.prev_hash)  # 13a. outpoint
        write_uint32(h_preimage, txi.prev_index)

        script_code = derive_script_code(txi, pubkeyhash)  # 13b. scriptCode
        write_varint(h_preimage, len(script_code))
        write_bytes(h_preimage, script_code)

        write_uint64(h_preimage, txi.amount)  # 13c. value

        write_uint32(h_preimage, txi.sequence)  # 13d. nSequence

        return get_tx_hash(h_preimage)
예제 #7
0
class DecredPrefixHasher:
    """
    While Decred does not have the exact same implementation as bip143/zip143,
    the semantics for using the prefix hash of transactions are close enough
    that a pseudo-bip143 class can be used to store the prefix hash during the
    check_fee stage of transaction signature to then reuse it at the sign_tx
    stage without having to request the inputs again.
    """
    def __init__(self, tx: SignTx):
        self.h_prefix = HashWriter(blake256())
        self.last_output_bytes = None
        write_uint32(self.h_prefix, tx.version | DECRED_SERIALIZE_NO_WITNESS)
        write_varint(self.h_prefix, tx.inputs_count)

    def add_prevouts(self, txi: TxInputType):
        write_tx_input_decred(self.h_prefix, txi)

    def add_sequence(self, txi: TxInputType):
        pass

    def add_output_count(self, tx: SignTx):
        write_varint(self.h_prefix, tx.outputs_count)

    def add_output(self, txo_bin: TxOutputBinType):
        write_tx_output(self.h_prefix, txo_bin)

    def set_last_output_bytes(self, w_txo_bin: bytearray):
        """
        This is required because the last serialized output obtained in
        `check_fee` will only be sent to the client in `sign_tx`
        """
        self.last_output_bytes = w_txo_bin

    def get_last_output_bytes(self):
        return self.last_output_bytes

    def add_locktime_expiry(self, tx: SignTx):
        write_uint32(self.h_prefix, tx.lock_time)
        write_uint32(self.h_prefix, tx.expiry)

    def prefix_hash(self) -> bytes:
        return self.h_prefix.get_digest()
예제 #8
0
    def preimage_hash(
        self,
        coin: CoinInfo,
        tx: SignTx,
        txi: TxInputType,
        pubkeyhash: bytes,
        sighash: int,
    ) -> bytes:
        h_preimage = HashWriter(blake2b,
                                outlen=32,
                                personal=b"ZcashSigHash\x19\x1b\xa8\x5b"
                                )  # BRANCH_ID = 0x5ba81b19 / Overwinter

        ensure(tx.overwintered)
        ensure(tx.version == 3)

        write_uint32(h_preimage,
                     tx.version | OVERWINTERED)  # 1. nVersion | fOverwintered
        write_uint32(h_preimage, tx.version_group_id)  # 2. nVersionGroupId
        write_bytes(h_preimage,
                    bytearray(self.get_prevouts_hash()))  # 3. hashPrevouts
        write_bytes(h_preimage,
                    bytearray(self.get_sequence_hash()))  # 4. hashSequence
        write_bytes(h_preimage,
                    bytearray(self.get_outputs_hash()))  # 5. hashOutputs
        write_bytes(h_preimage, b"\x00" * 32)  # 6. hashJoinSplits
        write_uint32(h_preimage, tx.lock_time)  # 7. nLockTime
        write_uint32(h_preimage, tx.expiry)  # 8. expiryHeight
        write_uint32(h_preimage, sighash)  # 9. nHashType

        write_bytes_reversed(h_preimage, txi.prev_hash)  # 10a. outpoint
        write_uint32(h_preimage, txi.prev_index)

        script_code = derive_script_code(txi, pubkeyhash)  # 10b. scriptCode
        write_varint(h_preimage, len(script_code))
        write_bytes(h_preimage, script_code)

        write_uint64(h_preimage, txi.amount)  # 10c. value

        write_uint32(h_preimage, txi.sequence)  # 10d. nSequence

        return get_tx_hash(h_preimage)
예제 #9
0
    def preimage_hash(self, coin: CoinInfo, tx: SignTx, txi: TxInputType, pubkeyhash: bytes, sighash: int) -> bytes:
        h_preimage = HashWriter(sha256)

        assert not tx.overwintered

        write_uint32(h_preimage, tx.version)                          # nVersion
        write_bytes(h_preimage, bytearray(self.get_prevouts_hash()))  # hashPrevouts
        write_bytes(h_preimage, bytearray(self.get_sequence_hash()))  # hashSequence

        write_bytes_rev(h_preimage, txi.prev_hash)                    # outpoint
        write_uint32(h_preimage, txi.prev_index)                      # outpoint

        script_code = self.derive_script_code(txi, pubkeyhash)        # scriptCode
        write_varint(h_preimage, len(script_code))
        write_bytes(h_preimage, script_code)

        write_uint64(h_preimage, txi.amount)                          # amount
        write_uint32(h_preimage, txi.sequence)                        # nSequence
        write_bytes(h_preimage, bytearray(self.get_outputs_hash()))   # hashOutputs
        write_uint32(h_preimage, tx.lock_time)                        # nLockTime
        write_uint32(h_preimage, sighash)                             # nHashType

        return get_tx_hash(h_preimage, True)
예제 #10
0
async def process_unknown_action(ctx, w, action):
    checksum = HashWriter(sha256())
    writers.write_variant32(checksum, action.unknown.data_size)
    checksum.extend(action.unknown.data_chunk)

    writers.write_bytes(w, action.unknown.data_chunk)
    bytes_left = action.unknown.data_size - len(action.unknown.data_chunk)

    while bytes_left != 0:
        action = await ctx.call(EosTxActionRequest(data_size=bytes_left),
                                EosTxActionAck)

        if action.unknown is None:
            raise ValueError("Bad response. Unknown struct expected.")

        checksum.extend(action.unknown.data_chunk)
        writers.write_bytes(w, action.unknown.data_chunk)

        bytes_left -= len(action.unknown.data_chunk)
        if bytes_left < 0:
            raise ValueError("Bad response. Buffer overflow.")

    await layout.confirm_action_unknown(ctx, action.common,
                                        checksum.get_digest())
예제 #11
0
    async def get_legacy_tx_digest(
        self,
        index: int,
        tx_info: Union[TxInfo, OriginalTxInfo],
        script_pubkey: Optional[bytes] = None,
    ) -> Tuple[bytes, TxInput, Optional[bip32.HDNode]]:
        tx_hash = tx_info.orig_hash if isinstance(tx_info,
                                                  OriginalTxInfo) else None

        # the transaction digest which gets signed for this input
        h_sign = self.create_hash_writer()
        # should come out the same as h_tx_check, checked before signing the digest
        h_check = HashWriter(sha256())

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

        for i in range(tx_info.tx.inputs_count):
            # STAGE_REQUEST_4_INPUT in legacy
            txi = await helpers.request_tx_input(self.tx_req, i, self.coin,
                                                 tx_hash)
            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.tx_info.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:
                        assert txi.multisig is not None  # checked in sanitize_tx_input
                        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, tx_info.tx.outputs_count)

        for i in range(tx_info.tx.outputs_count):
            # STAGE_REQUEST_4_OUTPUT in legacy
            txo = await helpers.request_tx_output(self.tx_req, i, self.coin,
                                                  tx_hash)
            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, tx_info.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 approval
        if tx_info.get_tx_check_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
예제 #12
0
 def init_hash143(self) -> None:
     self.h_prevouts = HashWriter(sha256())
     self.h_sequence = HashWriter(sha256())
     self.h_outputs = HashWriter(sha256())
예제 #13
0
 def __init__(self):
     self.h_prevouts = HashWriter(blake2b, b'', 32, b'ZcashPrevoutHash')
     self.h_sequence = HashWriter(blake2b, b'', 32, b'ZcashSequencHash')
     self.h_outputs = HashWriter(blake2b, b'', 32, b'ZcashOutputsHash')
예제 #14
0
def address_multisig_p2wsh(pubkeys: list[bytes], m: int, hrp: str) -> str:
    if not hrp:
        raise wire.ProcessError("Multisig not enabled on this coin")
    witness_script_h = HashWriter(sha256())
    write_output_script_multisig(witness_script_h, pubkeys, m)
    return address_p2wsh(witness_script_h.get_digest(), hrp)
예제 #15
0
 def create_hash_writer(self) -> HashWriter:
     return HashWriter(blake256())
예제 #16
0
async def get_prevtx_output_value(coin: CoinInfo, tx_req: TxRequest,
                                  prev_hash: bytes, prev_index: int) -> int:
    total_out = 0  # sum of output amounts

    # STAGE_REQUEST_2_PREV_META
    tx = await request_tx_meta(tx_req, prev_hash)

    if coin.decred:
        txh = HashWriter(blake256())
    else:
        txh = HashWriter(sha256())

    if tx.overwintered:
        write_uint32(txh,
                     tx.version | OVERWINTERED)  # nVersion | fOverwintered
        write_uint32(txh, tx.version_group_id)  # nVersionGroupId
    elif coin.decred:
        write_uint32(txh, tx.version | DECRED_SERIALIZE_NO_WITNESS)
    else:
        write_uint32(txh, tx.version)  # nVersion

    write_varint(txh, tx.inputs_cnt)

    for i in range(tx.inputs_cnt):
        # STAGE_REQUEST_2_PREV_INPUT
        txi = await request_tx_input(tx_req, i, prev_hash)
        if coin.decred:
            write_tx_input_decred(txh, txi)
        else:
            write_tx_input(txh, txi)

    write_varint(txh, tx.outputs_cnt)

    for o in range(tx.outputs_cnt):
        # STAGE_REQUEST_2_PREV_OUTPUT
        txo_bin = await request_tx_output(tx_req, o, prev_hash)
        write_tx_output(txh, txo_bin)
        if o == prev_index:
            total_out += txo_bin.amount
            if (coin.decred and txo_bin.decred_script_version is not None
                    and txo_bin.decred_script_version != 0):
                raise SigningError(
                    FailureType.ProcessError,
                    "Cannot use utxo that has script_version != 0",
                )

    write_uint32(txh, tx.lock_time)

    if tx.overwintered or coin.decred:
        write_uint32(txh, tx.expiry)

    ofs = 0
    while ofs < tx.extra_data_len:
        size = min(1024, tx.extra_data_len - ofs)
        data = await request_tx_extra_data(tx_req, ofs, size, prev_hash)
        write_bytes(txh, data)
        ofs += len(data)

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

    return total_out
예제 #17
0
def tagged_hashwriter(tag: bytes) -> HashWriter:
    tag_digest = sha256(tag).digest()
    ctx = sha256(tag_digest)
    ctx.update(tag_digest)
    return HashWriter(ctx)
예제 #18
0
 def __init__(self):
     self.h_prevouts = HashWriter(sha256)
     self.h_sequence = HashWriter(sha256)
     self.h_outputs = HashWriter(sha256)
예제 #19
0
 def __init__(self) -> None:
     self.h_prevouts = HashWriter(sha256())
     self.h_amounts = HashWriter(sha256())
     self.h_scriptpubkeys = HashWriter(sha256())
     self.h_sequences = HashWriter(sha256())
     self.h_outputs = HashWriter(sha256())
예제 #20
0
 def init_hash143(self) -> None:
     self.h_prevouts = HashWriter(blake2b(outlen=32, personal=b"ZcashPrevoutHash"))
     self.h_sequence = HashWriter(blake2b(outlen=32, personal=b"ZcashSequencHash"))
     self.h_outputs = HashWriter(blake2b(outlen=32, personal=b"ZcashOutputsHash"))
예제 #21
0
    def hash143_preimage_hash(self, txi: TxInputType, pubkeyhash: bytes) -> bytes:
        h_preimage = HashWriter(
            blake2b(
                outlen=32,
                personal=b"ZcashSigHash" + struct.pack("<I", self.tx.branch_id),
            )
        )

        # 1. nVersion | fOverwintered
        write_uint32(h_preimage, self.tx.version | OVERWINTERED)
        # 2. nVersionGroupId
        write_uint32(h_preimage, self.tx.version_group_id)
        # 3. hashPrevouts
        write_bytes_fixed(h_preimage, get_tx_hash(self.h_prevouts), TX_HASH_SIZE)
        # 4. hashSequence
        write_bytes_fixed(h_preimage, get_tx_hash(self.h_sequence), TX_HASH_SIZE)
        # 5. hashOutputs
        write_bytes_fixed(h_preimage, get_tx_hash(self.h_outputs), TX_HASH_SIZE)

        if self.tx.version == 3:
            # 6. hashJoinSplits
            write_bytes_fixed(h_preimage, b"\x00" * TX_HASH_SIZE, TX_HASH_SIZE)
            # 7. nLockTime
            write_uint32(h_preimage, self.tx.lock_time)
            # 8. expiryHeight
            write_uint32(h_preimage, self.tx.expiry)
            # 9. nHashType
            write_uint32(h_preimage, self.get_hash_type())
        elif self.tx.version == 4:
            zero_hash = b"\x00" * TX_HASH_SIZE
            # 6. hashJoinSplits
            write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE)
            # 7. hashShieldedSpends
            write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE)
            # 8. hashShieldedOutputs
            write_bytes_fixed(h_preimage, zero_hash, TX_HASH_SIZE)
            # 9. nLockTime
            write_uint32(h_preimage, self.tx.lock_time)
            # 10. expiryHeight
            write_uint32(h_preimage, self.tx.expiry)
            # 11. valueBalance
            write_uint64(h_preimage, 0)
            # 12. nHashType
            write_uint32(h_preimage, self.get_hash_type())
        else:
            raise wire.DataError("Unsupported version for overwintered transaction")

        # 10a /13a. outpoint
        write_bytes_reversed(h_preimage, txi.prev_hash, TX_HASH_SIZE)
        write_uint32(h_preimage, txi.prev_index)

        # 10b / 13b. scriptCode
        script_code = derive_script_code(txi, pubkeyhash)
        write_bytes_prefixed(h_preimage, script_code)

        # 10c / 13c. value
        write_uint64(h_preimage, txi.amount)

        # 10d / 13d. nSequence
        write_uint32(h_preimage, txi.sequence)

        return get_tx_hash(h_preimage)
예제 #22
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, bip143, 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

    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 = bytearray_with_cap(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(tx, True))
            write_tx_input(w_txi, txi_sign)
            tx_ser.serialized_tx = w_txi
            tx_req.serialized = tx_ser

        elif coin.force_bip143:
            # 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()
            bip143_hash = bip143.preimage_hash(tx, txi_sign,
                                               ecdsa_hash_pubkey(key_sign_pub),
                                               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, bip143_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 = bytearray_with_cap(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(tx))
            write_tx_input(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)

            write_uint32(h_sign, tx.version)
            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))
                    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)

            write_uint32(h_sign, get_hash_type(coin))

            # check the control digests
            if get_tx_hash(h_first, False) != get_tx_hash(h_second, False):
                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, True))
            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 = bytearray_with_cap(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(tx))
            write_tx_input(w_txi_sign, txi_sign)
            tx_ser.serialized_tx = w_txi_sign

            tx_req.serialized = tx_ser

    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 = bytearray_with_cap(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()
            bip143_hash = bip143.preimage_hash(tx, txi,
                                               ecdsa_hash_pubkey(key_sign_pub),
                                               get_hash_type(coin))

            signature = ecdsa_sign(key_sign, bip143_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)

    await request_tx_finish(tx_req)
예제 #23
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)
예제 #24
0
async def ethereum_sign_tx(ctx, msg):
    from trezor.crypto.hashlib import sha3_256

    msg = sanitize(msg)
    check(msg)

    data_total = msg.data_length

    # detect ERC - 20 token
    token = None
    recipient = msg.to
    value = int.from_bytes(msg.value, "big")
    if (len(msg.to) == 20 and len(msg.value) == 0 and data_total == 68
            and len(msg.data_initial_chunk) == 68
            and msg.data_initial_chunk[:16] ==
            b"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        ):
        token = tokens.token_by_chain_address(msg.chain_id, msg.to)
        recipient = msg.data_initial_chunk[16:36]
        value = int.from_bytes(msg.data_initial_chunk[36:68], "big")

    await require_confirm_tx(ctx, recipient, value, msg.chain_id, token,
                             msg.tx_type)
    if token is None and msg.data_length > 0:
        await require_confirm_data(ctx, msg.data_initial_chunk, data_total)

    await require_confirm_fee(
        ctx,
        value,
        int.from_bytes(msg.gas_price, "big"),
        int.from_bytes(msg.gas_limit, "big"),
        msg.chain_id,
        token,
        msg.tx_type,
    )

    data = bytearray()
    data += msg.data_initial_chunk
    data_left = data_total - len(msg.data_initial_chunk)

    total_length = get_total_length(msg, data_total)

    sha = HashWriter(sha3_256)
    sha.extend(rlp.encode_length(total_length, True))  # total length

    if msg.tx_type is not None:
        sha.extend(rlp.encode(msg.tx_type))

    for field in [msg.nonce, msg.gas_price, msg.gas_limit, msg.to, msg.value]:
        sha.extend(rlp.encode(field))

    if data_left == 0:
        sha.extend(rlp.encode(data))
    else:
        sha.extend(rlp.encode_length(data_total, False))
        sha.extend(rlp.encode(data, False))

    while data_left > 0:
        resp = await send_request_chunk(ctx, data_left)
        data_left -= len(resp.data_chunk)
        sha.extend(resp.data_chunk)

    # eip 155 replay protection
    if msg.chain_id:
        sha.extend(rlp.encode(msg.chain_id))
        sha.extend(rlp.encode(0))
        sha.extend(rlp.encode(0))

    digest = sha.get_digest(True)  # True -> use keccak mode
    return await send_signature(ctx, msg, digest)
예제 #25
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
예제 #26
0
 def __init__(self) -> None:
     self.h_prevouts = HashWriter(sha256())
     self.h_sequence = HashWriter(sha256())
     self.h_outputs = HashWriter(sha256())
예제 #27
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

    bip143 = Bip143()  # bip143 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)
        bip143.add_prevouts(
            txi)  # all inputs are included (non-segwit as well)
        bip143.add_sequence(txi)

        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:
                if not txi.amount:
                    raise SigningError(FailureType.DataError,
                                       'BIP 143 input without amount')
                segwit[i] = False
                segwit_in += txi.amount
                total_in += txi.amount
            else:
                segwit[i] = False
                total_in += await get_prevtx_output_value(
                    tx_req, txi.prev_hash, txi.prev_index)

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

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

        write_tx_output(h_first, txo_bin)
        bip143.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_out - change_out, fee, coin):
        raise SigningError(FailureType.ActionCancelled, 'Total cancelled')

    return h_first, bip143, segwit, total_in, wallet_path
예제 #28
0
def address_multisig_p2sh(pubkeys: list[bytes], m: int, coin: CoinInfo) -> str:
    if coin.address_type_p2sh is None:
        raise wire.ProcessError("Multisig not enabled on this coin")
    redeem_script = HashWriter(coin.script_hash())
    write_output_script_multisig(redeem_script, pubkeys, m)
    return address_p2sh(redeem_script.get_digest(), coin)
예제 #29
0
class Decred(Bitcoin):
    def __init__(
        self,
        tx: SignTx,
        keychain: Keychain,
        coin: CoinInfo,
        approver: approvers.Approver | None,
    ) -> None:
        ensure(coin.decred)
        self.h_prefix = HashWriter(blake256())

        ensure(approver is None)
        approver = DecredApprover(tx, coin)
        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)

    def create_hash_writer(self) -> HashWriter:
        return HashWriter(blake256())

    def create_sig_hasher(self) -> SigHasher:
        return DecredSigHasher(self.h_prefix)

    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)

    async def process_internal_input(self, txi: TxInput) -> None:
        await super().process_internal_input(txi)

        # Decred serializes inputs early.
        self.write_tx_input(self.serialized_tx, txi, bytes())

    async def process_external_input(self, txi: TxInput) -> None:
        raise wire.DataError("External inputs not supported")

    async def process_original_input(self, txi: TxInput,
                                     script_pubkey: bytes) -> None:
        raise wire.DataError("Replacement transactions not supported")

    async def approve_output(
        self,
        txo: TxOutput,
        script_pubkey: bytes,
        orig_txo: TxOutput | None,
    ) -> None:
        await super().approve_output(txo, script_pubkey, orig_txo)
        self.write_tx_output(self.serialized_tx, txo, script_pubkey)

    async def step4_serialize_inputs(self) -> None:
        write_bitcoin_varint(self.serialized_tx, self.tx_info.tx.inputs_count)

        prefix_hash = self.h_prefix.get_digest()

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

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

            self.tx_info.check_input(txi_sign)

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

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

            for ii in range(self.tx_info.tx.inputs_count):
                if ii == i_sign:
                    if txi_sign.decred_staking_spend == DecredStakingSpendType.SSRTX:
                        scripts_decred.write_output_script_ssrtx_prefixed(
                            h_witness,
                            ecdsa_hash_pubkey(key_sign_pub, self.coin))
                    elif txi_sign.decred_staking_spend == DecredStakingSpendType.SSGen:
                        scripts_decred.write_output_script_ssgen_prefixed(
                            h_witness,
                            ecdsa_hash_pubkey(key_sign_pub, self.coin))
                    elif txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                        assert txi_sign.multisig is not None
                        scripts_decred.write_output_script_multisig(
                            h_witness,
                            multisig.multisig_get_pubkeys(txi_sign.multisig),
                            txi_sign.multisig.m,
                            prefixed=True,
                        )
                    elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                        scripts_decred.write_output_script_p2pkh(
                            h_witness,
                            ecdsa_hash_pubkey(key_sign_pub, self.coin),
                            prefixed=True,
                        )
                    else:
                        raise wire.DataError("Unsupported input script type")
                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, SigHashType.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
            self.write_tx_input_witness(self.serialized_tx, txi_sign,
                                        key_sign_pub, signature)
            self.set_serialized_signature(i_sign, signature)

    async def step5_serialize_outputs(self) -> None:
        pass

    async def step6_sign_segwit_inputs(self) -> None:
        pass

    async def step7_finish(self) -> None:
        await helpers.request_tx_finish(self.tx_req)

    def check_prevtx_output(self, txo_bin: PrevOutput) -> None:
        if txo_bin.decred_script_version != 0:
            raise wire.ProcessError(
                "Cannot use utxo that has script_version != 0")

    @staticmethod
    def write_tx_input(
        w: writers.Writer,
        txi: TxInput | PrevInput,
        script: bytes,
    ) -> None:
        writers.write_bytes_reversed(w, txi.prev_hash, writers.TX_HASH_SIZE)
        writers.write_uint32(w, txi.prev_index or 0)
        writers.write_uint8(w, txi.decred_tree or 0)
        writers.write_uint32(w, txi.sequence)

    @staticmethod
    def write_tx_output(
        w: writers.Writer,
        txo: TxOutput | PrevOutput,
        script_pubkey: bytes,
    ) -> None:
        writers.write_uint64(w, txo.amount)
        if PrevOutput.is_type_of(txo):
            if txo.decred_script_version is None:
                raise wire.DataError("Script version must be provided")
            writers.write_uint16(w, txo.decred_script_version)
        else:
            writers.write_uint16(w, DECRED_SCRIPT_VERSION)
        writers.write_bytes_prefixed(w, script_pubkey)

    def process_sstx_commitment_owned(self, txo: TxOutput) -> bytearray:
        if not self.tx_info.output_is_change(txo):
            raise wire.DataError("Invalid sstxcommitment path.")
        node = self.keychain.derive(txo.address_n)
        pkh = ecdsa_hash_pubkey(node.public_key(), self.coin)
        op_return_data = scripts_decred.sstxcommitment_pkh(pkh, txo.amount)
        txo.amount = 0  # Clear the amount, since this is an OP_RETURN.
        return scripts_decred.output_script_paytoopreturn(op_return_data)

    async def approve_staking_ticket(self) -> None:
        assert isinstance(self.approver, DecredApprover)

        if self.tx_info.tx.outputs_count != 3:
            raise wire.DataError("Ticket has wrong number of outputs.")

        # SSTX submission
        txo = await helpers.request_tx_output(self.tx_req, 0, self.coin)
        if txo.address is None:
            raise wire.DataError("Missing address.")
        script_pubkey = scripts_decred.output_script_sstxsubmissionpkh(
            txo.address)
        await self.approver.add_decred_sstx_submission(txo, script_pubkey)
        self.tx_info.add_output(txo, script_pubkey)
        self.write_tx_output(self.serialized_tx, txo, script_pubkey)

        # SSTX commitment
        txo = await helpers.request_tx_output(self.tx_req, 1, self.coin)
        if txo.amount != self.approver.total_in:
            raise wire.DataError("Wrong sstxcommitment amount.")
        script_pubkey = self.process_sstx_commitment_owned(txo)
        self.approver.add_change_output(txo, script_pubkey)
        self.tx_info.add_output(txo, script_pubkey)
        self.write_tx_output(self.serialized_tx, txo, script_pubkey)

        # SSTX change
        txo = await helpers.request_tx_output(self.tx_req, 2, self.coin)
        if txo.address is None:
            raise wire.DataError("Missing address.")
        script_pubkey = scripts_decred.output_script_sstxchange(txo.address)
        # Using change addresses is no longer common practice. Inputs are split
        # beforehand and should be exact. SSTX change should pay zero amount to
        # a zeroed hash.
        if txo.amount != 0:
            raise wire.DataError("Only value of 0 allowed for sstx change.")
        if script_pubkey != OUTPUT_SCRIPT_NULL_SSTXCHANGE:
            raise wire.DataError(
                "Only zeroed addresses accepted for sstx change.")
        self.approver.add_change_output(txo, script_pubkey)
        self.tx_info.add_output(txo, script_pubkey)
        self.write_tx_output(self.serialized_tx, txo, script_pubkey)

    def write_tx_header(
        self,
        w: writers.Writer,
        tx: SignTx | PrevTx,
        witness_marker: bool,
    ) -> None:
        # The upper 16 bits of the transaction version specify the serialization
        # format and the lower 16 bits specify the version number.
        if witness_marker:
            version = tx.version | DECRED_SERIALIZE_FULL
        else:
            version = tx.version | DECRED_SERIALIZE_NO_WITNESS

        writers.write_uint32(w, version)

    def write_tx_footer(self, w: writers.Writer, tx: SignTx | PrevTx) -> None:
        assert tx.expiry is not None  # checked in sanitize_*
        writers.write_uint32(w, tx.lock_time)
        writers.write_uint32(w, tx.expiry)

    def write_tx_input_witness(self, w: writers.Writer, txi: TxInput,
                               pubkey: bytes, signature: bytes) -> None:
        writers.write_uint64(w, txi.amount)
        writers.write_uint32(w, 0)  # block height fraud proof
        writers.write_uint32(w, 0xFFFF_FFFF)  # block index fraud proof
        scripts_decred.write_input_script_prefixed(
            w,
            txi.script_type,
            txi.multisig,
            self.coin,
            self.get_sighash_type(txi),
            pubkey,
            signature,
        )
예제 #30
0
def address_multisig_p2wsh_in_p2sh(pubkeys: list[bytes], m: int, coin: CoinInfo) -> str:
    if coin.address_type_p2sh is None:
        raise wire.ProcessError("Multisig not enabled on this coin")
    witness_script_h = HashWriter(sha256())
    write_output_script_multisig(witness_script_h, pubkeys, m)
    return address_p2wsh_in_p2sh(witness_script_h.get_digest(), coin)