You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/bitcoin/sign_tx/layout.py

179 lines
6.0 KiB

from micropython import const
from ubinascii import hexlify
6 years ago
from trezor import ui
from trezor.messages import AmountUnit, ButtonRequestType, OutputScriptType
from trezor.strings import format_amount
from trezor.ui.components.tt.text import Text
from trezor.utils import chunks
6 years ago
from apps.common.confirm import require_confirm, require_hold_to_confirm
from .. import addresses
from . import omni
4 years ago
if False:
from typing import Iterator
4 years ago
from trezor import wire
from trezor.messages.SignTx import EnumTypeAmountUnit
from trezor.messages.TxOutput import TxOutput
from apps.common.coininfo import CoinInfo
4 years ago
_LOCKTIME_TIMESTAMP_MIN_VALUE = const(500_000_000)
def format_coin_amount(
amount: int, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
) -> str:
decimals, shortcut = coin.decimals, coin.coin_shortcut
if amount_unit == AmountUnit.SATOSHI:
decimals = 0
shortcut = "sat " + shortcut
elif amount_unit == AmountUnit.MICROBITCOIN and decimals >= 6:
decimals -= 6
shortcut = "u" + shortcut
elif amount_unit == AmountUnit.MILLIBITCOIN and decimals >= 3:
decimals -= 3
shortcut = "m" + shortcut
# we don't need to do anything for AmountUnit.BITCOIN
return "%s %s" % (format_amount(amount, decimals), shortcut)
4 years ago
def split_address(address: str) -> Iterator[str]:
return chunks(address, 17)
4 years ago
def split_op_return(data: str) -> Iterator[str]:
return chunks(data, 18)
async def confirm_output(
ctx: wire.Context, output: TxOutput, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
) -> None:
if output.script_type == OutputScriptType.PAYTOOPRETURN:
data = output.op_return_data
assert data is not None
if omni.is_valid(data):
# OMNI transaction
text = Text("OMNI transaction", ui.ICON_SEND, ui.GREEN)
text.normal(omni.parse(data))
else:
# generic OP_RETURN
4 years ago
hex_data = hexlify(data).decode()
if len(hex_data) >= 18 * 5:
hex_data = hex_data[: (18 * 5 - 3)] + "..."
text = Text("OP_RETURN", ui.ICON_SEND, ui.GREEN)
4 years ago
text.mono(*split_op_return(hex_data))
else:
address = output.address
assert address is not None
address_short = addresses.address_short(coin, address)
text = Text("Confirm sending", ui.ICON_SEND, ui.GREEN)
text.normal(format_coin_amount(output.amount, coin, amount_unit) + " to")
text.mono(*split_address(address_short))
await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
async def confirm_replacement(ctx: wire.Context, description: str, txid: bytes) -> None:
text = Text(description, ui.ICON_SEND, ui.GREEN)
text.normal("Confirm transaction ID:")
hex_data = hexlify(txid).decode()
if len(hex_data) >= 18 * 4:
hex_data = hex_data[: (18 * 4 - 3)] + "..."
text.mono(*split_op_return(hex_data))
await require_confirm(ctx, text, ButtonRequestType.SignTx)
async def confirm_modify_fee(
ctx: wire.Context,
user_fee_change: int,
total_fee_new: int,
coin: CoinInfo,
amount_unit: EnumTypeAmountUnit,
) -> None:
text = Text("Fee modification", ui.ICON_SEND, ui.GREEN)
if user_fee_change == 0:
text.normal("Your fee did not change.")
else:
if user_fee_change < 0:
text.normal("Decrease your fee by:")
else:
text.normal("Increase your fee by:")
text.bold(format_coin_amount(abs(user_fee_change), coin, amount_unit))
text.br_half()
text.normal("Transaction fee:")
text.bold(format_coin_amount(total_fee_new, coin, amount_unit))
await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
async def confirm_joint_total(
ctx: wire.Context,
spending: int,
total: int,
coin: CoinInfo,
amount_unit: EnumTypeAmountUnit,
) -> None:
text = Text("Joint transaction", ui.ICON_SEND, ui.GREEN)
text.normal("You are contributing:")
text.bold(format_coin_amount(spending, coin, amount_unit))
text.normal("to the total amount:")
text.bold(format_coin_amount(total, coin, amount_unit))
await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
4 years ago
async def confirm_total(
ctx: wire.Context,
spending: int,
fee: int,
coin: CoinInfo,
amount_unit: EnumTypeAmountUnit,
) -> None:
text = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
6 years ago
text.normal("Total amount:")
text.bold(format_coin_amount(spending, coin, amount_unit))
6 years ago
text.normal("including fee:")
text.bold(format_coin_amount(fee, coin, amount_unit))
await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
async def confirm_feeoverthreshold(
ctx: wire.Context, fee: int, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
) -> None:
text = Text("High fee", ui.ICON_SEND, ui.GREEN)
6 years ago
text.normal("The fee of")
text.bold(format_coin_amount(fee, coin, amount_unit))
6 years ago
text.normal("is unexpectedly high.", "Continue?")
await require_confirm(ctx, text, ButtonRequestType.FeeOverThreshold)
async def confirm_change_count_over_threshold(
ctx: wire.Context, change_count: int
) -> None:
text = Text("Warning", ui.ICON_SEND, ui.GREEN)
text.normal("There are {}".format(change_count))
text.normal("change-outputs.")
text.br_half()
text.normal("Continue?")
await require_confirm(ctx, text, ButtonRequestType.SignTx)
async def confirm_nondefault_locktime(
ctx: wire.Context, lock_time: int, lock_time_disabled: bool
) -> None:
if lock_time_disabled:
text = Text("Warning", ui.ICON_SEND, ui.GREEN)
text.normal("Locktime is set but will", "have no effect.")
text.br_half()
else:
text = Text("Confirm locktime", ui.ICON_SEND, ui.GREEN)
text.normal("Locktime for this", "transaction is set to")
if lock_time < _LOCKTIME_TIMESTAMP_MIN_VALUE:
text.normal("blockheight:")
else:
text.normal("timestamp:")
text.bold(str(lock_time))
text.normal("Continue?")
await require_confirm(ctx, text, ButtonRequestType.SignTx)