Exemple #1
0
    def test_paths_bch(self):
        incorrect_derivation_paths = [
            ([44 | HARDENED], InputScriptType.SPENDADDRESS),  # invalid length
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], InputScriptType.SPENDADDRESS),  # too many HARDENED
            ([49 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),  # bch is not segwit coin so 49' is not allowed
            ([84 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 1], InputScriptType.SPENDWITNESS),  # and neither is 84'
            ([44 | HARDENED, 145 | HARDENED], InputScriptType.SPENDADDRESS),  # invalid length
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0, 0, 0], InputScriptType.SPENDADDRESS),  # invalid length
            ([44 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDADDRESS),  # invalid slip44
            ([44 | HARDENED, 145 | HARDENED, 1000 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),  # account too high
            ([44 | HARDENED, 145 | HARDENED, 1 | HARDENED, 2, 0], InputScriptType.SPENDADDRESS),  # invalid y
            ([44 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDADDRESS),  # address index too high
            ([84 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDWITNESS),  # address index too high
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS),  # input type mismatch
        ]
        correct_derivation_paths = [
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 1123], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 1, 44444], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 145 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),
            ([48 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDMULTISIG),
            ([48 | HARDENED, 145 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDMULTISIG),
            ([48 | HARDENED, 145 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDMULTISIG),
        ]
        coin = coins.by_name('Bcash')  # segwit is disabled
        for path, input_type in incorrect_derivation_paths:
            self.assertFalse(validate_full_path(path, coin, input_type))

        for path, input_type in correct_derivation_paths:
            self.assertTrue(validate_full_path(path, coin, input_type))
Exemple #2
0
    def test_paths_other(self):
        incorrect_derivation_paths = [
            ([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDMULTISIG),  # input type mismatch
        ]
        correct_derivation_paths = [
            ([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 1123], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 1, 44444], InputScriptType.SPENDADDRESS),
        ]
        coin = coins.by_name('Dogecoin')  # segwit is disabled
        for path, input_type in correct_derivation_paths:
            self.assertTrue(validate_full_path(path, coin, input_type))

        for path, input_type in incorrect_derivation_paths:
            self.assertFalse(validate_full_path(path, coin, input_type))
Exemple #3
0
    def test_paths_btc(self):
        incorrect_derivation_paths = [
            ([49 | HARDENED], InputScriptType.SPENDP2SHWITNESS),  # invalid length
            ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], InputScriptType.SPENDP2SHWITNESS),  # too many HARDENED
            ([49 | HARDENED, 0 | HARDENED], InputScriptType.SPENDP2SHWITNESS),  # invalid length
            ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS),  # invalid length
            ([49 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS),  # invalid slip44
            ([49 | HARDENED, 0 | HARDENED, 1000 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),  # account too high
            ([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 2, 0], InputScriptType.SPENDP2SHWITNESS),  # invalid y
            ([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDP2SHWITNESS),  # address index too high
            ([84 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDWITNESS),  # address index too high
            ([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS),  # invalid input type
            ([84 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),  # invalid input type
            ([49 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDMULTISIG),  # invalid input type
        ]
        correct_derivation_paths = [
            ([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),  # btc is segwit coin, but non-segwit paths are allowed as well
            ([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 1], InputScriptType.SPENDADDRESS),
            ([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDADDRESS),
            ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),
            ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDP2SHWITNESS),
            ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 1123], InputScriptType.SPENDP2SHWITNESS),
            ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 44444], InputScriptType.SPENDP2SHWITNESS),
            ([49 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),
            ([84 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS),
            ([84 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS),
            ([84 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDWITNESS),
            ([48 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDMULTISIG),
        ]
        coin = coins.by_name('Bitcoin')
        for path, input_type in incorrect_derivation_paths:
            self.assertFalse(validate_full_path(path, coin, input_type))

        for path, input_type in correct_derivation_paths:
            self.assertTrue(validate_full_path(path, coin, input_type))

        self.assertTrue(validate_full_path([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], coin, InputScriptType.SPENDADDRESS))
        self.assertFalse(validate_full_path([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], coin, InputScriptType.SPENDWITNESS))
        self.assertTrue(validate_full_path([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], coin, InputScriptType.SPENDWITNESS, validate_script_type=False))
Exemple #4
0
    async def process_input(self, txi: TxInputType) -> None:
        self.wallet_path.add_input(txi)
        self.multisig_fingerprint.add_input(txi)
        writers.write_tx_input_check(self.h_confirmed, txi)
        self.hash143_add_input(txi)  # all inputs are included (non-segwit as well)

        if not addresses.validate_full_path(txi.address_n, self.coin, txi.script_type):
            await helpers.confirm_foreign_address(txi.address_n)

        if input_is_segwit(txi):
            await self.process_segwit_input(txi)
        elif input_is_nonsegwit(txi):
            await self.process_nonsegwit_input(txi)
        else:
            raise SigningError(FailureType.DataError, "Wrong input script type")
Exemple #5
0
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