mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 23:48:12 +00:00
refactor(core): introduce layouts
Layouts can be used by the application code to interact with user using small number of dialogs or other groups of UI components. Each layout is identified by name and takes some parameters. Most layouts will have an implementation for each hardware model, mechanism is provided to import the correct version so that application code can be oblivious to the model. This commit introduces the layout concept and converts a couple of dialogs to use it.
This commit is contained in:
parent
18cb429610
commit
f38abf9d89
@ -495,12 +495,16 @@ if FROZEN:
|
|||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/common/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/common/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/__init__.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/__init__.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/__init__.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py'))
|
||||||
if TREZOR_MODEL == 'T':
|
if TREZOR_MODEL == 'T':
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt.py'))
|
||||||
elif TREZOR_MODEL == '1':
|
elif TREZOR_MODEL == '1':
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/t1/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/t1/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/t1.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/t1.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/t1.py'))
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown Trezor model')
|
raise ValueError('Unknown Trezor model')
|
||||||
|
|
||||||
|
@ -446,12 +446,16 @@ if FROZEN:
|
|||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/common/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/common/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/__init__.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/__init__.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/__init__.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py'))
|
||||||
if TREZOR_MODEL == 'T':
|
if TREZOR_MODEL == 'T':
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/tt/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/tt.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/tt.py'))
|
||||||
elif TREZOR_MODEL == '1':
|
elif TREZOR_MODEL == '1':
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/t1/*.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/components/t1/*.py'))
|
||||||
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/t1.py'))
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/constants/t1.py'))
|
||||||
|
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/t1.py'))
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown Trezor model')
|
raise ValueError('Unknown Trezor model')
|
||||||
|
|
||||||
|
@ -129,11 +129,12 @@ async def handle_EndSession(ctx: wire.Context, msg: EndSession) -> Success:
|
|||||||
|
|
||||||
async def handle_Ping(ctx: wire.Context, msg: Ping) -> Success:
|
async def handle_Ping(ctx: wire.Context, msg: Ping) -> Success:
|
||||||
if msg.button_protection:
|
if msg.button_protection:
|
||||||
from apps.common.confirm import require_confirm
|
from trezor.ui.layouts import require, confirm_action
|
||||||
from trezor.messages.ButtonRequestType import ProtectCall
|
from trezor.messages.ButtonRequestType import ProtectCall
|
||||||
from trezor.ui.components.tt.text import Text
|
|
||||||
|
|
||||||
await require_confirm(ctx, Text("Confirm"), ProtectCall)
|
await require(
|
||||||
|
confirm_action(ctx, "ping", "Confirm", "ping", br_code=ProtectCall)
|
||||||
|
)
|
||||||
return Success(message=msg.message)
|
return Success(message=msg.message)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from trezor.crypto import bip32
|
from trezor.crypto import bip32
|
||||||
from trezor.messages import InputScriptType
|
from trezor.messages import InputScriptType
|
||||||
from trezor.messages.Address import Address
|
from trezor.messages.Address import Address
|
||||||
|
from trezor.ui.layouts import show_address
|
||||||
|
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr, show_xpub
|
from apps.common.layout import address_n_to_str
|
||||||
from apps.common.paths import validate_path
|
from apps.common.paths import validate_path
|
||||||
|
|
||||||
from . import addresses
|
from . import addresses
|
||||||
@ -18,15 +19,11 @@ if False:
|
|||||||
from apps.common.coininfo import CoinInfo
|
from apps.common.coininfo import CoinInfo
|
||||||
|
|
||||||
|
|
||||||
async def show_xpubs(
|
def _get_xpubs(
|
||||||
ctx: wire.Context,
|
coin: CoinInfo, xpub_magic: int, pubnodes: List[HDNodeType]
|
||||||
coin: CoinInfo,
|
) -> List[str]:
|
||||||
xpub_magic: int,
|
result = []
|
||||||
pubnodes: List[HDNodeType],
|
for pubnode in pubnodes:
|
||||||
multisig_index: int,
|
|
||||||
) -> bool:
|
|
||||||
for i, pubnode in enumerate(pubnodes):
|
|
||||||
cancel = "Next" if i < len(pubnodes) - 1 else "Address"
|
|
||||||
node = bip32.HDNode(
|
node = bip32.HDNode(
|
||||||
depth=pubnode.depth,
|
depth=pubnode.depth,
|
||||||
fingerprint=pubnode.fingerprint,
|
fingerprint=pubnode.fingerprint,
|
||||||
@ -35,12 +32,9 @@ async def show_xpubs(
|
|||||||
public_key=pubnode.public_key,
|
public_key=pubnode.public_key,
|
||||||
curve_name=coin.curve_name,
|
curve_name=coin.curve_name,
|
||||||
)
|
)
|
||||||
xpub = node.serialize_public(xpub_magic)
|
result.append(node.serialize_public(xpub_magic))
|
||||||
desc = "XPUB #%d" % (i + 1)
|
|
||||||
desc += " (yours)" if i == multisig_index else " (cosigner)"
|
return result
|
||||||
if await show_xpub(ctx, xpub, desc=desc, cancel=cancel):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@with_keychain
|
@with_keychain
|
||||||
@ -88,22 +82,20 @@ async def get_address(
|
|||||||
else:
|
else:
|
||||||
pubnodes = [hd.node for hd in msg.multisig.pubkeys]
|
pubnodes = [hd.node for hd in msg.multisig.pubkeys]
|
||||||
multisig_index = multisig_pubkey_index(msg.multisig, node.public_key())
|
multisig_index = multisig_pubkey_index(msg.multisig, node.public_key())
|
||||||
|
|
||||||
desc = "Multisig %d of %d" % (msg.multisig.m, len(pubnodes))
|
desc = "Multisig %d of %d" % (msg.multisig.m, len(pubnodes))
|
||||||
while True:
|
await show_address(
|
||||||
if await show_address(ctx, address_short, desc=desc):
|
ctx,
|
||||||
break
|
address=address_short,
|
||||||
if await show_qr(ctx, address_qr, desc=desc, cancel="XPUBs"):
|
address_qr=address_qr,
|
||||||
break
|
desc=desc,
|
||||||
if await show_xpubs(
|
multisig_index=multisig_index,
|
||||||
ctx, coin, multisig_xpub_magic, pubnodes, multisig_index
|
xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes),
|
||||||
):
|
)
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
desc = address_n_to_str(msg.address_n)
|
desc = address_n_to_str(msg.address_n)
|
||||||
while True:
|
await show_address(
|
||||||
if await show_address(ctx, address_short, desc=desc):
|
ctx, address=address_short, address_qr=address_qr, desc=desc
|
||||||
break
|
)
|
||||||
if await show_qr(ctx, address_qr, desc=desc):
|
|
||||||
break
|
|
||||||
|
|
||||||
return Address(address=address)
|
return Address(address=address)
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
from ubinascii import hexlify
|
from ubinascii import hexlify
|
||||||
|
|
||||||
from trezor import ui
|
|
||||||
from trezor.messages import AmountUnit, ButtonRequestType, OutputScriptType
|
from trezor.messages import AmountUnit, ButtonRequestType, OutputScriptType
|
||||||
from trezor.strings import format_amount
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.components.tt.text import Text
|
from trezor.ui import layouts
|
||||||
from trezor.utils import chunks
|
from trezor.ui.layouts import require
|
||||||
|
|
||||||
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
|
||||||
|
|
||||||
from .. import addresses
|
from .. import addresses
|
||||||
from . import omni
|
from . import omni
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Iterator
|
from typing import Optional
|
||||||
|
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.messages.SignTx import EnumTypeAmountUnit
|
from trezor.messages.SignTx import EnumTypeAmountUnit
|
||||||
from trezor.messages.TxOutput import TxOutput
|
from trezor.messages.TxOutput import TxOutput
|
||||||
|
from trezor.ui.layouts import LayoutType
|
||||||
|
|
||||||
from apps.common.coininfo import CoinInfo
|
from apps.common.coininfo import CoinInfo
|
||||||
|
|
||||||
@ -40,14 +39,6 @@ def format_coin_amount(
|
|||||||
return "%s %s" % (format_amount(amount, decimals), shortcut)
|
return "%s %s" % (format_amount(amount, decimals), shortcut)
|
||||||
|
|
||||||
|
|
||||||
def split_address(address: str) -> Iterator[str]:
|
|
||||||
return chunks(address, 17)
|
|
||||||
|
|
||||||
|
|
||||||
def split_op_return(data: str) -> Iterator[str]:
|
|
||||||
return chunks(data, 18)
|
|
||||||
|
|
||||||
|
|
||||||
async def confirm_output(
|
async def confirm_output(
|
||||||
ctx: wire.Context, output: TxOutput, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
|
ctx: wire.Context, output: TxOutput, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -56,33 +47,40 @@ async def confirm_output(
|
|||||||
assert data is not None
|
assert data is not None
|
||||||
if omni.is_valid(data):
|
if omni.is_valid(data):
|
||||||
# OMNI transaction
|
# OMNI transaction
|
||||||
text = Text("OMNI transaction", ui.ICON_SEND, ui.GREEN)
|
layout: LayoutType = layouts.confirm_metadata(
|
||||||
text.normal(omni.parse(data))
|
ctx,
|
||||||
|
"omni_transaction",
|
||||||
|
"OMNI transaction",
|
||||||
|
omni.parse(data),
|
||||||
|
br_code=ButtonRequestType.ConfirmOutput,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# generic OP_RETURN
|
# generic OP_RETURN
|
||||||
hex_data = hexlify(data).decode()
|
layout = layouts.confirm_hex(
|
||||||
if len(hex_data) >= 18 * 5:
|
ctx,
|
||||||
hex_data = hex_data[: (18 * 5 - 3)] + "..."
|
"op_return",
|
||||||
text = Text("OP_RETURN", ui.ICON_SEND, ui.GREEN)
|
"OP_RETURN",
|
||||||
text.mono(*split_op_return(hex_data))
|
hexlify(data).decode(),
|
||||||
|
ButtonRequestType.ConfirmOutput,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
address = output.address
|
assert output.address is not None
|
||||||
assert address is not None
|
address_short = addresses.address_short(coin, output.address)
|
||||||
address_short = addresses.address_short(coin, address)
|
layout = layouts.confirm_output(
|
||||||
text = Text("Confirm sending", ui.ICON_SEND, ui.GREEN)
|
ctx, address_short, format_coin_amount(output.amount, coin, amount_unit)
|
||||||
text.normal(format_coin_amount(output.amount, coin, amount_unit) + " to")
|
)
|
||||||
text.mono(*split_address(address_short))
|
|
||||||
await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
|
await require(layout)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_replacement(ctx: wire.Context, description: str, txid: bytes) -> None:
|
async def confirm_replacement(ctx: wire.Context, description: str, txid: bytes) -> None:
|
||||||
text = Text(description, ui.ICON_SEND, ui.GREEN)
|
await require(
|
||||||
text.normal("Confirm transaction ID:")
|
layouts.confirm_replacement(
|
||||||
hex_data = hexlify(txid).decode()
|
ctx,
|
||||||
if len(hex_data) >= 18 * 4:
|
description,
|
||||||
hex_data = hex_data[: (18 * 4 - 3)] + "..."
|
hexlify(txid).decode(),
|
||||||
text.mono(*split_op_return(hex_data))
|
)
|
||||||
await require_confirm(ctx, text, ButtonRequestType.SignTx)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_modify_fee(
|
async def confirm_modify_fee(
|
||||||
@ -92,19 +90,14 @@ async def confirm_modify_fee(
|
|||||||
coin: CoinInfo,
|
coin: CoinInfo,
|
||||||
amount_unit: EnumTypeAmountUnit,
|
amount_unit: EnumTypeAmountUnit,
|
||||||
) -> None:
|
) -> None:
|
||||||
text = Text("Fee modification", ui.ICON_SEND, ui.GREEN)
|
await require(
|
||||||
if user_fee_change == 0:
|
layouts.confirm_modify_fee(
|
||||||
text.normal("Your fee did not change.")
|
ctx,
|
||||||
else:
|
user_fee_change,
|
||||||
if user_fee_change < 0:
|
format_coin_amount(abs(user_fee_change), coin, amount_unit),
|
||||||
text.normal("Decrease your fee by:")
|
format_coin_amount(total_fee_new, coin, amount_unit),
|
||||||
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(
|
async def confirm_joint_total(
|
||||||
@ -114,12 +107,13 @@ async def confirm_joint_total(
|
|||||||
coin: CoinInfo,
|
coin: CoinInfo,
|
||||||
amount_unit: EnumTypeAmountUnit,
|
amount_unit: EnumTypeAmountUnit,
|
||||||
) -> None:
|
) -> None:
|
||||||
text = Text("Joint transaction", ui.ICON_SEND, ui.GREEN)
|
await require(
|
||||||
text.normal("You are contributing:")
|
layouts.confirm_joint_total(
|
||||||
text.bold(format_coin_amount(spending, coin, amount_unit))
|
ctx,
|
||||||
text.normal("to the total amount:")
|
spending_amount=format_coin_amount(spending, coin, amount_unit),
|
||||||
text.bold(format_coin_amount(total, coin, amount_unit))
|
total_amount=format_coin_amount(total, coin, amount_unit),
|
||||||
await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_total(
|
async def confirm_total(
|
||||||
@ -129,50 +123,69 @@ async def confirm_total(
|
|||||||
coin: CoinInfo,
|
coin: CoinInfo,
|
||||||
amount_unit: EnumTypeAmountUnit,
|
amount_unit: EnumTypeAmountUnit,
|
||||||
) -> None:
|
) -> None:
|
||||||
text = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
|
await require(
|
||||||
text.normal("Total amount:")
|
layouts.confirm_total(
|
||||||
text.bold(format_coin_amount(spending, coin, amount_unit))
|
ctx,
|
||||||
text.normal("including fee:")
|
total_amount=format_coin_amount(spending, coin, amount_unit),
|
||||||
text.bold(format_coin_amount(fee, coin, amount_unit))
|
fee_amount=format_coin_amount(fee, coin, amount_unit),
|
||||||
await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_feeoverthreshold(
|
async def confirm_feeoverthreshold(
|
||||||
ctx: wire.Context, fee: int, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
|
ctx: wire.Context, fee: int, coin: CoinInfo, amount_unit: EnumTypeAmountUnit
|
||||||
) -> None:
|
) -> None:
|
||||||
text = Text("High fee", ui.ICON_SEND, ui.GREEN)
|
fee_amount = format_coin_amount(fee, coin, amount_unit)
|
||||||
text.normal("The fee of")
|
await require(
|
||||||
text.bold(format_coin_amount(fee, coin, amount_unit))
|
layouts.confirm_metadata(
|
||||||
text.normal("is unexpectedly high.", "Continue?")
|
ctx,
|
||||||
await require_confirm(ctx, text, ButtonRequestType.FeeOverThreshold)
|
"fee_over_threshold",
|
||||||
|
"High fee",
|
||||||
|
"The fee of\n{}is unexpectedly high.",
|
||||||
|
fee_amount,
|
||||||
|
ButtonRequestType.FeeOverThreshold,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_change_count_over_threshold(
|
async def confirm_change_count_over_threshold(
|
||||||
ctx: wire.Context, change_count: int
|
ctx: wire.Context, change_count: int
|
||||||
) -> None:
|
) -> None:
|
||||||
text = Text("Warning", ui.ICON_SEND, ui.GREEN)
|
await require(
|
||||||
text.normal("There are {}".format(change_count))
|
layouts.confirm_metadata(
|
||||||
text.normal("change-outputs.")
|
ctx,
|
||||||
text.br_half()
|
"change_count_over_threshold",
|
||||||
text.normal("Continue?")
|
"Warning",
|
||||||
await require_confirm(ctx, text, ButtonRequestType.SignTx)
|
"There are {}\nchange-outputs.\n",
|
||||||
|
str(change_count),
|
||||||
|
ButtonRequestType.SignTx,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_nondefault_locktime(
|
async def confirm_nondefault_locktime(
|
||||||
ctx: wire.Context, lock_time: int, lock_time_disabled: bool
|
ctx: wire.Context, lock_time: int, lock_time_disabled: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
if lock_time_disabled:
|
if lock_time_disabled:
|
||||||
text = Text("Warning", ui.ICON_SEND, ui.GREEN)
|
title = "Warning"
|
||||||
text.normal("Locktime is set but will", "have no effect.")
|
text = "Locktime is set but will\nhave no effect.\n"
|
||||||
text.br_half()
|
param: Optional[str] = None
|
||||||
|
elif lock_time < _LOCKTIME_TIMESTAMP_MIN_VALUE:
|
||||||
|
title = "Confirm locktime"
|
||||||
|
text = "Locktime for this\ntransaction is set to\nblockheight:\n{}"
|
||||||
|
param = str(lock_time)
|
||||||
else:
|
else:
|
||||||
text = Text("Confirm locktime", ui.ICON_SEND, ui.GREEN)
|
title = "Confirm locktime"
|
||||||
text.normal("Locktime for this", "transaction is set to")
|
text = "Locktime for this\ntransaction is set to\ntimestamp:\n{}"
|
||||||
if lock_time < _LOCKTIME_TIMESTAMP_MIN_VALUE:
|
param = str(lock_time)
|
||||||
text.normal("blockheight:")
|
|
||||||
else:
|
|
||||||
text.normal("timestamp:")
|
|
||||||
text.bold(str(lock_time))
|
|
||||||
|
|
||||||
text.normal("Continue?")
|
await require(
|
||||||
await require_confirm(ctx, text, ButtonRequestType.SignTx)
|
layouts.confirm_metadata(
|
||||||
|
ctx,
|
||||||
|
"nondefault_locktime",
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
param,
|
||||||
|
br_code=ButtonRequestType.SignTx,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -69,27 +69,11 @@ async def show_pubkey(ctx: wire.Context, pubkey: bytes) -> None:
|
|||||||
await require_confirm(ctx, text, ButtonRequestType.PublicKey)
|
await require_confirm(ctx, text, ButtonRequestType.PublicKey)
|
||||||
|
|
||||||
|
|
||||||
async def show_xpub(ctx: wire.Context, xpub: str, desc: str, cancel: str) -> bool:
|
|
||||||
pages: List[ui.Component] = []
|
|
||||||
for lines in chunks(list(chunks(xpub, 16)), 5):
|
|
||||||
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN)
|
|
||||||
text.mono(*lines)
|
|
||||||
pages.append(text)
|
|
||||||
|
|
||||||
return await confirm(
|
|
||||||
ctx,
|
|
||||||
Paginated(pages),
|
|
||||||
code=ButtonRequestType.PublicKey,
|
|
||||||
cancel=cancel,
|
|
||||||
cancel_style=ButtonDefault,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def split_address(address: str) -> Iterator[str]:
|
def split_address(address: str) -> Iterator[str]:
|
||||||
return chunks(address, 17)
|
return chunks(address, 17)
|
||||||
|
|
||||||
|
|
||||||
def address_n_to_str(address_n: list) -> str:
|
def address_n_to_str(address_n: Iterable[int]) -> str:
|
||||||
def path_item(i: int) -> str:
|
def path_item(i: int) -> str:
|
||||||
if i & HARDENED:
|
if i & HARDENED:
|
||||||
return str(i ^ HARDENED) + "'"
|
return str(i ^ HARDENED) + "'"
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
from micropython import const
|
from trezor.ui.constants import MONO_CHARS_PER_LINE
|
||||||
|
from trezor.ui.layouts import confirm_path_warning, require
|
||||||
from trezor import ui
|
|
||||||
from trezor.messages import ButtonRequestType
|
|
||||||
from trezor.ui.components.tt.text import Text
|
|
||||||
|
|
||||||
from . import HARDENED
|
from . import HARDENED
|
||||||
from .confirm import require_confirm
|
from .layout import address_n_to_str
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -262,12 +259,7 @@ async def validate_path(
|
|||||||
|
|
||||||
|
|
||||||
async def show_path_warning(ctx: wire.Context, path: Bip32Path) -> None:
|
async def show_path_warning(ctx: wire.Context, path: Bip32Path) -> None:
|
||||||
text = Text("Confirm path", ui.ICON_WRONG, ui.RED)
|
await require(confirm_path_warning(ctx, address_n_to_str(path)))
|
||||||
text.normal("Path")
|
|
||||||
text.mono(*break_address_n_to_lines(path))
|
|
||||||
text.normal("is unknown.")
|
|
||||||
text.normal("Are you sure?")
|
|
||||||
await require_confirm(ctx, text, ButtonRequestType.UnknownDerivationPath)
|
|
||||||
|
|
||||||
|
|
||||||
def is_hardened(i: int) -> bool:
|
def is_hardened(i: int) -> bool:
|
||||||
@ -278,21 +270,11 @@ def path_is_hardened(address_n: Bip32Path) -> bool:
|
|||||||
return all(is_hardened(n) for n in address_n)
|
return all(is_hardened(n) for n in address_n)
|
||||||
|
|
||||||
|
|
||||||
def address_n_to_str(address_n: Bip32Path) -> str:
|
|
||||||
def path_item(i: int) -> str:
|
|
||||||
if i & HARDENED:
|
|
||||||
return str(i ^ HARDENED) + "'"
|
|
||||||
else:
|
|
||||||
return str(i)
|
|
||||||
|
|
||||||
return "m/" + "/".join([path_item(i) for i in address_n])
|
|
||||||
|
|
||||||
|
|
||||||
def break_address_n_to_lines(address_n: Bip32Path) -> List[str]:
|
def break_address_n_to_lines(address_n: Bip32Path) -> List[str]:
|
||||||
lines = []
|
lines = []
|
||||||
path_str = address_n_to_str(address_n)
|
path_str = address_n_to_str(address_n)
|
||||||
|
|
||||||
per_line = const(17)
|
per_line = MONO_CHARS_PER_LINE
|
||||||
while len(path_str) > per_line:
|
while len(path_str) > per_line:
|
||||||
i = path_str[:per_line].rfind("/")
|
i = path_str[:per_line].rfind("/")
|
||||||
lines.append(path_str[:i])
|
lines.append(path_str[:i])
|
||||||
|
@ -5,9 +5,8 @@ from trezor.crypto import bip39, slip39
|
|||||||
from trezor.messages import BackupType
|
from trezor.messages import BackupType
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
from trezor.pin import pin_to_int
|
from trezor.pin import pin_to_int
|
||||||
from trezor.ui.components.tt.text import Text
|
from trezor.ui.layouts import confirm_action, require
|
||||||
|
|
||||||
from apps.common.confirm import require_confirm
|
|
||||||
from apps.management import backup_types
|
from apps.management import backup_types
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +69,12 @@ def _validate(msg) -> int:
|
|||||||
|
|
||||||
|
|
||||||
async def _warn(ctx: wire.Context):
|
async def _warn(ctx: wire.Context):
|
||||||
text = Text("Loading seed")
|
await require(
|
||||||
text.bold("Loading private seed", "is not recommended.")
|
confirm_action(
|
||||||
text.normal("Continue only if you", "know what you are doing!")
|
ctx,
|
||||||
await require_confirm(ctx, text)
|
"warn_loading_seed",
|
||||||
|
"Loading seed",
|
||||||
|
"Loading private seed\nis not recommended.",
|
||||||
|
"Continue only if you\nknow what you are doing!",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -7,6 +7,8 @@ from trezor.messages.EntropyAck import EntropyAck
|
|||||||
from trezor.messages.EntropyRequest import EntropyRequest
|
from trezor.messages.EntropyRequest import EntropyRequest
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
from trezor.pin import pin_to_int
|
from trezor.pin import pin_to_int
|
||||||
|
from trezor.ui.layouts import confirm_backup, confirm_reset_device, require
|
||||||
|
from trezor.ui.loader import LoadingAnimation
|
||||||
|
|
||||||
from .. import backup_types
|
from .. import backup_types
|
||||||
from ..change_pin import request_pin_confirm
|
from ..change_pin import request_pin_confirm
|
||||||
@ -26,7 +28,14 @@ async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success:
|
|||||||
_validate_reset_device(msg)
|
_validate_reset_device(msg)
|
||||||
|
|
||||||
# make sure user knows they're setting up a new wallet
|
# make sure user knows they're setting up a new wallet
|
||||||
await layout.show_reset_device_warning(ctx, msg.backup_type)
|
if msg.backup_type == BackupType.Slip39_Basic:
|
||||||
|
prompt = "Create a new wallet\nwith Shamir Backup?"
|
||||||
|
elif msg.backup_type == BackupType.Slip39_Advanced:
|
||||||
|
prompt = "Create a new wallet\nwith Super Shamir?"
|
||||||
|
else:
|
||||||
|
prompt = "Do you want to create\na new wallet?"
|
||||||
|
await require(confirm_reset_device(ctx, prompt))
|
||||||
|
await LoadingAnimation()
|
||||||
|
|
||||||
# wipe storage to make sure the device is in a clear state
|
# wipe storage to make sure the device is in a clear state
|
||||||
storage.reset()
|
storage.reset()
|
||||||
@ -68,7 +77,7 @@ async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success:
|
|||||||
|
|
||||||
# If doing backup, ask the user to confirm.
|
# If doing backup, ask the user to confirm.
|
||||||
if perform_backup:
|
if perform_backup:
|
||||||
perform_backup = await layout.confirm_backup(ctx)
|
perform_backup = await confirm_backup(ctx)
|
||||||
|
|
||||||
# generate and display backup information for the master secret
|
# generate and display backup information for the master secret
|
||||||
if perform_backup:
|
if perform_backup:
|
||||||
|
@ -9,7 +9,6 @@ from trezor.ui.components.tt.info import InfoConfirm
|
|||||||
from trezor.ui.components.tt.num_input import NumInput
|
from trezor.ui.components.tt.num_input import NumInput
|
||||||
from trezor.ui.components.tt.scroll import Paginated
|
from trezor.ui.components.tt.scroll import Paginated
|
||||||
from trezor.ui.components.tt.text import Text
|
from trezor.ui.components.tt.text import Text
|
||||||
from trezor.ui.loader import LoadingAnimation
|
|
||||||
|
|
||||||
from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm
|
from apps.common.confirm import confirm, require_confirm, require_hold_to_confirm
|
||||||
from apps.common.layout import show_success
|
from apps.common.layout import show_success
|
||||||
@ -30,47 +29,6 @@ async def show_internal_entropy(ctx, entropy: bytes):
|
|||||||
await require_confirm(ctx, text, ButtonRequestType.ResetDevice)
|
await require_confirm(ctx, text, ButtonRequestType.ResetDevice)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_backup(ctx):
|
|
||||||
# First prompt
|
|
||||||
text = Text("Success", ui.ICON_CONFIRM, ui.GREEN, new_lines=False)
|
|
||||||
text.bold("New wallet created")
|
|
||||||
text.br()
|
|
||||||
text.bold("successfully!")
|
|
||||||
text.br()
|
|
||||||
text.br_half()
|
|
||||||
text.normal("You should back up your")
|
|
||||||
text.br()
|
|
||||||
text.normal("new wallet right now.")
|
|
||||||
if await confirm(
|
|
||||||
ctx,
|
|
||||||
text,
|
|
||||||
ButtonRequestType.ResetDevice,
|
|
||||||
cancel="Skip",
|
|
||||||
confirm="Back up",
|
|
||||||
major_confirm=True,
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# If the user selects Skip, ask again
|
|
||||||
text = Text("Warning", ui.ICON_WRONG, ui.RED, new_lines=False)
|
|
||||||
text.bold("Are you sure you want")
|
|
||||||
text.br()
|
|
||||||
text.bold("to skip the backup?")
|
|
||||||
text.br()
|
|
||||||
text.br_half()
|
|
||||||
text.normal("You can back up your")
|
|
||||||
text.br()
|
|
||||||
text.normal("Trezor once, at any time.")
|
|
||||||
return await confirm(
|
|
||||||
ctx,
|
|
||||||
text,
|
|
||||||
ButtonRequestType.ResetDevice,
|
|
||||||
cancel="Skip",
|
|
||||||
confirm="Back up",
|
|
||||||
major_confirm=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def _show_share_words(ctx, share_words, share_index=None, group_index=None):
|
async def _show_share_words(ctx, share_words, share_index=None, group_index=None):
|
||||||
first, chunks, last = _split_share_into_pages(share_words)
|
first, chunks, last = _split_share_into_pages(share_words)
|
||||||
|
|
||||||
@ -643,27 +601,3 @@ class MnemonicWordSelect(ui.Layout):
|
|||||||
|
|
||||||
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
return super().create_tasks() + (debug.input_signal(),)
|
return super().create_tasks() + (debug.input_signal(),)
|
||||||
|
|
||||||
|
|
||||||
async def show_reset_device_warning(ctx, backup_type: BackupType = BackupType.Bip39):
|
|
||||||
text = Text("Create new wallet", ui.ICON_RESET, new_lines=False)
|
|
||||||
if backup_type == BackupType.Slip39_Basic:
|
|
||||||
text.bold("Create a new wallet")
|
|
||||||
text.br()
|
|
||||||
text.bold("with Shamir Backup?")
|
|
||||||
elif backup_type == BackupType.Slip39_Advanced:
|
|
||||||
text.bold("Create a new wallet")
|
|
||||||
text.br()
|
|
||||||
text.bold("with Super Shamir?")
|
|
||||||
else:
|
|
||||||
text.bold("Do you want to create")
|
|
||||||
text.br()
|
|
||||||
text.bold("a new wallet?")
|
|
||||||
text.br()
|
|
||||||
text.br_half()
|
|
||||||
text.normal("By continuing you agree")
|
|
||||||
text.br()
|
|
||||||
text.normal("to")
|
|
||||||
text.bold("https://trezor.io/tos")
|
|
||||||
await require_confirm(ctx, text, ButtonRequestType.ResetDevice, major_confirm=True)
|
|
||||||
await LoadingAnimation()
|
|
||||||
|
@ -1,27 +1,12 @@
|
|||||||
import storage
|
import storage
|
||||||
from trezor import ui
|
|
||||||
from trezor.messages import ButtonRequestType
|
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
from trezor.ui.components.tt.button import ButtonCancel
|
from trezor.ui.layouts import confirm_wipe, require
|
||||||
from trezor.ui.components.tt.text import Text
|
|
||||||
from trezor.ui.loader import LoaderDanger
|
|
||||||
|
|
||||||
from apps.common.confirm import require_hold_to_confirm
|
|
||||||
|
|
||||||
from .apply_settings import reload_settings_from_storage
|
from .apply_settings import reload_settings_from_storage
|
||||||
|
|
||||||
|
|
||||||
async def wipe_device(ctx, msg):
|
async def wipe_device(ctx, msg):
|
||||||
text = Text("Wipe device", ui.ICON_WIPE, ui.RED)
|
await require(confirm_wipe(ctx))
|
||||||
text.normal("Do you really want to", "wipe the device?", "")
|
|
||||||
text.bold("All data will be lost.")
|
|
||||||
await require_hold_to_confirm(
|
|
||||||
ctx,
|
|
||||||
text,
|
|
||||||
ButtonRequestType.WipeDevice,
|
|
||||||
confirm_style=ButtonCancel,
|
|
||||||
loader_style=LoaderDanger,
|
|
||||||
)
|
|
||||||
|
|
||||||
storage.wipe()
|
storage.wipe()
|
||||||
reload_settings_from_storage()
|
reload_settings_from_storage()
|
||||||
|
@ -5,3 +5,10 @@ TEXT_LINE_HEIGHT = const(26)
|
|||||||
TEXT_LINE_HEIGHT_HALF = const(13)
|
TEXT_LINE_HEIGHT_HALF = const(13)
|
||||||
TEXT_MARGIN_LEFT = const(14)
|
TEXT_MARGIN_LEFT = const(14)
|
||||||
TEXT_MAX_LINES = const(5)
|
TEXT_MAX_LINES = const(5)
|
||||||
|
|
||||||
|
MONO_CHARS_PER_LINE = const(17)
|
||||||
|
MONO_HEX_PER_LINE = const(18)
|
||||||
|
|
||||||
|
QR_X = const(120)
|
||||||
|
QR_Y = const(115)
|
||||||
|
QR_SIZE_THRESHOLD = const(63)
|
||||||
|
12
core/src/trezor/ui/layouts/__init__.py
Normal file
12
core/src/trezor/ui/layouts/__init__.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from trezor import utils
|
||||||
|
|
||||||
|
from .common import * # noqa: F401,F403
|
||||||
|
|
||||||
|
# NOTE: using any import magic probably causes mypy not to check equivalence of
|
||||||
|
# layout type signatures across models
|
||||||
|
if utils.MODEL == "1":
|
||||||
|
from .t1 import * # noqa: F401,F403
|
||||||
|
elif utils.MODEL == "T":
|
||||||
|
from .tt import * # noqa: F401,F403
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown Trezor model")
|
31
core/src/trezor/ui/layouts/common.py
Normal file
31
core/src/trezor/ui/layouts/common.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from trezor import log, wire, workflow
|
||||||
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.messages.ButtonAck import ButtonAck
|
||||||
|
from trezor.messages.ButtonRequest import ButtonRequest
|
||||||
|
|
||||||
|
from ..components.common.confirm import CONFIRMED
|
||||||
|
|
||||||
|
if False:
|
||||||
|
from typing import Any, Awaitable
|
||||||
|
|
||||||
|
from trezor.messages.ButtonRequest import EnumTypeButtonRequestType
|
||||||
|
|
||||||
|
LayoutType = Awaitable[Any]
|
||||||
|
|
||||||
|
|
||||||
|
async def require(a: LayoutType) -> None:
|
||||||
|
result = await a
|
||||||
|
if result is not CONFIRMED:
|
||||||
|
raise wire.ActionCancelled
|
||||||
|
|
||||||
|
|
||||||
|
async def interact(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
layout: LayoutType,
|
||||||
|
brtype: str,
|
||||||
|
brcode: EnumTypeButtonRequestType = ButtonRequestType.Other,
|
||||||
|
) -> Any:
|
||||||
|
log.debug(__name__, "ButtonRequest.type={}".format(brtype))
|
||||||
|
workflow.close_others()
|
||||||
|
await ctx.call(ButtonRequest(code=brcode), ButtonAck)
|
||||||
|
return await ctx.wait(layout)
|
0
core/src/trezor/ui/layouts/t1.py
Normal file
0
core/src/trezor/ui/layouts/t1.py
Normal file
346
core/src/trezor/ui/layouts/tt.py
Normal file
346
core/src/trezor/ui/layouts/tt.py
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
from micropython import const
|
||||||
|
|
||||||
|
from trezor import ui
|
||||||
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.ui.container import Container
|
||||||
|
from trezor.ui.loader import LoaderDanger
|
||||||
|
from trezor.ui.qr import Qr
|
||||||
|
from trezor.utils import chunks
|
||||||
|
|
||||||
|
from ..components.common import break_path_to_lines
|
||||||
|
from ..components.common.confirm import CONFIRMED
|
||||||
|
from ..components.tt.button import ButtonCancel, ButtonDefault
|
||||||
|
from ..components.tt.confirm import Confirm, HoldToConfirm
|
||||||
|
from ..components.tt.scroll import Paginated
|
||||||
|
from ..components.tt.text import Text
|
||||||
|
from ..constants.tt import (
|
||||||
|
MONO_CHARS_PER_LINE,
|
||||||
|
MONO_HEX_PER_LINE,
|
||||||
|
QR_SIZE_THRESHOLD,
|
||||||
|
QR_X,
|
||||||
|
QR_Y,
|
||||||
|
TEXT_MAX_LINES,
|
||||||
|
)
|
||||||
|
from .common import interact
|
||||||
|
|
||||||
|
if False:
|
||||||
|
from typing import Any, Iterator, List, Sequence, Union, Optional
|
||||||
|
|
||||||
|
from trezor import wire
|
||||||
|
from trezor.messages.ButtonRequest import EnumTypeButtonRequestType
|
||||||
|
|
||||||
|
from . import LayoutType
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"confirm_action",
|
||||||
|
"confirm_wipe",
|
||||||
|
"confirm_reset_device",
|
||||||
|
"confirm_backup",
|
||||||
|
"confirm_path_warning",
|
||||||
|
"show_address",
|
||||||
|
"confirm_output",
|
||||||
|
"confirm_hex",
|
||||||
|
"confirm_total",
|
||||||
|
"confirm_joint_total",
|
||||||
|
"confirm_metadata",
|
||||||
|
"confirm_replacement",
|
||||||
|
"confirm_modify_fee",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_action(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
br_type: str,
|
||||||
|
title: str,
|
||||||
|
action: str,
|
||||||
|
description: str = None,
|
||||||
|
verb: Union[str, bytes] = Confirm.DEFAULT_CONFIRM,
|
||||||
|
icon: str = None,
|
||||||
|
br_code: EnumTypeButtonRequestType = ButtonRequestType.Other,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text(title, icon if icon is not None else ui.ICON_DEFAULT, new_lines=False)
|
||||||
|
text.bold(action)
|
||||||
|
text.br()
|
||||||
|
if description:
|
||||||
|
text.normal(description)
|
||||||
|
|
||||||
|
return interact(ctx, Confirm(text, confirm=verb), br_type, br_code)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_wipe(ctx: wire.GenericContext) -> LayoutType:
|
||||||
|
text = Text("Wipe device", ui.ICON_WIPE, ui.RED)
|
||||||
|
text.normal("Do you really want to", "wipe the device?", "")
|
||||||
|
text.bold("All data will be lost.")
|
||||||
|
return interact(
|
||||||
|
ctx,
|
||||||
|
HoldToConfirm(text, confirm_style=ButtonCancel, loader_style=LoaderDanger),
|
||||||
|
"wipe_device",
|
||||||
|
ButtonRequestType.WipeDevice,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_reset_device(ctx: wire.GenericContext, prompt: str) -> LayoutType:
|
||||||
|
text = Text("Create new wallet", ui.ICON_RESET, new_lines=False)
|
||||||
|
text.bold(prompt)
|
||||||
|
text.br()
|
||||||
|
text.br_half()
|
||||||
|
text.normal("By continuing you agree")
|
||||||
|
text.br()
|
||||||
|
text.normal("to")
|
||||||
|
text.bold("https://trezor.io/tos")
|
||||||
|
return interact(
|
||||||
|
ctx,
|
||||||
|
Confirm(text, major_confirm=True),
|
||||||
|
"setup_device",
|
||||||
|
ButtonRequestType.ResetDevice,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def confirm_backup(ctx: wire.GenericContext) -> bool:
|
||||||
|
text1 = Text("Success", ui.ICON_CONFIRM, ui.GREEN)
|
||||||
|
text1.bold("New wallet created", "successfully!")
|
||||||
|
text1.br_half()
|
||||||
|
text1.normal("You should back up your", "new wallet right now.")
|
||||||
|
|
||||||
|
text2 = Text("Warning", ui.ICON_WRONG, ui.RED)
|
||||||
|
text2.bold("Are you sure you want", "to skip the backup?")
|
||||||
|
text2.br_half()
|
||||||
|
text2.normal("You can back up your", "Trezor once, at any time.")
|
||||||
|
|
||||||
|
if (
|
||||||
|
await interact(
|
||||||
|
ctx,
|
||||||
|
Confirm(text1, cancel="Skip", confirm="Back up", major_confirm=True),
|
||||||
|
"backup_device",
|
||||||
|
ButtonRequestType.ResetDevice,
|
||||||
|
)
|
||||||
|
is CONFIRMED
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
confirmed = (
|
||||||
|
await interact(
|
||||||
|
ctx,
|
||||||
|
Confirm(text2, cancel="Skip", confirm="Back up", major_confirm=True),
|
||||||
|
"backup_device",
|
||||||
|
ButtonRequestType.ResetDevice,
|
||||||
|
)
|
||||||
|
) is CONFIRMED
|
||||||
|
return confirmed
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_path_warning(ctx: wire.GenericContext, path: str) -> LayoutType:
|
||||||
|
text = Text("Confirm path", ui.ICON_WRONG, ui.RED)
|
||||||
|
text.normal("Path")
|
||||||
|
text.mono(*break_path_to_lines(path, MONO_CHARS_PER_LINE))
|
||||||
|
text.normal("is unknown.", "Are you sure?")
|
||||||
|
return interact(
|
||||||
|
ctx,
|
||||||
|
Confirm(text),
|
||||||
|
"path_warning",
|
||||||
|
ButtonRequestType.UnknownDerivationPath,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_qr(
|
||||||
|
address: str,
|
||||||
|
desc: str,
|
||||||
|
cancel: str = "Address",
|
||||||
|
) -> Confirm:
|
||||||
|
QR_COEF = const(4) if len(address) < QR_SIZE_THRESHOLD else const(3)
|
||||||
|
qr = Qr(address, QR_X, QR_Y, QR_COEF)
|
||||||
|
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN)
|
||||||
|
|
||||||
|
return Confirm(Container(qr, text), cancel=cancel, cancel_style=ButtonDefault)
|
||||||
|
|
||||||
|
|
||||||
|
def _split_address(address: str) -> Iterator[str]:
|
||||||
|
return chunks(address, MONO_CHARS_PER_LINE)
|
||||||
|
|
||||||
|
|
||||||
|
def _hex_lines(hex_data: str, lines: int = TEXT_MAX_LINES) -> Iterator[str]:
|
||||||
|
if len(hex_data) >= MONO_HEX_PER_LINE * lines:
|
||||||
|
hex_data = hex_data[: (MONO_HEX_PER_LINE * lines - 3)] + "..."
|
||||||
|
return chunks(hex_data, MONO_HEX_PER_LINE)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_address(
|
||||||
|
address: str,
|
||||||
|
desc: str,
|
||||||
|
network: str = None,
|
||||||
|
) -> Confirm:
|
||||||
|
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN)
|
||||||
|
if network is not None:
|
||||||
|
text.normal("%s network" % network)
|
||||||
|
text.mono(*_split_address(address))
|
||||||
|
|
||||||
|
return Confirm(text, cancel="QR", cancel_style=ButtonDefault)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_xpub(xpub: str, desc: str, cancel: str) -> Paginated:
|
||||||
|
pages: List[ui.Component] = []
|
||||||
|
for lines in chunks(list(chunks(xpub, 16)), 5):
|
||||||
|
text = Text(desc, ui.ICON_RECEIVE, ui.GREEN)
|
||||||
|
text.mono(*lines)
|
||||||
|
pages.append(text)
|
||||||
|
|
||||||
|
content = Paginated(pages)
|
||||||
|
|
||||||
|
content.pages[-1] = Confirm(
|
||||||
|
content.pages[-1],
|
||||||
|
cancel=cancel,
|
||||||
|
cancel_style=ButtonDefault,
|
||||||
|
)
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
async def show_address(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
address: str,
|
||||||
|
address_qr: str = None,
|
||||||
|
desc: str = "Confirm address",
|
||||||
|
network: str = None,
|
||||||
|
multisig_index: int = None,
|
||||||
|
xpubs: Sequence[str] = [],
|
||||||
|
) -> None:
|
||||||
|
is_multisig = len(xpubs) > 0
|
||||||
|
while True:
|
||||||
|
if (
|
||||||
|
await interact(
|
||||||
|
ctx,
|
||||||
|
_show_address(address, desc, network),
|
||||||
|
"show_address",
|
||||||
|
ButtonRequestType.Address,
|
||||||
|
)
|
||||||
|
is CONFIRMED
|
||||||
|
):
|
||||||
|
break
|
||||||
|
if (
|
||||||
|
await interact(
|
||||||
|
ctx,
|
||||||
|
_show_qr(
|
||||||
|
address if address_qr is None else address_qr,
|
||||||
|
desc,
|
||||||
|
cancel="XPUBs" if is_multisig else "Address",
|
||||||
|
),
|
||||||
|
"show_qr",
|
||||||
|
ButtonRequestType.Address,
|
||||||
|
)
|
||||||
|
is CONFIRMED
|
||||||
|
):
|
||||||
|
break
|
||||||
|
|
||||||
|
if is_multisig:
|
||||||
|
for i, xpub in enumerate(xpubs):
|
||||||
|
cancel = "Next" if i < len(xpubs) - 1 else "Address"
|
||||||
|
desc = "XPUB #%d" % (i + 1)
|
||||||
|
desc += " (yours)" if i == multisig_index else " (cosigner)"
|
||||||
|
if (
|
||||||
|
await interact(
|
||||||
|
ctx,
|
||||||
|
_show_xpub(xpub, desc=desc, cancel=cancel),
|
||||||
|
"show_xpub",
|
||||||
|
ButtonRequestType.PublicKey,
|
||||||
|
)
|
||||||
|
is CONFIRMED
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_output(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
address: str,
|
||||||
|
amount: str,
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text("Confirm sending", ui.ICON_SEND, ui.GREEN)
|
||||||
|
text.normal(amount + " to")
|
||||||
|
text.mono(*_split_address(address))
|
||||||
|
return interact(
|
||||||
|
ctx, Confirm(text), "confirm_output", ButtonRequestType.ConfirmOutput
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_hex(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
br_type: str,
|
||||||
|
title: str,
|
||||||
|
data: str,
|
||||||
|
br_code: EnumTypeButtonRequestType = ButtonRequestType.Other,
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text(title, ui.ICON_SEND, ui.GREEN)
|
||||||
|
text.mono(*_hex_lines(data))
|
||||||
|
return interact(ctx, Confirm(text), br_type, br_code)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_total(
|
||||||
|
ctx: wire.GenericContext, total_amount: str, fee_amount: str
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN)
|
||||||
|
text.normal("Total amount:")
|
||||||
|
text.bold(total_amount)
|
||||||
|
text.normal("including fee:")
|
||||||
|
text.bold(fee_amount)
|
||||||
|
return interact(ctx, HoldToConfirm(text), "confirm_total", ButtonRequestType.SignTx)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_joint_total(
|
||||||
|
ctx: wire.GenericContext, spending_amount: str, total_amount: str
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text("Joint transaction", ui.ICON_SEND, ui.GREEN)
|
||||||
|
text.normal("You are contributing:")
|
||||||
|
text.bold(spending_amount)
|
||||||
|
text.normal("to the total amount:")
|
||||||
|
text.bold(total_amount)
|
||||||
|
return interact(
|
||||||
|
ctx, HoldToConfirm(text), "confirm_joint_total", ButtonRequestType.SignTx
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_metadata(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
br_type: str,
|
||||||
|
title: str,
|
||||||
|
content: str,
|
||||||
|
param: Optional[str] = None,
|
||||||
|
br_code: EnumTypeButtonRequestType = ButtonRequestType.SignTx,
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text(title, ui.ICON_SEND, ui.GREEN, new_lines=False)
|
||||||
|
text.format_parametrized(content, param if param is not None else "")
|
||||||
|
text.br()
|
||||||
|
|
||||||
|
text.normal("Continue?")
|
||||||
|
|
||||||
|
return interact(ctx, Confirm(text), br_type, br_code)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_replacement(
|
||||||
|
ctx: wire.GenericContext, description: str, txid: str
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text(description, ui.ICON_SEND, ui.GREEN)
|
||||||
|
text.normal("Confirm transaction ID:")
|
||||||
|
text.mono(*_hex_lines(txid, TEXT_MAX_LINES - 1))
|
||||||
|
return interact(ctx, Confirm(text), "confirm_replacement", ButtonRequestType.SignTx)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_modify_fee(
|
||||||
|
ctx: wire.GenericContext,
|
||||||
|
sign: int,
|
||||||
|
user_fee_change: str,
|
||||||
|
total_fee_new: str,
|
||||||
|
) -> LayoutType:
|
||||||
|
text = Text("Fee modification", ui.ICON_SEND, ui.GREEN)
|
||||||
|
if sign == 0:
|
||||||
|
text.normal("Your fee did not change.")
|
||||||
|
else:
|
||||||
|
if sign < 0:
|
||||||
|
text.normal("Decrease your fee by:")
|
||||||
|
else:
|
||||||
|
text.normal("Increase your fee by:")
|
||||||
|
text.bold(user_fee_change)
|
||||||
|
text.br_half()
|
||||||
|
text.normal("Transaction fee:")
|
||||||
|
text.bold(total_fee_new)
|
||||||
|
return interact(ctx, HoldToConfirm(text), "modify_fee", ButtonRequestType.SignTx)
|
@ -184,7 +184,7 @@
|
|||||||
"test_msg_ethereum_signtx_eip155.py::test_chain_ids[609112567-60-sig6]": "c8e01d20eccadcca4f05e4e8351c3bfc38d0fdbe4a61f63dfd74e065faea86e7",
|
"test_msg_ethereum_signtx_eip155.py::test_chain_ids[609112567-60-sig6]": "c8e01d20eccadcca4f05e4e8351c3bfc38d0fdbe4a61f63dfd74e065faea86e7",
|
||||||
"test_msg_ethereum_signtx_eip155.py::test_chain_ids[61-61-sig3]": "cd5f04cc7b055503e83f0538709a7ac577445c6089ead12f1fc3a3c45ad96419",
|
"test_msg_ethereum_signtx_eip155.py::test_chain_ids[61-61-sig3]": "cd5f04cc7b055503e83f0538709a7ac577445c6089ead12f1fc3a3c45ad96419",
|
||||||
"test_msg_ethereum_signtx_eip155.py::test_with_data": "33e97436953f55bc61c5f54bd9702b1ba962c0e716d20ddbe9827be3d24ad98d",
|
"test_msg_ethereum_signtx_eip155.py::test_with_data": "33e97436953f55bc61c5f54bd9702b1ba962c0e716d20ddbe9827be3d24ad98d",
|
||||||
"test_msg_ethereum_verifymessage.py-test_verify": "da70ac961dc8ff548943498f42ad9436d3e2591f42793e37c8ec4cda9ea7835e",
|
"test_msg_ethereum_verifymessage.py-test_verify": "c1f94d9a7810458643cd441bb9a0844e1fbc519683dd6a5725a9285b247c7648",
|
||||||
"test_msg_ethereum_verifymessage.py-test_verify_invalid": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
"test_msg_ethereum_verifymessage.py-test_verify_invalid": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||||
"test_msg_getaddress.py-test_bch": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
"test_msg_getaddress.py-test_bch": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||||
"test_msg_getaddress.py-test_bch_multisig": "f620ed42c682dd55ab7a1b4ac53686c03dd51966ad4bdd018bd24a3305b92148",
|
"test_msg_getaddress.py-test_bch_multisig": "f620ed42c682dd55ab7a1b4ac53686c03dd51966ad4bdd018bd24a3305b92148",
|
||||||
@ -284,7 +284,7 @@
|
|||||||
"test_msg_lisk_signtx.py-test_lisk_sign_tx_send": "07e5a905588b9e2518db7554c76c548c5a1267849b30cf911259477938705ff8",
|
"test_msg_lisk_signtx.py-test_lisk_sign_tx_send": "07e5a905588b9e2518db7554c76c548c5a1267849b30cf911259477938705ff8",
|
||||||
"test_msg_lisk_signtx.py-test_lisk_sign_tx_send_with_data": "ea969f90b6e4b840bb8728a9a99e5d07f59f478dba6b144218148d9db83f7a49",
|
"test_msg_lisk_signtx.py-test_lisk_sign_tx_send_with_data": "ea969f90b6e4b840bb8728a9a99e5d07f59f478dba6b144218148d9db83f7a49",
|
||||||
"test_msg_lisk_verifymessage.py-test_verify": "492d75b6126690f5ecb17132cd1dafcf0da9bddbb8fecd3c32e7654f346039e2",
|
"test_msg_lisk_verifymessage.py-test_verify": "492d75b6126690f5ecb17132cd1dafcf0da9bddbb8fecd3c32e7654f346039e2",
|
||||||
"test_msg_lisk_verifymessage.py-test_verify_long": "8158d14f6ceabb8865f3c5631360673c4c2226e563d7e18342f18f8af2b7c8a1",
|
"test_msg_lisk_verifymessage.py-test_verify_long": "89d00ad07a5951e47484879b9bdb7998f3364b755aafb3e7e783c28e0521a8e7",
|
||||||
"test_msg_loaddevice.py-test_load_device_1": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
|
"test_msg_loaddevice.py-test_load_device_1": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
|
||||||
"test_msg_loaddevice.py-test_load_device_2": "dc13c8486d8a59c5062e19139d8b3cea4ece1a3bc93592be7dc226f83ba54477",
|
"test_msg_loaddevice.py-test_load_device_2": "dc13c8486d8a59c5062e19139d8b3cea4ece1a3bc93592be7dc226f83ba54477",
|
||||||
"test_msg_loaddevice.py-test_load_device_slip39_advanced": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
|
"test_msg_loaddevice.py-test_load_device_slip39_advanced": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605",
|
||||||
@ -309,7 +309,7 @@
|
|||||||
"test_msg_nem_signtx_transfers.py-test_nem_signtx_simple": "d36a67610b16f835b174a053fe60104a03ea5d49fbd612d73f5d8cdb31fce421",
|
"test_msg_nem_signtx_transfers.py-test_nem_signtx_simple": "d36a67610b16f835b174a053fe60104a03ea5d49fbd612d73f5d8cdb31fce421",
|
||||||
"test_msg_nem_signtx_transfers.py-test_nem_signtx_unknown_mosaic": "1fd9bf33c3c481d8b76fbdddfc3e8d91df6a1a97661a8f8c4b57cd3df41e83f0",
|
"test_msg_nem_signtx_transfers.py-test_nem_signtx_unknown_mosaic": "1fd9bf33c3c481d8b76fbdddfc3e8d91df6a1a97661a8f8c4b57cd3df41e83f0",
|
||||||
"test_msg_nem_signtx_transfers.py-test_nem_signtx_xem_as_mosaic": "842307e1734fea44aca9e53e2d76e0c6206348c4461f9eb1a36021bed1f681c8",
|
"test_msg_nem_signtx_transfers.py-test_nem_signtx_xem_as_mosaic": "842307e1734fea44aca9e53e2d76e0c6206348c4461f9eb1a36021bed1f681c8",
|
||||||
"test_msg_ping.py::test_ping": "229a51d6e66ea58927167e84f96d5d4d30dd0ebace0258fcb709f841ce0beb9e",
|
"test_msg_ping.py::test_ping": "949781cf7d772de19cca691adecdca523f5cada8f83b5713af0bc179435995e4",
|
||||||
"test_msg_recoverydevice_bip39_dryrun.py::test_bad_parameters[label-test]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
"test_msg_recoverydevice_bip39_dryrun.py::test_bad_parameters[label-test]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||||
"test_msg_recoverydevice_bip39_dryrun.py::test_bad_parameters[language-test]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
"test_msg_recoverydevice_bip39_dryrun.py::test_bad_parameters[language-test]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||||
"test_msg_recoverydevice_bip39_dryrun.py::test_bad_parameters[passphrase_protection-True]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
"test_msg_recoverydevice_bip39_dryrun.py::test_bad_parameters[passphrase_protection-True]": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
|
||||||
@ -394,10 +394,10 @@
|
|||||||
"test_msg_signtx.py-test_incorrect_input_script_type[2]": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",
|
"test_msg_signtx.py-test_incorrect_input_script_type[2]": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",
|
||||||
"test_msg_signtx.py-test_incorrect_output_script_type[0]": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",
|
"test_msg_signtx.py-test_incorrect_output_script_type[0]": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",
|
||||||
"test_msg_signtx.py-test_incorrect_output_script_type[1]": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",
|
"test_msg_signtx.py-test_incorrect_output_script_type[1]": "ff8306b910f6886638e30736acd025ff7f45dde3c6648de1f6c6922bc6f590c5",
|
||||||
"test_msg_signtx.py-test_lock_time[1-4294967295]": "d805244ea557c3695101a6f79f13045f22bc16d5608744e0321eab7f3a98d8b0",
|
"test_msg_signtx.py-test_lock_time[1-4294967295]": "da3d6b38ec9264e38f3547427eb748d7b94e0802eb2aee411e0f72bf97620280",
|
||||||
"test_msg_signtx.py-test_lock_time[499999999-4294967294]": "23a154e7b40680161bb099cfc6702d75909c222056867515647123573eef1716",
|
"test_msg_signtx.py-test_lock_time[499999999-4294967294]": "23a154e7b40680161bb099cfc6702d75909c222056867515647123573eef1716",
|
||||||
"test_msg_signtx.py-test_lock_time[500000000-4294967294]": "2ac61446c20785e45223a20ae90660905fedf20d2a383a65fcbc1edd5fe87ad1",
|
"test_msg_signtx.py-test_lock_time[500000000-4294967294]": "2ac61446c20785e45223a20ae90660905fedf20d2a383a65fcbc1edd5fe87ad1",
|
||||||
"test_msg_signtx.py-test_lots_of_change": "9e143458b399d187b6a3060fc95b998822f5a7ed67d6915610fd02c0ccab791e",
|
"test_msg_signtx.py-test_lots_of_change": "0fc26d5d47b2fd07b3423c4092cdeb27c1af2a8095eed375dd1e8db10766135a",
|
||||||
"test_msg_signtx.py-test_lots_of_inputs": "7d02dd952be20b46005af23c8b46a95590529d8e838459e540eca0b058fc32c1",
|
"test_msg_signtx.py-test_lots_of_inputs": "7d02dd952be20b46005af23c8b46a95590529d8e838459e540eca0b058fc32c1",
|
||||||
"test_msg_signtx.py-test_lots_of_outputs": "f9d50f30dbdaeddf1f54e8bf76dce07fa9d40dcb7ef36a908e76123f2151501c",
|
"test_msg_signtx.py-test_lots_of_outputs": "f9d50f30dbdaeddf1f54e8bf76dce07fa9d40dcb7ef36a908e76123f2151501c",
|
||||||
"test_msg_signtx.py-test_not_enough_funds": "dbaa027aa1f4b08b138a5965245593dab2a662b0f4d88dd28b82a64f88f5d7fe",
|
"test_msg_signtx.py-test_not_enough_funds": "dbaa027aa1f4b08b138a5965245593dab2a662b0f4d88dd28b82a64f88f5d7fe",
|
||||||
@ -463,8 +463,8 @@
|
|||||||
"test_msg_signtx_grs.py-test_send_segwit_p2sh_change": "6c352ab975a75a150f7c3415a967fb8635395ff8db0de89ecb9c2011cb519509",
|
"test_msg_signtx_grs.py-test_send_segwit_p2sh_change": "6c352ab975a75a150f7c3415a967fb8635395ff8db0de89ecb9c2011cb519509",
|
||||||
"test_msg_signtx_invalid_path.py-test_invalid_path_fail": "b0f22cba2dbab2cd21c15c002b66ed89b6c728b10daa8d0c0e78abd4164a3912",
|
"test_msg_signtx_invalid_path.py-test_invalid_path_fail": "b0f22cba2dbab2cd21c15c002b66ed89b6c728b10daa8d0c0e78abd4164a3912",
|
||||||
"test_msg_signtx_invalid_path.py-test_invalid_path_pass_forkid": "667dcb09b569e5b4e091e6b1ac7e8e057c0c730c931b22f8c0ee64050f3f467b",
|
"test_msg_signtx_invalid_path.py-test_invalid_path_pass_forkid": "667dcb09b569e5b4e091e6b1ac7e8e057c0c730c931b22f8c0ee64050f3f467b",
|
||||||
"test_msg_signtx_komodo.py-test_one_one_fee_sapling": "14bad8852ee51f6fec12677cced9ffafa0cbae91b4ba94e988a800544072ed21",
|
"test_msg_signtx_komodo.py-test_one_one_fee_sapling": "5643a961909bbac2ff7cc7df9766836957ba78b2bf35ba101f2ef7df18445cfe",
|
||||||
"test_msg_signtx_komodo.py-test_one_one_rewards_claim": "751e83d63bf01c6c57047b5e004629d613df75342371cd43a7b4b80a07f4b88d",
|
"test_msg_signtx_komodo.py-test_one_one_rewards_claim": "0cd0d0609522ace94f970ded00f7aebfe0503d2894d516aa9d674b9573779d2d",
|
||||||
"test_msg_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "d72acb396bbc3109054919bddc823e8900bb30b6c41c553922beb449af9bb51d",
|
"test_msg_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "d72acb396bbc3109054919bddc823e8900bb30b6c41c553922beb449af9bb51d",
|
||||||
"test_msg_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "50b846945367990f0b6bcad9161cb899b265c9540a2578e0f5c39533a7ec0010",
|
"test_msg_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "50b846945367990f0b6bcad9161cb899b265c9540a2578e0f5c39533a7ec0010",
|
||||||
"test_msg_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "d72acb396bbc3109054919bddc823e8900bb30b6c41c553922beb449af9bb51d",
|
"test_msg_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "d72acb396bbc3109054919bddc823e8900bb30b6c41c553922beb449af9bb51d",
|
||||||
@ -551,17 +551,17 @@
|
|||||||
"test_msg_tezos_sign_tx.py-test_tezos_smart_contract_transfer": "2d6b7f18fb79676707e58804414553e95bf3aca8eb15bc513137e81de73441a0",
|
"test_msg_tezos_sign_tx.py-test_tezos_smart_contract_transfer": "2d6b7f18fb79676707e58804414553e95bf3aca8eb15bc513137e81de73441a0",
|
||||||
"test_msg_tezos_sign_tx.py-test_tezos_smart_contract_transfer_to_contract": "88eed16fe60952c069326b1a74b3ee3d65598f42381e51d9a17ed8618a567612",
|
"test_msg_tezos_sign_tx.py-test_tezos_smart_contract_transfer_to_contract": "88eed16fe60952c069326b1a74b3ee3d65598f42381e51d9a17ed8618a567612",
|
||||||
"test_msg_verifymessage.py-test_message_grs": "6cc8d8e5abffb5e956e6f5bb9ff7c973ec85209012deb27ee0d234301a1c9819",
|
"test_msg_verifymessage.py-test_message_grs": "6cc8d8e5abffb5e956e6f5bb9ff7c973ec85209012deb27ee0d234301a1c9819",
|
||||||
"test_msg_verifymessage.py-test_message_long": "3cdeddf3c32147a1a7bbf0b6b303d022a6ce6c13f8ed1f480ca95972c95e3467",
|
"test_msg_verifymessage.py-test_message_long": "e234504e23eab8159e229617c183db6a3eac411bdb49cadd0502361d52c00455",
|
||||||
"test_msg_verifymessage.py-test_message_testnet": "8649c8d0eeaef9bfa0b3d13466bc2bb60f2059b52effd3937ffdf3443287b78a",
|
"test_msg_verifymessage.py-test_message_testnet": "8649c8d0eeaef9bfa0b3d13466bc2bb60f2059b52effd3937ffdf3443287b78a",
|
||||||
"test_msg_verifymessage.py-test_message_verify": "dc98df956c9160bdaa6535c3de2760f86e31e5200c453d1593c9791b97214138",
|
"test_msg_verifymessage.py-test_message_verify": "dc98df956c9160bdaa6535c3de2760f86e31e5200c453d1593c9791b97214138",
|
||||||
"test_msg_verifymessage.py-test_message_verify_bcash": "55fcea392b0f4cb3cac16d172edf6024fa0de5ec6d9421fa4eca4ef245fe16ca",
|
"test_msg_verifymessage.py-test_message_verify_bcash": "55fcea392b0f4cb3cac16d172edf6024fa0de5ec6d9421fa4eca4ef245fe16ca",
|
||||||
"test_msg_verifymessage.py-test_verify_bitcoind": "f4c1e9be6d5f3ad2aa5fbc23389ea44f185b606b5c7eed4b72506cc2afd8829f",
|
"test_msg_verifymessage.py-test_verify_bitcoind": "f4c1e9be6d5f3ad2aa5fbc23389ea44f185b606b5c7eed4b72506cc2afd8829f",
|
||||||
"test_msg_verifymessage.py-test_verify_utf": "a312ddde284be0b6f251c3474cf7c2a96ac5b7bd3f27da6c2203eb06fdce8655",
|
"test_msg_verifymessage.py-test_verify_utf": "a312ddde284be0b6f251c3474cf7c2a96ac5b7bd3f27da6c2203eb06fdce8655",
|
||||||
"test_msg_verifymessage_segwit.py-test_message_long": "fd89ce23a94862326524b2ac4edaf7153553960a2168e6e148c59734030da305",
|
"test_msg_verifymessage_segwit.py-test_message_long": "9224423991d092bb9aed5cb91199c939cbd7168696bf2474d4319b0d494abf94",
|
||||||
"test_msg_verifymessage_segwit.py-test_message_testnet": "f602742784969a434a44dee2e7d939af1c5f24beb5f78cca397a5cab85176173",
|
"test_msg_verifymessage_segwit.py-test_message_testnet": "f602742784969a434a44dee2e7d939af1c5f24beb5f78cca397a5cab85176173",
|
||||||
"test_msg_verifymessage_segwit.py-test_message_verify": "3bb8619103ed01ae6f8f7744e7ef3031f42699c699b0b4866ac8eac707e04428",
|
"test_msg_verifymessage_segwit.py-test_message_verify": "3bb8619103ed01ae6f8f7744e7ef3031f42699c699b0b4866ac8eac707e04428",
|
||||||
"test_msg_verifymessage_segwit.py-test_verify_utf": "a1a17b676a1a0e44c122a815825ef572edb77d83d4ba31ef20b9b2e54f44ccfe",
|
"test_msg_verifymessage_segwit.py-test_verify_utf": "a1a17b676a1a0e44c122a815825ef572edb77d83d4ba31ef20b9b2e54f44ccfe",
|
||||||
"test_msg_verifymessage_segwit_native.py-test_message_long": "cc515be104597dffdd0389e3dcf1a7cd42cc2d8c8586d48466d6f269eabff66f",
|
"test_msg_verifymessage_segwit_native.py-test_message_long": "f8075cdfb9d4c6790109a74d698150bd609ce2684ab8a98a81b9c4c7098f02aa",
|
||||||
"test_msg_verifymessage_segwit_native.py-test_message_testnet": "c4dc0ee5a473449455a58675d6d1a72ddc131de012e69f9c95df9a314a4650fc",
|
"test_msg_verifymessage_segwit_native.py-test_message_testnet": "c4dc0ee5a473449455a58675d6d1a72ddc131de012e69f9c95df9a314a4650fc",
|
||||||
"test_msg_verifymessage_segwit_native.py-test_message_verify": "3aeca0b02254b83988008b5129812a749f320add09146d189fa294f2b5c80c34",
|
"test_msg_verifymessage_segwit_native.py-test_message_verify": "3aeca0b02254b83988008b5129812a749f320add09146d189fa294f2b5c80c34",
|
||||||
"test_msg_verifymessage_segwit_native.py-test_verify_utf": "62d12291ee0f0d4639d861ea61d55c9944c37aad24bd70dd35877e9d12a2b731",
|
"test_msg_verifymessage_segwit_native.py-test_verify_utf": "62d12291ee0f0d4639d861ea61d55c9944c37aad24bd70dd35877e9d12a2b731",
|
||||||
|
Loading…
Reference in New Issue
Block a user