mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-25 14:50:57 +00:00
refactor(core): convert rest of apps.bitcoin to layouts
This commit is contained in:
parent
b1e38fe382
commit
2b6ea25712
@ -4,10 +4,9 @@ from trezor import ui
|
|||||||
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
from trezor.strings import format_amount
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.components.tt.text import Text
|
from trezor.ui.layouts import confirm_action, confirm_coinjoin, require
|
||||||
|
|
||||||
from apps.base import set_authorization
|
from apps.base import set_authorization
|
||||||
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
|
||||||
from apps.common.paths import validate_path
|
from apps.common.paths import validate_path
|
||||||
|
|
||||||
from .authorization import FEE_PER_ANONYMITY_DECIMALS, CoinJoinAuthorization
|
from .authorization import FEE_PER_ANONYMITY_DECIMALS, CoinJoinAuthorization
|
||||||
@ -46,30 +45,30 @@ async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin) -> Succe
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
text = Text("Authorize CoinJoin", ui.ICON_RECOVERY)
|
await require(
|
||||||
text.normal("Do you really want to")
|
confirm_action(
|
||||||
text.normal("take part in a CoinJoin")
|
ctx,
|
||||||
text.normal("transaction at:")
|
"coinjoin_coordinator",
|
||||||
text.mono(msg.coordinator)
|
title="Authorize CoinJoin",
|
||||||
await require_confirm(ctx, text)
|
description="Do you really want to take part in a CoinJoin transaction at:\n{}",
|
||||||
|
description_param=msg.coordinator,
|
||||||
text = Text("Authorize CoinJoin", ui.ICON_RECOVERY)
|
description_param_font=ui.MONO,
|
||||||
if msg.fee_per_anonymity is not None:
|
icon=ui.ICON_RECOVERY,
|
||||||
text.normal("Fee per anonymity set:")
|
)
|
||||||
text.bold(
|
)
|
||||||
"{} %".format(
|
|
||||||
format_amount(msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS)
|
fee_per_anonymity = None
|
||||||
)
|
if msg.fee_per_anonymity is not None:
|
||||||
)
|
fee_per_anonymity = format_amount(
|
||||||
text.normal("Maximum total fees:")
|
msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS
|
||||||
text.bold(
|
)
|
||||||
format_coin_amount(
|
await require(
|
||||||
msg.max_total_fee,
|
confirm_coinjoin(
|
||||||
coin,
|
ctx,
|
||||||
msg.amount_unit,
|
fee_per_anonymity,
|
||||||
|
format_coin_amount(msg.max_total_fee, coin, msg.amount_unit),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await require_hold_to_confirm(ctx, text)
|
|
||||||
|
|
||||||
set_authorization(CoinJoinAuthorization(msg, keychain, coin))
|
set_authorization(CoinJoinAuthorization(msg, keychain, coin))
|
||||||
|
|
||||||
|
@ -3,9 +3,8 @@ from ubinascii import hexlify
|
|||||||
from trezor import ui, wire
|
from trezor import ui, wire
|
||||||
from trezor.messages.GetOwnershipProof import GetOwnershipProof
|
from trezor.messages.GetOwnershipProof import GetOwnershipProof
|
||||||
from trezor.messages.OwnershipProof import OwnershipProof
|
from trezor.messages.OwnershipProof import OwnershipProof
|
||||||
from trezor.ui.components.tt.text import Text
|
from trezor.ui.layouts import confirm_action, confirm_hex, require
|
||||||
|
|
||||||
from apps.common.confirm import require_confirm
|
|
||||||
from apps.common.paths import validate_path
|
from apps.common.paths import validate_path
|
||||||
|
|
||||||
from . import addresses, common, scripts
|
from . import addresses, common, scripts
|
||||||
@ -65,25 +64,28 @@ async def get_ownership_proof(
|
|||||||
|
|
||||||
# In order to set the "user confirmation" bit in the proof, the user must actually confirm.
|
# In order to set the "user confirmation" bit in the proof, the user must actually confirm.
|
||||||
if msg.user_confirmation and not authorization:
|
if msg.user_confirmation and not authorization:
|
||||||
text = Text("Proof of ownership", ui.ICON_CONFIG)
|
|
||||||
text.normal("Do you want to create a")
|
|
||||||
if not msg.commitment_data:
|
if not msg.commitment_data:
|
||||||
text.normal("proof of ownership?")
|
await require(
|
||||||
else:
|
confirm_action(
|
||||||
hex_data = hexlify(msg.commitment_data).decode()
|
ctx,
|
||||||
text.normal("proof of ownership for:")
|
"confirm_ownership_proof",
|
||||||
if len(hex_data) > 3 * _MAX_MONO_LINE:
|
title="Proof of ownership",
|
||||||
text.mono(hex_data[0:_MAX_MONO_LINE])
|
description="Do you want to create a proof of ownership?",
|
||||||
text.mono(
|
|
||||||
hex_data[_MAX_MONO_LINE : 3 * _MAX_MONO_LINE // 2 - 1]
|
|
||||||
+ "..."
|
|
||||||
+ hex_data[-3 * _MAX_MONO_LINE // 2 + 2 : -_MAX_MONO_LINE]
|
|
||||||
)
|
)
|
||||||
text.mono(hex_data[-_MAX_MONO_LINE:])
|
)
|
||||||
else:
|
else:
|
||||||
text.mono(hex_data)
|
await require(
|
||||||
|
confirm_hex(
|
||||||
await require_confirm(ctx, text)
|
ctx,
|
||||||
|
"confirm_ownership_proof",
|
||||||
|
title="Proof of ownership",
|
||||||
|
description="Do you want to create a proof of ownership for:",
|
||||||
|
data=hexlify(msg.commitment_data).decode(),
|
||||||
|
icon=ui.ICON_CONFIG,
|
||||||
|
icon_color=ui.ORANGE_ICON,
|
||||||
|
truncate_middle=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
ownership_proof, signature = generate_proof(
|
ownership_proof, signature = generate_proof(
|
||||||
node,
|
node,
|
||||||
|
@ -2,9 +2,10 @@ from trezor import wire
|
|||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import secp256k1
|
||||||
from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS
|
from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS
|
||||||
from trezor.messages.MessageSignature import MessageSignature
|
from trezor.messages.MessageSignature import MessageSignature
|
||||||
|
from trezor.ui.layouts import confirm_signverify, require
|
||||||
|
|
||||||
from apps.common.paths import validate_path
|
from apps.common.paths import validate_path
|
||||||
from apps.common.signverify import message_digest, require_confirm_sign_message
|
from apps.common.signverify import decode_message, message_digest
|
||||||
|
|
||||||
from .addresses import get_address
|
from .addresses import get_address
|
||||||
from .keychain import with_keychain
|
from .keychain import with_keychain
|
||||||
@ -25,7 +26,7 @@ async def sign_message(
|
|||||||
script_type = msg.script_type or 0
|
script_type = msg.script_type or 0
|
||||||
|
|
||||||
await validate_path(ctx, keychain, address_n)
|
await validate_path(ctx, keychain, address_n)
|
||||||
await require_confirm_sign_message(ctx, coin.coin_shortcut, message)
|
await require(confirm_signverify(ctx, coin.coin_shortcut, decode_message(message)))
|
||||||
|
|
||||||
node = keychain.derive(address_n)
|
node = keychain.derive(address_n)
|
||||||
seckey = node.private_key()
|
seckey = node.private_key()
|
||||||
|
@ -59,9 +59,9 @@ async def confirm_output(
|
|||||||
layout = layouts.confirm_hex(
|
layout = layouts.confirm_hex(
|
||||||
ctx,
|
ctx,
|
||||||
"op_return",
|
"op_return",
|
||||||
"OP_RETURN",
|
title="OP_RETURN",
|
||||||
hexlify(data).decode(),
|
data=hexlify(data).decode(),
|
||||||
ButtonRequestType.ConfirmOutput,
|
br_code=ButtonRequestType.ConfirmOutput,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert output.address is not None
|
assert output.address is not None
|
||||||
|
@ -2,9 +2,10 @@ from trezor import wire
|
|||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import secp256k1
|
||||||
from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS
|
from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
|
from trezor.ui.layouts import confirm_signverify, require
|
||||||
|
|
||||||
from apps.common import coins
|
from apps.common import coins
|
||||||
from apps.common.signverify import message_digest, require_confirm_verify_message
|
from apps.common.signverify import decode_message, message_digest
|
||||||
|
|
||||||
from .addresses import (
|
from .addresses import (
|
||||||
address_p2wpkh,
|
address_p2wpkh,
|
||||||
@ -62,8 +63,13 @@ async def verify_message(ctx: wire.Context, msg: VerifyMessage) -> Success:
|
|||||||
if addr != address:
|
if addr != address:
|
||||||
raise wire.ProcessError("Invalid signature")
|
raise wire.ProcessError("Invalid signature")
|
||||||
|
|
||||||
await require_confirm_verify_message(
|
await require(
|
||||||
ctx, address_short(coin, address), coin.coin_shortcut, message
|
confirm_signverify(
|
||||||
|
ctx,
|
||||||
|
coin.coin_shortcut,
|
||||||
|
decode_message(message),
|
||||||
|
address=address_short(coin, address),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return Success(message="Message verified")
|
return Success(message="Message verified")
|
||||||
|
@ -3,8 +3,7 @@ from micropython import const
|
|||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
from trezor.ui.components.tt.button import ButtonDefault
|
from trezor.ui.components.tt.button import ButtonDefault
|
||||||
from trezor.ui.components.tt.scroll import Paginated
|
from trezor.ui.components.tt.text import Text
|
||||||
from trezor.ui.components.tt.text import TEXT_MAX_LINES, Span, Text
|
|
||||||
from trezor.ui.container import Container
|
from trezor.ui.container import Container
|
||||||
from trezor.ui.qr import Qr
|
from trezor.ui.qr import Qr
|
||||||
from trezor.utils import chunks
|
from trezor.utils import chunks
|
||||||
@ -13,7 +12,7 @@ from apps.common import HARDENED
|
|||||||
from apps.common.confirm import confirm
|
from apps.common.confirm import confirm
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Iterable, Iterator, List, Union
|
from typing import Iterable, Iterator
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
|
|
||||||
|
|
||||||
@ -55,48 +54,3 @@ def address_n_to_str(address_n: Iterable[int]) -> str:
|
|||||||
return "m"
|
return "m"
|
||||||
|
|
||||||
return "m/" + "/".join([path_item(i) for i in address_n])
|
return "m/" + "/".join([path_item(i) for i in address_n])
|
||||||
|
|
||||||
|
|
||||||
def paginate_text(
|
|
||||||
text: str,
|
|
||||||
header: str,
|
|
||||||
font: int = ui.NORMAL,
|
|
||||||
header_icon: str = ui.ICON_DEFAULT,
|
|
||||||
icon_color: int = ui.ORANGE_ICON,
|
|
||||||
break_words: bool = False,
|
|
||||||
) -> Union[Text, Paginated]:
|
|
||||||
span = Span(text, 0, font, break_words=break_words)
|
|
||||||
if span.count_lines() <= TEXT_MAX_LINES:
|
|
||||||
result = Text(
|
|
||||||
header,
|
|
||||||
header_icon=header_icon,
|
|
||||||
icon_color=icon_color,
|
|
||||||
new_lines=False,
|
|
||||||
)
|
|
||||||
result.content = [font, text]
|
|
||||||
return result
|
|
||||||
|
|
||||||
else:
|
|
||||||
pages: List[ui.Component] = []
|
|
||||||
span.reset(text, 0, font, break_words=break_words, line_width=204)
|
|
||||||
while span.has_more_content():
|
|
||||||
# advance to first line of the page
|
|
||||||
span.next_line()
|
|
||||||
page = Text(
|
|
||||||
header,
|
|
||||||
header_icon=header_icon,
|
|
||||||
icon_color=icon_color,
|
|
||||||
new_lines=False,
|
|
||||||
content_offset=0,
|
|
||||||
char_offset=span.start,
|
|
||||||
line_width=204,
|
|
||||||
render_page_overflow=False,
|
|
||||||
)
|
|
||||||
page.content = [font, text]
|
|
||||||
pages.append(page)
|
|
||||||
|
|
||||||
# roll over the remaining lines on the page
|
|
||||||
for _ in range(TEXT_MAX_LINES - 1):
|
|
||||||
span.next_line()
|
|
||||||
|
|
||||||
return Paginated(pages)
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
from ubinascii import hexlify
|
from ubinascii import hexlify
|
||||||
|
|
||||||
from trezor import ui, utils, wire
|
from trezor import utils, wire
|
||||||
from trezor.crypto.hashlib import blake256, sha256
|
from trezor.crypto.hashlib import blake256, sha256
|
||||||
from trezor.ui.components.tt.text import Text
|
|
||||||
|
|
||||||
from apps.common.confirm import require_confirm
|
|
||||||
from apps.common.layout import paginate_text, split_address
|
|
||||||
from apps.common.writers import write_bitcoin_varint
|
from apps.common.writers import write_bitcoin_varint
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
@ -34,25 +31,3 @@ def decode_message(message: bytes) -> str:
|
|||||||
return bytes(message).decode()
|
return bytes(message).decode()
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
return "hex(%s)" % hexlify(message).decode()
|
return "hex(%s)" % hexlify(message).decode()
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_sign_message(
|
|
||||||
ctx: wire.Context, coin: str, message: bytes
|
|
||||||
) -> None:
|
|
||||||
header = "Sign {} message".format(coin)
|
|
||||||
await require_confirm(ctx, paginate_text(decode_message(message), header))
|
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_verify_message(
|
|
||||||
ctx: wire.Context, address: str, coin: str, message: bytes
|
|
||||||
) -> None:
|
|
||||||
header = "Verify {} message".format(coin)
|
|
||||||
text = Text(header)
|
|
||||||
text.bold("Confirm address:")
|
|
||||||
text.mono(*split_address(address))
|
|
||||||
await require_confirm(ctx, text)
|
|
||||||
|
|
||||||
await require_confirm(
|
|
||||||
ctx,
|
|
||||||
paginate_text(decode_message(message), header, font=ui.MONO),
|
|
||||||
)
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import secp256k1
|
||||||
from trezor.crypto.hashlib import sha3_256
|
from trezor.crypto.hashlib import sha3_256
|
||||||
from trezor.messages.EthereumMessageSignature import EthereumMessageSignature
|
from trezor.messages.EthereumMessageSignature import EthereumMessageSignature
|
||||||
|
from trezor.ui.layouts import confirm_signverify, require
|
||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from apps.common import paths
|
from apps.common import paths
|
||||||
from apps.common.signverify import require_confirm_sign_message
|
from apps.common.signverify import decode_message
|
||||||
|
|
||||||
from . import address
|
from . import address
|
||||||
from .keychain import PATTERNS_ADDRESS, with_keychain_from_path
|
from .keychain import PATTERNS_ADDRESS, with_keychain_from_path
|
||||||
@ -22,7 +23,7 @@ def message_digest(message):
|
|||||||
@with_keychain_from_path(*PATTERNS_ADDRESS)
|
@with_keychain_from_path(*PATTERNS_ADDRESS)
|
||||||
async def sign_message(ctx, msg, keychain):
|
async def sign_message(ctx, msg, keychain):
|
||||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||||
await require_confirm_sign_message(ctx, "ETH", msg.message)
|
await require(confirm_signverify(ctx, "ETH", decode_message(msg.message)))
|
||||||
|
|
||||||
node = keychain.derive(msg.address_n)
|
node = keychain.derive(msg.address_n)
|
||||||
signature = secp256k1.sign(
|
signature = secp256k1.sign(
|
||||||
|
@ -2,8 +2,9 @@ from trezor import wire
|
|||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import secp256k1
|
||||||
from trezor.crypto.hashlib import sha3_256
|
from trezor.crypto.hashlib import sha3_256
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
|
from trezor.ui.layouts import confirm_signverify, require
|
||||||
|
|
||||||
from apps.common.signverify import require_confirm_verify_message
|
from apps.common.signverify import decode_message
|
||||||
|
|
||||||
from .address import address_from_bytes, bytes_from_address
|
from .address import address_from_bytes, bytes_from_address
|
||||||
from .sign_message import message_digest
|
from .sign_message import message_digest
|
||||||
@ -28,6 +29,8 @@ async def verify_message(ctx, msg):
|
|||||||
|
|
||||||
address = address_from_bytes(address_bytes)
|
address = address_from_bytes(address_bytes)
|
||||||
|
|
||||||
await require_confirm_verify_message(ctx, address, "ETH", msg.message)
|
await require(
|
||||||
|
confirm_signverify(ctx, "ETH", decode_message(msg.message), address=address)
|
||||||
|
)
|
||||||
|
|
||||||
return Success(message="Message verified")
|
return Success(message="Message verified")
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
from trezor.crypto.curve import ed25519
|
from trezor.crypto.curve import ed25519
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
from trezor.messages.LiskMessageSignature import LiskMessageSignature
|
from trezor.messages.LiskMessageSignature import LiskMessageSignature
|
||||||
|
from trezor.ui.layouts import confirm_signverify, require
|
||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from apps.common import paths
|
from apps.common import paths
|
||||||
from apps.common.keychain import auto_keychain
|
from apps.common.keychain import auto_keychain
|
||||||
from apps.common.signverify import require_confirm_sign_message
|
from apps.common.signverify import decode_message
|
||||||
from apps.common.writers import write_bitcoin_varint
|
from apps.common.writers import write_bitcoin_varint
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ def message_digest(message):
|
|||||||
@auto_keychain(__name__)
|
@auto_keychain(__name__)
|
||||||
async def sign_message(ctx, msg, keychain):
|
async def sign_message(ctx, msg, keychain):
|
||||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||||
await require_confirm_sign_message(ctx, "Lisk", msg.message)
|
await require(confirm_signverify(ctx, "Lisk", decode_message(msg.message)))
|
||||||
|
|
||||||
node = keychain.derive(msg.address_n)
|
node = keychain.derive(msg.address_n)
|
||||||
seckey = node.private_key()
|
seckey = node.private_key()
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.crypto.curve import ed25519
|
from trezor.crypto.curve import ed25519
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
|
from trezor.ui.layouts import confirm_signverify, require
|
||||||
|
|
||||||
from apps.common.signverify import require_confirm_verify_message
|
from apps.common.signverify import decode_message
|
||||||
|
|
||||||
from .helpers import get_address_from_public_key
|
from .helpers import get_address_from_public_key
|
||||||
from .sign_message import message_digest
|
from .sign_message import message_digest
|
||||||
@ -15,6 +16,8 @@ async def verify_message(ctx, msg):
|
|||||||
raise wire.ProcessError("Invalid signature")
|
raise wire.ProcessError("Invalid signature")
|
||||||
|
|
||||||
address = get_address_from_public_key(msg.public_key)
|
address = get_address_from_public_key(msg.public_key)
|
||||||
await require_confirm_verify_message(ctx, address, "Lisk", msg.message)
|
await require(
|
||||||
|
confirm_signverify(ctx, "Lisk", decode_message(msg.message), address=address)
|
||||||
|
)
|
||||||
|
|
||||||
return Success(message="Message verified")
|
return Success(message="Message verified")
|
||||||
|
@ -3,14 +3,18 @@ from micropython import const
|
|||||||
from trezor import loop, res, ui, utils
|
from trezor import loop, res, ui, utils
|
||||||
|
|
||||||
from .button import Button, ButtonCancel, ButtonConfirm, ButtonDefault
|
from .button import Button, ButtonCancel, ButtonConfirm, ButtonDefault
|
||||||
from .confirm import CANCELLED, CONFIRMED
|
from .confirm import CANCELLED, CONFIRMED, Confirm
|
||||||
from .swipe import SWIPE_DOWN, SWIPE_UP, SWIPE_VERTICAL, Swipe
|
from .swipe import SWIPE_DOWN, SWIPE_UP, SWIPE_VERTICAL, Swipe
|
||||||
|
from .text import TEXT_MAX_LINES, Span, Text
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
from apps.debug import confirm_signal, swipe_signal, notify_layout_change
|
from apps.debug import confirm_signal, swipe_signal, notify_layout_change
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple, Union
|
||||||
|
|
||||||
|
|
||||||
|
_PAGINATED_LINE_WIDTH = const(204)
|
||||||
|
|
||||||
|
|
||||||
def render_scrollbar(pages: int, page: int) -> None:
|
def render_scrollbar(pages: int, page: int) -> None:
|
||||||
@ -231,3 +235,53 @@ class PaginatedWithButtons(ui.Layout):
|
|||||||
|
|
||||||
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
return super().create_tasks() + (confirm_signal(),)
|
return super().create_tasks() + (confirm_signal(),)
|
||||||
|
|
||||||
|
|
||||||
|
def paginate_text(
|
||||||
|
text: str,
|
||||||
|
header: str,
|
||||||
|
font: int = ui.NORMAL,
|
||||||
|
header_icon: str = ui.ICON_DEFAULT,
|
||||||
|
icon_color: int = ui.ORANGE_ICON,
|
||||||
|
break_words: bool = False,
|
||||||
|
) -> Union[Confirm, Paginated]:
|
||||||
|
span = Span(text, 0, font, break_words=break_words)
|
||||||
|
if span.count_lines() <= TEXT_MAX_LINES:
|
||||||
|
result = Text(
|
||||||
|
header,
|
||||||
|
header_icon=header_icon,
|
||||||
|
icon_color=icon_color,
|
||||||
|
new_lines=False,
|
||||||
|
break_words=break_words,
|
||||||
|
)
|
||||||
|
result.content = [font, text]
|
||||||
|
return Confirm(result)
|
||||||
|
|
||||||
|
else:
|
||||||
|
pages: List[ui.Component] = []
|
||||||
|
span.reset(
|
||||||
|
text, 0, font, break_words=break_words, line_width=_PAGINATED_LINE_WIDTH
|
||||||
|
)
|
||||||
|
while span.has_more_content():
|
||||||
|
# advance to first line of the page
|
||||||
|
span.next_line()
|
||||||
|
page = Text(
|
||||||
|
header,
|
||||||
|
header_icon=header_icon,
|
||||||
|
icon_color=icon_color,
|
||||||
|
new_lines=False,
|
||||||
|
content_offset=0,
|
||||||
|
char_offset=span.start,
|
||||||
|
line_width=_PAGINATED_LINE_WIDTH,
|
||||||
|
break_words=break_words,
|
||||||
|
render_page_overflow=False,
|
||||||
|
)
|
||||||
|
page.content = [font, text]
|
||||||
|
pages.append(page)
|
||||||
|
|
||||||
|
# roll over the remaining lines on the page
|
||||||
|
for _ in range(TEXT_MAX_LINES - 1):
|
||||||
|
span.next_line()
|
||||||
|
|
||||||
|
pages[-1] = Confirm(pages[-1])
|
||||||
|
return Paginated(pages)
|
||||||
|
@ -11,8 +11,8 @@ from ..components.common import break_path_to_lines
|
|||||||
from ..components.common.confirm import is_confirmed
|
from ..components.common.confirm import is_confirmed
|
||||||
from ..components.tt.button import ButtonCancel, ButtonDefault
|
from ..components.tt.button import ButtonCancel, ButtonDefault
|
||||||
from ..components.tt.confirm import Confirm, HoldToConfirm
|
from ..components.tt.confirm import Confirm, HoldToConfirm
|
||||||
from ..components.tt.scroll import Paginated
|
from ..components.tt.scroll import Paginated, paginate_text
|
||||||
from ..components.tt.text import Text
|
from ..components.tt.text import Span, Text
|
||||||
from ..constants.tt import (
|
from ..constants.tt import (
|
||||||
MONO_CHARS_PER_LINE,
|
MONO_CHARS_PER_LINE,
|
||||||
MONO_HEX_PER_LINE,
|
MONO_HEX_PER_LINE,
|
||||||
@ -38,6 +38,7 @@ __all__ = (
|
|||||||
"confirm_backup",
|
"confirm_backup",
|
||||||
"confirm_path_warning",
|
"confirm_path_warning",
|
||||||
"confirm_sign_identity",
|
"confirm_sign_identity",
|
||||||
|
"confirm_signverify",
|
||||||
"show_address",
|
"show_address",
|
||||||
"show_error",
|
"show_error",
|
||||||
"show_pubkey",
|
"show_pubkey",
|
||||||
@ -53,6 +54,7 @@ __all__ = (
|
|||||||
"confirm_replacement",
|
"confirm_replacement",
|
||||||
"confirm_modify_output",
|
"confirm_modify_output",
|
||||||
"confirm_modify_fee",
|
"confirm_modify_fee",
|
||||||
|
"confirm_coinjoin",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -63,6 +65,7 @@ async def confirm_action(
|
|||||||
action: str = None,
|
action: str = None,
|
||||||
description: str = None,
|
description: str = None,
|
||||||
description_param: str = None,
|
description_param: str = None,
|
||||||
|
description_param_font: int = ui.BOLD,
|
||||||
verb: Union[str, bytes, None] = Confirm.DEFAULT_CONFIRM,
|
verb: Union[str, bytes, None] = Confirm.DEFAULT_CONFIRM,
|
||||||
verb_cancel: Union[str, bytes, None] = Confirm.DEFAULT_CANCEL,
|
verb_cancel: Union[str, bytes, None] = Confirm.DEFAULT_CANCEL,
|
||||||
hold: bool = False,
|
hold: bool = False,
|
||||||
@ -82,7 +85,9 @@ async def confirm_action(
|
|||||||
|
|
||||||
if reverse and description is not None:
|
if reverse and description is not None:
|
||||||
text.format_parametrized(
|
text.format_parametrized(
|
||||||
description, description_param if description_param is not None else ""
|
description,
|
||||||
|
description_param if description_param is not None else "",
|
||||||
|
param_font=description_param_font,
|
||||||
)
|
)
|
||||||
elif action is not None:
|
elif action is not None:
|
||||||
text.bold(action)
|
text.bold(action)
|
||||||
@ -96,7 +101,9 @@ async def confirm_action(
|
|||||||
text.bold(action)
|
text.bold(action)
|
||||||
elif description is not None:
|
elif description is not None:
|
||||||
text.format_parametrized(
|
text.format_parametrized(
|
||||||
description, description_param if description_param is not None else ""
|
description,
|
||||||
|
description_param if description_param is not None else "",
|
||||||
|
param_font=description_param_font,
|
||||||
)
|
)
|
||||||
|
|
||||||
cls = HoldToConfirm if hold else Confirm
|
cls = HoldToConfirm if hold else Confirm
|
||||||
@ -208,11 +215,21 @@ def _split_address(address: str) -> Iterator[str]:
|
|||||||
return chunks(address, MONO_CHARS_PER_LINE)
|
return chunks(address, MONO_CHARS_PER_LINE)
|
||||||
|
|
||||||
|
|
||||||
def _hex_lines(
|
def _truncate_hex(
|
||||||
hex_data: str, lines: int = TEXT_MAX_LINES, width: int = MONO_HEX_PER_LINE
|
hex_data: str,
|
||||||
|
lines: int = TEXT_MAX_LINES,
|
||||||
|
width: int = MONO_HEX_PER_LINE,
|
||||||
|
middle: bool = False,
|
||||||
) -> Iterator[str]:
|
) -> Iterator[str]:
|
||||||
if len(hex_data) >= width * lines:
|
if len(hex_data) >= width * lines:
|
||||||
hex_data = hex_data[: (width * lines - 3)] + "..."
|
if middle:
|
||||||
|
hex_data = (
|
||||||
|
hex_data[: lines * width // 2 - 1]
|
||||||
|
+ "..."
|
||||||
|
+ hex_data[-lines * width // 2 + 2 :]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
hex_data = hex_data[: (width * lines - 3)] + "..."
|
||||||
return chunks(hex_data, width)
|
return chunks(hex_data, width)
|
||||||
|
|
||||||
|
|
||||||
@ -457,14 +474,29 @@ async def confirm_hex(
|
|||||||
br_type: str,
|
br_type: str,
|
||||||
title: str,
|
title: str,
|
||||||
data: str,
|
data: str,
|
||||||
|
description: str = None,
|
||||||
br_code: EnumTypeButtonRequestType = ButtonRequestType.Other,
|
br_code: EnumTypeButtonRequestType = ButtonRequestType.Other,
|
||||||
icon: str = ui.ICON_SEND, # TODO cleanup @ redesign
|
icon: str = ui.ICON_SEND, # TODO cleanup @ redesign
|
||||||
icon_color: int = ui.GREEN, # TODO cleanup @ redesign
|
icon_color: int = ui.GREEN, # TODO cleanup @ redesign
|
||||||
width: int = MONO_HEX_PER_LINE,
|
width: int = MONO_HEX_PER_LINE,
|
||||||
|
truncate_middle: bool = False,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
text = Text(title, icon, icon_color)
|
text = Text(title, icon, icon_color, new_lines=False)
|
||||||
text.mono(*_hex_lines(data, width=width))
|
description_lines = 0
|
||||||
return is_confirmed(await interact(ctx, Confirm(text), br_type, br_code))
|
if description is not None:
|
||||||
|
description_lines = Span(description, 0, ui.NORMAL).count_lines()
|
||||||
|
text.normal(description)
|
||||||
|
text.br()
|
||||||
|
text.mono(
|
||||||
|
*_truncate_hex(
|
||||||
|
data,
|
||||||
|
lines=TEXT_MAX_LINES - description_lines,
|
||||||
|
width=width,
|
||||||
|
middle=truncate_middle,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
content: ui.Layout = Confirm(text)
|
||||||
|
return is_confirmed(await interact(ctx, content, br_type, br_code))
|
||||||
|
|
||||||
|
|
||||||
async def confirm_total(
|
async def confirm_total(
|
||||||
@ -519,7 +551,7 @@ async def confirm_replacement(
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
text = Text(description, ui.ICON_SEND, ui.GREEN)
|
text = Text(description, ui.ICON_SEND, ui.GREEN)
|
||||||
text.normal("Confirm transaction ID:")
|
text.normal("Confirm transaction ID:")
|
||||||
text.mono(*_hex_lines(txid, TEXT_MAX_LINES - 1))
|
text.mono(*_truncate_hex(txid, TEXT_MAX_LINES - 1))
|
||||||
return is_confirmed(
|
return is_confirmed(
|
||||||
await interact(
|
await interact(
|
||||||
ctx, Confirm(text), "confirm_replacement", ButtonRequestType.SignTx
|
ctx, Confirm(text), "confirm_replacement", ButtonRequestType.SignTx
|
||||||
@ -582,6 +614,22 @@ async def confirm_modify_fee(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def confirm_coinjoin(
|
||||||
|
ctx: wire.GenericContext, fee_per_anonymity: Optional[str], total_fee: str
|
||||||
|
) -> bool:
|
||||||
|
text = Text("Authorize CoinJoin", ui.ICON_RECOVERY, new_lines=False)
|
||||||
|
if fee_per_anonymity is not None:
|
||||||
|
text.normal("Fee per anonymity set:\n")
|
||||||
|
text.bold("{} %\n".format(fee_per_anonymity))
|
||||||
|
text.normal("Maximum total fees:\n")
|
||||||
|
text.bold(total_fee)
|
||||||
|
return is_confirmed(
|
||||||
|
await interact(
|
||||||
|
ctx, HoldToConfirm(text), "coinjoin_final", ButtonRequestType.Other
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO cleanup @ redesign
|
# TODO cleanup @ redesign
|
||||||
async def confirm_sign_identity(
|
async def confirm_sign_identity(
|
||||||
ctx: wire.GenericContext, proto: str, identity: str, challenge_visual: Optional[str]
|
ctx: wire.GenericContext, proto: str, identity: str, challenge_visual: Optional[str]
|
||||||
@ -598,3 +646,33 @@ async def confirm_sign_identity(
|
|||||||
return is_confirmed(
|
return is_confirmed(
|
||||||
await interact(ctx, Confirm(text), "sign_identity", ButtonRequestType.Other)
|
await interact(ctx, Confirm(text), "sign_identity", ButtonRequestType.Other)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def confirm_signverify(
|
||||||
|
ctx: wire.GenericContext, coin: str, message: str, address: str = None
|
||||||
|
) -> bool:
|
||||||
|
if address:
|
||||||
|
header = "Verify {} message".format(coin)
|
||||||
|
font = ui.MONO
|
||||||
|
br_type = "verify_message"
|
||||||
|
|
||||||
|
text = Text(header)
|
||||||
|
text.bold("Confirm address:")
|
||||||
|
text.mono(*_split_address(address))
|
||||||
|
if not is_confirmed(
|
||||||
|
await interact(ctx, Confirm(text), br_type, ButtonRequestType.Other)
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
header = "Sign {} message".format(coin)
|
||||||
|
font = ui.NORMAL
|
||||||
|
br_type = "sign_message"
|
||||||
|
|
||||||
|
return is_confirmed(
|
||||||
|
await interact(
|
||||||
|
ctx,
|
||||||
|
paginate_text(message, header, font=font),
|
||||||
|
br_type,
|
||||||
|
ButtonRequestType.Other,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user