コード例 #1
0
    def __init__(self, tx: SignTx, keychain: seed.Keychain,
                 coin: coininfo.CoinInfo) -> None:
        self.coin = coin
        self.tx = helpers.sanitize_sign_tx(tx, coin)
        self.keychain = keychain

        # checksum of multisig inputs, used to validate change-output
        self.multisig_fingerprint = MultisigFingerprintChecker()

        # common prefix of input paths, used to validate change-output
        self.wallet_path = WalletPathChecker()

        # set of indices of inputs which are segwit
        self.segwit = set()  # type: Set[int]

        # amounts
        self.total_in = 0  # sum of input amounts
        self.bip143_in = 0  # sum of segwit input amounts
        self.total_out = 0  # sum of output amounts
        self.change_out = 0  # change output amount
        self.weight = tx_weight.TxWeightCalculator(tx.inputs_count,
                                                   tx.outputs_count)

        # transaction and signature serialization
        self.serialized_tx = writers.empty_bytearray(
            _MAX_SERIALIZED_CHUNK_SIZE)
        self.tx_req = TxRequest()
        self.tx_req.details = TxRequestDetailsType()
        self.tx_req.serialized = TxRequestSerializedType()
        self.tx_req.serialized.serialized_tx = self.serialized_tx

        # h_confirmed is used to make sure that the inputs and outputs streamed for
        # confirmation in Steps 1 and 2 are the same as the ones streamed for signing
        # legacy inputs in Step 4.
        self.h_confirmed = self.create_hash_writer()  # not a real tx hash

        # BIP-0143 transaction hashing
        self.init_hash143()
コード例 #2
0
ファイル: signing.py プロジェクト: pmclassic/trezor-core
async def check_tx_fee(tx: SignTx, keychain: seed.Keychain):
    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 = utils.HashWriter(sha256())  # not a real tx hash

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

    multifp = multisig.MultisigFingerprint(
    )  # control checksum of multisig inputs
    weight = tx_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 helpers.request_tx_input(tx_req, i)
        wallet_path = input_extract_wallet_path(txi, wallet_path)
        writers.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 addresses.validate_full_path(txi.address_n, coin,
                                            txi.script_type):
            await helpers.confirm_foreign_address(txi.address_n)

        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 = writers.empty_bytearray(8 if i == 0 else 0 + 9 +
                                            len(txi.prev_hash))
            if i == 0:  # serializing first input => prepend headers
                writers.write_bytes(w_txi, get_tx_header(coin, tx))
            writers.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 helpers.request_tx_output(tx_req, o)
        txo_bin.amount = txo.amount
        txo_bin.script_pubkey = output_derive_script(txo, coin, keychain)
        weight.add_output(txo_bin.script_pubkey)

        if change_out == 0 and output_is_change(txo, wallet_path, segwit_in,
                                                multifp):
            # output is change and does not need confirmation
            change_out = txo.amount
        elif not await helpers.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 = writers.empty_bytearray(4 + 8 + 2 + 4 +
                                                len(txo_bin.script_pubkey))
            if o == 0:  # serializing first output => prepend outputs count
                writers.write_varint(w_txo_bin, tx.outputs_count)
            writers.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)

        writers.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 helpers.confirm_feeoverthreshold(fee, coin):
            raise SigningError(FailureType.ActionCancelled,
                               "Signing cancelled")

    if not await helpers.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