1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-01 03:18:12 +00:00

refactor(core/bitcoin): make use of Context omission to simplify bitcoin app

This commit is contained in:
matejcik 2023-06-26 14:47:26 +02:00
parent 8538f3a65f
commit 5a4aaa79fc
6 changed files with 68 additions and 347 deletions

View File

@ -43,7 +43,7 @@ if TYPE_CHECKING:
) -> None:
...
async def signer(self) -> None:
async def signer(self) -> TxRequest:
...
@ -54,12 +54,8 @@ async def sign_tx(
coin: CoinInfo,
authorization: CoinJoinAuthorization | None = None,
) -> TxRequest:
from trezor.enums import RequestType
from trezor.messages import TxRequest
from trezor.wire.context import call
from ..common import BITCOIN_NAMES
from . import approvers, bitcoin, helpers, progress
from . import approvers, bitcoin
approver: approvers.Approver | None = None
if authorization:
@ -86,19 +82,4 @@ async def sign_tx(
signer_class = bitcoinlike.Bitcoinlike
signer = signer_class(msg, keychain, coin, approver).signer()
res: TxAckType | bool | None = None
while True:
req = signer.send(res)
if isinstance(req, tuple):
request_class, req = req
assert TxRequest.is_type_of(req)
if req.request_type == RequestType.TXFINISHED:
return req
res = await call(req, request_class)
elif isinstance(req, helpers.UiConfirm):
res = await req.confirm_dialog()
progress.progress.report_init()
else:
raise TypeError("Invalid signing instruction")
return await signer_class(msg, keychain, coin, approver).signer()

View File

@ -11,7 +11,7 @@ from apps.common import safety_checks
from .. import writers
from ..common import input_is_external_unverified
from ..keychain import SLIP44_TESTNET, validate_path_against_script_type
from . import helpers, tx_weight
from . import layout, tx_weight
from .sig_hasher import BitcoinSigHasher
from .tx_info import OriginalTxInfo
@ -147,8 +147,10 @@ class BasicApprover(Approver):
self.chunkify = bool(tx.chunkify)
async def add_internal_input(self, txi: TxInput, node: bip32.HDNode) -> None:
from apps.common.paths import show_path_warning
if not validate_path_against_script_type(self.coin, txi):
await helpers.confirm_foreign_address(txi.address_n)
await show_path_warning(txi.address_n)
self.foreign_address_confirmed = True
await super().add_internal_input(txi, node)
@ -165,6 +167,8 @@ class BasicApprover(Approver):
raise ProcessError("Transaction has changed during signing")
async def _add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
from apps.common.paths import show_path_warning
from ..common import CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES
if txo.address_n and not validate_path_against_script_type(
@ -173,7 +177,7 @@ class BasicApprover(Approver):
script_type=CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES[txo.script_type],
multisig=bool(txo.multisig),
):
await helpers.confirm_foreign_address(txo.address_n)
await show_path_warning(txo.address_n)
await super()._add_output(txo, script_pubkey)
@ -202,7 +206,7 @@ class BasicApprover(Approver):
raise ProcessError(
"Reducing original output amounts is not supported."
)
await helpers.confirm_modify_output(
await layout.confirm_modify_output(
txo, orig_txo, self.coin, self.amount_unit
)
elif txo.amount > orig_txo.amount:
@ -224,7 +228,7 @@ class BasicApprover(Approver):
elif txo.payment_req_index is None or self.show_payment_req_details:
# Ask user to confirm output, unless it is part of a payment
# request, which gets confirmed separately.
await helpers.confirm_output(
await layout.confirm_output(
txo,
self.coin,
self.amount_unit,
@ -240,7 +244,7 @@ class BasicApprover(Approver):
if msg.amount is None:
raise DataError("Missing payment request amount.")
result = await helpers.confirm_payment_request(msg, self.coin, self.amount_unit)
result = await layout.confirm_payment_request(msg, self.coin, self.amount_unit)
# When user wants to see more info, the result will be False.
self.show_payment_req_details = result is False
@ -252,7 +256,7 @@ class BasicApprover(Approver):
title = self._replacement_title(tx_info, orig_txs)
for orig in orig_txs:
await helpers.confirm_replacement(title, orig.orig_hash)
await layout.confirm_replacement(title, orig.orig_hash)
def _replacement_title(
self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]
@ -277,10 +281,10 @@ class BasicApprover(Approver):
await super().approve_tx(tx_info, orig_txs)
if self.has_unverified_external_input:
await helpers.confirm_unverified_external_input()
await layout.confirm_unverified_external_input()
if tx_info.wallet_path.get_path() is None:
await helpers.confirm_multiple_accounts()
await layout.confirm_multiple_accounts()
fee = self.total_in - self.total_out
@ -299,10 +303,10 @@ class BasicApprover(Approver):
if fee > fee_threshold:
if fee > 10 * fee_threshold and safety_checks.is_strict():
raise DataError("The fee is unexpectedly large")
await helpers.confirm_feeoverthreshold(fee, coin, amount_unit)
await layout.confirm_feeoverthreshold(fee, coin, amount_unit)
if self.change_count > self.MAX_SILENT_CHANGE_COUNT:
await helpers.confirm_change_count_over_threshold(self.change_count)
await layout.confirm_change_count_over_threshold(self.change_count)
if orig_txs:
# Replacement transaction.
@ -338,7 +342,7 @@ class BasicApprover(Approver):
# Not a PayJoin: Show the actual fee difference, since any difference in the fee is
# coming entirely from the user's own funds and from decreases of external outputs.
# We consider the decreases as belonging to the user.
await helpers.confirm_modify_fee(
await layout.confirm_modify_fee(
title, fee - orig_fee, fee, fee_rate, coin, amount_unit
)
elif spending > orig_spending:
@ -346,7 +350,7 @@ class BasicApprover(Approver):
# PayJoin and user is spending more: Show the increase in the user's contribution
# to the fee, ignoring any contribution from external inputs. Decreasing of
# external outputs is not allowed in PayJoin, so there is no need to handle those.
await helpers.confirm_modify_fee(
await layout.confirm_modify_fee(
title, spending - orig_spending, fee, fee_rate, coin, amount_unit
)
else:
@ -357,12 +361,12 @@ class BasicApprover(Approver):
else:
# Standard transaction.
if tx_info.tx.lock_time > 0:
await helpers.confirm_nondefault_locktime(
await layout.confirm_nondefault_locktime(
tx_info.tx.lock_time, tx_info.lock_time_disabled()
)
if not self.external_in:
await helpers.confirm_total(
await layout.confirm_total(
total,
fee,
fee_rate,
@ -371,7 +375,7 @@ class BasicApprover(Approver):
tx_info.wallet_path.get_path(),
)
else:
await helpers.confirm_joint_total(spending, total, coin, amount_unit)
await layout.confirm_joint_total(spending, total, coin, amount_unit)
class CoinJoinApprover(Approver):

View File

@ -23,7 +23,15 @@ if TYPE_CHECKING:
from typing import Sequence
from trezor.crypto import bip32
from trezor.messages import PrevInput, PrevOutput, PrevTx, SignTx, TxInput, TxOutput
from trezor.messages import (
PrevInput,
PrevOutput,
PrevTx,
SignTx,
TxInput,
TxOutput,
TxRequest,
)
from apps.common.coininfo import CoinInfo
from apps.common.keychain import Keychain
@ -40,7 +48,7 @@ _SERIALIZED_TX_BUFFER = empty_bytearray(_MAX_SERIALIZED_CHUNK_SIZE)
class Bitcoin:
async def signer(self) -> None:
async def signer(self) -> TxRequest:
progress.init(
self.tx_info.tx, is_coinjoin=isinstance(self.approver, CoinJoinApprover)
)
@ -87,9 +95,12 @@ class Bitcoin:
# Sign segwit inputs and serialize witness data.
await self.step6_sign_segwit_inputs()
# Write footer and send remaining data.
# Write footer and remaining data.
await self.step7_finish()
# Return the finishing message.
return helpers.finished_request(self.tx_req)
def __init__(
self,
tx: SignTx,
@ -333,7 +344,6 @@ class Bitcoin:
self.write_tx_footer(self.serialized_tx, self.tx_info.tx)
if __debug__:
progress.assert_finished()
await helpers.request_tx_finish(self.tx_req)
async def process_internal_input(self, txi: TxInput, node: bip32.HDNode) -> None:
if txi.script_type not in common.INTERNAL_INPUT_SCRIPT_TYPES:

View File

@ -90,10 +90,12 @@ class DecredApprover(BasicApprover):
async def add_decred_sstx_submission(
self, txo: TxOutput, script_pubkey: bytes
) -> None:
from .layout import confirm_decred_sstx_submission
# NOTE: The following calls Approver.add_external_output(), not BasicApprover.add_external_output().
# This is needed to skip calling helpers.confirm_output(), which is what BasicApprover would do.
await super(BasicApprover, self).add_external_output(txo, script_pubkey, None)
await helpers.confirm_decred_sstx_submission(txo, self.coin, self.amount_unit)
await confirm_decred_sstx_submission(txo, self.coin, self.amount_unit)
class DecredSigHasher:
@ -293,8 +295,6 @@ class Decred(Bitcoin):
if __debug__:
progress.assert_finished()
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 ProcessError("Cannot use utxo that has script_version != 0")

View File

@ -1,17 +1,13 @@
from typing import TYPE_CHECKING
from trezor import utils
from trezor.enums import RequestType
from trezor.wire import DataError
from trezor.wire.context import call
from .. import common
from ..writers import TX_HASH_SIZE
from . import layout
if TYPE_CHECKING:
from typing import Any, Awaitable
from trezor.enums import AmountUnit
from trezor.messages import (
PrevInput,
PrevOutput,
@ -24,297 +20,24 @@ if TYPE_CHECKING:
)
from apps.common.coininfo import CoinInfo
from apps.common.paths import Bip32Path
# Machine instructions
# ===
class UiConfirm:
def confirm_dialog(self) -> Awaitable[Any]:
raise NotImplementedError
__eq__ = utils.obj_eq
class UiConfirmOutput(UiConfirm):
def __init__(
self,
output: TxOutput,
coin: CoinInfo,
amount_unit: AmountUnit,
output_index: int,
chunkify: bool,
):
self.output = output
self.coin = coin
self.amount_unit = amount_unit
self.output_index = output_index
self.chunkify = chunkify
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_output(
self.output,
self.coin,
self.amount_unit,
self.output_index,
self.chunkify,
)
class UiConfirmDecredSSTXSubmission(UiConfirm):
def __init__(self, output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit):
self.output = output
self.coin = coin
self.amount_unit = amount_unit
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_decred_sstx_submission(
self.output, self.coin, self.amount_unit
)
class UiConfirmPaymentRequest(UiConfirm):
def __init__(
self,
payment_req: TxAckPaymentRequest,
coin: CoinInfo,
amount_unit: AmountUnit,
):
self.payment_req = payment_req
self.amount_unit = amount_unit
self.coin = coin
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_payment_request(
self.payment_req, self.coin, self.amount_unit
)
__eq__ = utils.obj_eq
class UiConfirmReplacement(UiConfirm):
def __init__(self, title: str, txid: bytes):
self.title = title
self.txid = txid
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_replacement(self.title, self.txid)
class UiConfirmModifyOutput(UiConfirm):
def __init__(
self,
txo: TxOutput,
orig_txo: TxOutput,
coin: CoinInfo,
amount_unit: AmountUnit,
):
self.txo = txo
self.orig_txo = orig_txo
self.coin = coin
self.amount_unit = amount_unit
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_modify_output(
self.txo, self.orig_txo, self.coin, self.amount_unit
)
class UiConfirmModifyFee(UiConfirm):
def __init__(
self,
title: str,
user_fee_change: int,
total_fee_new: int,
fee_rate: float,
coin: CoinInfo,
amount_unit: AmountUnit,
):
self.title = title
self.user_fee_change = user_fee_change
self.total_fee_new = total_fee_new
self.fee_rate = fee_rate
self.coin = coin
self.amount_unit = amount_unit
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_modify_fee(
self.title,
self.user_fee_change,
self.total_fee_new,
self.fee_rate,
self.coin,
self.amount_unit,
)
class UiConfirmTotal(UiConfirm):
def __init__(
self,
spending: int,
fee: int,
fee_rate: float,
coin: CoinInfo,
amount_unit: AmountUnit,
address_n: Bip32Path | None,
):
self.spending = spending
self.fee = fee
self.fee_rate = fee_rate
self.coin = coin
self.amount_unit = amount_unit
self.address_n = address_n
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_total(
self.spending,
self.fee,
self.fee_rate,
self.coin,
self.amount_unit,
self.address_n,
)
class UiConfirmJointTotal(UiConfirm):
def __init__(
self, spending: int, total: int, coin: CoinInfo, amount_unit: AmountUnit
):
self.spending = spending
self.total = total
self.coin = coin
self.amount_unit = amount_unit
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_joint_total(
self.spending, self.total, self.coin, self.amount_unit
)
class UiConfirmFeeOverThreshold(UiConfirm):
def __init__(self, fee: int, coin: CoinInfo, amount_unit: AmountUnit):
self.fee = fee
self.coin = coin
self.amount_unit = amount_unit
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_feeoverthreshold(self.fee, self.coin, self.amount_unit)
class UiConfirmChangeCountOverThreshold(UiConfirm):
def __init__(self, change_count: int):
self.change_count = change_count
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_change_count_over_threshold(self.change_count)
class UiConfirmUnverifiedExternalInput(UiConfirm):
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_unverified_external_input()
class UiConfirmForeignAddress(UiConfirm):
def __init__(self, address_n: list):
self.address_n = address_n
def confirm_dialog(self) -> Awaitable[Any]:
from apps.common import paths
return paths.show_path_warning(self.address_n)
class UiConfirmNonDefaultLocktime(UiConfirm):
def __init__(self, lock_time: int, lock_time_disabled: bool):
self.lock_time = lock_time
self.lock_time_disabled = lock_time_disabled
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_nondefault_locktime(
self.lock_time, self.lock_time_disabled
)
class UiConfirmMultipleAccounts(UiConfirm):
def confirm_dialog(self) -> Awaitable[Any]:
return layout.confirm_multiple_accounts()
def confirm_output(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit, output_index: int, chunkify: bool) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmOutput(output, coin, amount_unit, output_index, chunkify))
def confirm_decred_sstx_submission(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmDecredSSTXSubmission(output, coin, amount_unit))
def confirm_payment_request(payment_req: TxAckPaymentRequest, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmPaymentRequest(payment_req, coin, amount_unit))
def confirm_replacement(description: str, txid: bytes) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmReplacement(description, txid))
def confirm_modify_output(txo: TxOutput, orig_txo: TxOutput, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmModifyOutput(txo, orig_txo, coin, amount_unit))
def confirm_modify_fee(title: str, user_fee_change: int, total_fee_new: int, fee_rate: float, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (
yield UiConfirmModifyFee(
title, user_fee_change, total_fee_new, fee_rate, coin, amount_unit
)
)
def confirm_total(spending: int, fee: int, fee_rate: float, coin: CoinInfo, amount_unit: AmountUnit, address_n: Bip32Path | None) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmTotal(spending, fee, fee_rate, coin, amount_unit, address_n))
def confirm_joint_total(spending: int, total: int, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmJointTotal(spending, total, coin, amount_unit))
def confirm_feeoverthreshold(fee: int, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmFeeOverThreshold(fee, coin, amount_unit))
def confirm_change_count_over_threshold(change_count: int) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmChangeCountOverThreshold(change_count))
def confirm_unverified_external_input() -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmUnverifiedExternalInput())
def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmForeignAddress(address_n))
def confirm_nondefault_locktime(lock_time: int, lock_time_disabled: bool) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmNonDefaultLocktime(lock_time, lock_time_disabled))
def confirm_multiple_accounts() -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmMultipleAccounts())
def request_tx_meta(tx_req: TxRequest, coin: CoinInfo, tx_hash: bytes | None = None) -> Awaitable[PrevTx]: # type: ignore [awaitable-is-generator]
async def request_tx_meta(
tx_req: TxRequest, coin: CoinInfo, tx_hash: bytes | None = None
) -> PrevTx:
from trezor.messages import TxAckPrevMeta
assert tx_req.details is not None
tx_req.request_type = RequestType.TXMETA
tx_req.details.tx_hash = tx_hash
ack = yield TxAckPrevMeta, tx_req
ack = await call(tx_req, TxAckPrevMeta)
_clear_tx_request(tx_req)
return _sanitize_tx_meta(ack.tx, coin)
def request_tx_extra_data(
async def request_tx_extra_data(
tx_req: TxRequest, offset: int, size: int, tx_hash: bytes | None = None
) -> Awaitable[bytearray]: # type: ignore [awaitable-is-generator]
) -> bytes:
from trezor.messages import TxAckPrevExtraData
details = tx_req.details # local_cache_attribute
@ -324,12 +47,14 @@ def request_tx_extra_data(
details.extra_data_offset = offset
details.extra_data_len = size
details.tx_hash = tx_hash
ack = yield TxAckPrevExtraData, tx_req
ack = await call(tx_req, TxAckPrevExtraData)
_clear_tx_request(tx_req)
return ack.tx.extra_data_chunk
def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None) -> Awaitable[TxInput]: # type: ignore [awaitable-is-generator]
async def request_tx_input(
tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None
) -> TxInput:
from trezor.messages import TxAckInput
assert tx_req.details is not None
@ -339,24 +64,28 @@ def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes |
else:
tx_req.request_type = RequestType.TXINPUT
tx_req.details.request_index = i
ack = yield TxAckInput, tx_req
ack = await call(tx_req, TxAckInput)
_clear_tx_request(tx_req)
return _sanitize_tx_input(ack.tx.input, coin)
def request_tx_prev_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None) -> Awaitable[PrevInput]: # type: ignore [awaitable-is-generator]
async def request_tx_prev_input(
tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None
) -> PrevInput:
from trezor.messages import TxAckPrevInput
assert tx_req.details is not None
tx_req.request_type = RequestType.TXINPUT
tx_req.details.request_index = i
tx_req.details.tx_hash = tx_hash
ack = yield TxAckPrevInput, tx_req
ack = await call(tx_req, TxAckPrevInput)
_clear_tx_request(tx_req)
return _sanitize_tx_prev_input(ack.tx.input, coin)
def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None) -> Awaitable[TxOutput]: # type: ignore [awaitable-is-generator]
async def request_tx_output(
tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None
) -> TxOutput:
from trezor.messages import TxAckOutput
assert tx_req.details is not None
@ -366,39 +95,40 @@ def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes
else:
tx_req.request_type = RequestType.TXOUTPUT
tx_req.details.request_index = i
ack = yield TxAckOutput, tx_req
ack = await call(tx_req, TxAckOutput)
_clear_tx_request(tx_req)
return _sanitize_tx_output(ack.tx.output, coin)
def request_tx_prev_output(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None) -> Awaitable[PrevOutput]: # type: ignore [awaitable-is-generator]
async def request_tx_prev_output(
tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes | None = None
) -> PrevOutput:
from trezor.messages import TxAckPrevOutput
assert tx_req.details is not None
tx_req.request_type = RequestType.TXOUTPUT
tx_req.details.request_index = i
tx_req.details.tx_hash = tx_hash
ack = yield TxAckPrevOutput, tx_req
ack = await call(tx_req, TxAckPrevOutput)
_clear_tx_request(tx_req)
# return sanitize_tx_prev_output(ack.tx, coin) # no sanitize is required
return ack.tx.output
def request_payment_req(tx_req: TxRequest, i: int) -> Awaitable[TxAckPaymentRequest]: # type: ignore [awaitable-is-generator]
async def request_payment_req(tx_req: TxRequest, i: int) -> TxAckPaymentRequest:
from trezor.messages import TxAckPaymentRequest
assert tx_req.details is not None
tx_req.request_type = RequestType.TXPAYMENTREQ
tx_req.details.request_index = i
ack = yield TxAckPaymentRequest, tx_req
ack = await call(tx_req, TxAckPaymentRequest)
_clear_tx_request(tx_req)
return _sanitize_payment_req(ack)
def request_tx_finish(tx_req: TxRequest) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
def finished_request(tx_req: TxRequest) -> TxRequest:
tx_req.request_type = RequestType.TXFINISHED
yield None, tx_req
_clear_tx_request(tx_req)
return tx_req
def _clear_tx_request(tx_req: TxRequest) -> None:

View File

@ -140,8 +140,6 @@ class ZcashV4(Bitcoinlike):
async def step7_finish(self) -> None:
from apps.common.writers import write_compact_size
from . import helpers
serialized_tx = self.serialized_tx # local_cache_attribute
if self.serialize:
@ -152,8 +150,6 @@ class ZcashV4(Bitcoinlike):
write_compact_size(serialized_tx, 0) # nShieldedOutput
write_compact_size(serialized_tx, 0) # nJoinSplit
await helpers.request_tx_finish(self.tx_req)
async def sign_nonsegwit_input(self, i_sign: int) -> None:
await self.sign_nonsegwit_bip143_input(i_sign)