async def require_confirm_sign_identity(ctx, identity, challenge_visual): lines = [] if challenge_visual: lines.append(challenge_visual) lines.append(ui.MONO) lines.extend(chunks(serialize_identity_without_proto(identity), 18)) proto = identity.proto.upper() if identity.proto else "identity" text = Text("Sign %s" % proto) text.normal(*lines) await require_confirm(ctx, text)
async def _require_confirm_paginated(ctx: wire.Context, header: str, fields: List[str], per_page: int) -> None: pages = [] for page in chunks(fields, per_page): if header == "Arbitrary data": text = Text(header, ui.ICON_WIPE, ui.RED) else: text = Text(header, ui.ICON_CONFIRM, ui.GREEN) text.mono(*page) pages.append(text) await require_confirm(ctx, Paginated(pages), ButtonRequestType.ConfirmOutput)
async def layout_reset_device(session_id, msg): from trezor.ui.text import Text from trezor.crypto import hashlib, random, bip39 from trezor.messages.EntropyRequest import EntropyRequest from trezor.messages.Success import Success from trezor.messages import FailureType from trezor.messages import ButtonRequestType from trezor.messages.wire_types import EntropyAck from apps.common.request_pin import request_pin_twice from apps.common.confirm import require_confirm from apps.common import storage if __debug__: global internal_entropy if msg.strength not in (128, 192, 256): raise wire.FailureError( FailureType.Other, 'Invalid strength (has to be 128, 192 or 256 bits)') if storage.is_initialized(): raise wire.FailureError( FailureType.UnexpectedMessage, 'Already initialized') internal_entropy = random.bytes(32) if msg.display_random: entropy_lines = chunks(ubinascii.hexlify(internal_entropy), 16) entropy_content = Text('Internal entropy', ui.ICON_RESET, *entropy_lines) await require_confirm(session_id, entropy_content, ButtonRequestType.ResetDevice) if msg.pin_protection: pin = await request_pin_twice(session_id) else: pin = None external_entropy_ack = await wire.call(session_id, EntropyRequest(), EntropyAck) ctx = hashlib.sha256() ctx.update(internal_entropy) ctx.update(external_entropy_ack.entropy) entropy = ctx.digest() mnemonic = bip39.from_data(entropy[:msg.strength // 8]) await show_mnemonic_by_word(session_id, mnemonic) storage.load_mnemonic(mnemonic) storage.load_settings(pin=pin, passphrase_protection=msg.passphrase_protection, language=msg.language, label=msg.label) return Success(message='Initialized')
async def naive_pagination( ctx, lines, title, icon=ui.ICON_RESET, icon_color=ui.ORANGE, per_page=5 ): from trezor.ui.confirm import CANCELLED, CONFIRMED, DEFAULT_CANCEL, DEFAULT_CONFIRM if isinstance(lines, (list, tuple)): lines = lines else: lines = list(chunks(lines, 16)) pages = paginate_lines(lines, per_page) npages = len(pages) cur_step = 0 code = ButtonRequestType.SignTx iback = res.load(ui.ICON_BACK) inext = res.load(ui.ICON_CLICK) while cur_step <= npages: text = pages[cur_step] fst_page = cur_step == 0 lst_page = cur_step + 1 >= npages cancel_btn = DEFAULT_CANCEL if fst_page else iback cancel_style = ui.BTN_CANCEL if fst_page else ui.BTN_DEFAULT confirm_btn = DEFAULT_CONFIRM if lst_page else inext confirm_style = ui.BTN_CONFIRM if lst_page else ui.BTN_DEFAULT paging = ("%d/%d" % (cur_step + 1, npages)) if npages > 1 else "" content = Text("%s %s" % (title, paging), icon, icon_color=icon_color) content.normal(*text) reaction = await tx_dialog( ctx, code, content, cancel_btn, confirm_btn, cancel_style, confirm_style, (cur_step, npages), ) if fst_page and reaction == CANCELLED: return False elif not lst_page and reaction == CONFIRMED: cur_step += 1 elif lst_page and reaction == CONFIRMED: return True elif reaction == CANCELLED: cur_step -= 1 elif reaction == CONFIRMED: cur_step += 1
def _truncate_hex( hex_data: str, lines: int = TEXT_MAX_LINES, width: int = MONO_HEX_PER_LINE, middle: bool = False, ) -> Iterator[str]: if len(hex_data) >= width * lines: 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)
async def _confirm_share_words(ctx, share_index, share_words, group_index=None): # divide list into thirds, rounding up, so that chunking by `third` always yields # three parts (the last one might be shorter) third = (len(share_words) + 2) // 3 offset = 0 count = len(share_words) for part in utils.chunks(share_words, third): if not await _confirm_word(ctx, share_index, part, offset, count, group_index): return False offset += len(part) return True
async def confirm_sending(ctx: wire.Context, amount: int, to: str) -> None: page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm sending:") page1.bold(format_coin_amount(amount)) page1.normal("to") to_lines = list(chunks(to, 17)) page1.bold(to_lines[0]) pages = [page1] + _paginate_lines(to_lines, 1, "Confirm transaction", ui.ICON_SEND) await require_confirm(ctx, Paginated(pages))
def _paginate_lines( lines: List[str], offset: int, desc: str, icon: str, per_page: int = 4 ) -> List[ui.Component]: pages = [] if len(lines) > offset: to_pages = list(chunks(lines[offset:], per_page)) for page in to_pages: t = Text(desc, icon, ui.GREEN) for line in page: t.bold(line) pages.append(t) return pages
async def show_address( ctx: wire.Context, address: str, address_type: EnumTypeCardanoAddressType, path: List[int], network: str = None, ) -> bool: """ Custom show_address function is needed because cardano addresses don't fit on a single screen. """ address_type_label = "%s address" % ADDRESS_TYPE_NAMES[address_type] page1 = Text(address_type_label, ui.ICON_RECEIVE, ui.GREEN) lines_per_page = 5 lines_used_on_first_page = 0 # assemble first page to be displayed (path + network + whatever part of the address fits) if network is not None: page1.normal("%s network" % network) lines_used_on_first_page += 1 path_str = address_n_to_str(path) page1.mono(path_str) lines_used_on_first_page = min( lines_used_on_first_page + math.ceil(len(path_str) / _MAX_MONO_LINE), lines_per_page, ) address_lines = list(chunks(address, 17)) for address_line in address_lines[:lines_per_page - lines_used_on_first_page]: page1.bold(address_line) # append remaining pages containing the rest of the address pages = [page1] + _paginate_lines( address_lines, lines_per_page - lines_used_on_first_page, address_type_label, ui.ICON_RECEIVE, lines_per_page, ) return await confirm( ctx, Paginated(pages), code=ButtonRequestType.Address, cancel="QR", cancel_style=ButtonDefault, )
async def require_confirm_sign_identity( ctx: wire.Context, identity: IdentityType, challenge_visual: Optional[str]) -> None: lines: List[TextContent] = [] if challenge_visual: lines.append(challenge_visual) lines.append(ui.MONO) lines.extend(chunks(serialize_identity_without_proto(identity), 18)) proto = identity.proto.upper() if identity.proto else "identity" text = Text("Sign %s" % proto) text.normal(*lines) await require_confirm(ctx, text)
async def confirm_sign_identity(ctx: wire.GenericContext, proto: str, identity: str, challenge_visual: str | None) -> None: lines: list[TextContent] = [] if challenge_visual: lines.append(challenge_visual) lines.append(ui.MONO) lines.extend(chunks(identity, 18)) text = Text("Sign %s" % proto) text.normal(*lines) await raise_if_cancelled( interact(ctx, Confirm(text), "sign_identity", ButtonRequestType.Other))
async def _confirm_share_words(ctx, share_index, share_words): numbered = list(enumerate(share_words)) # check three words third = len(numbered) // 3 # if the num of words is not dividable by 3 let's add 1 # to have more words at the beggining and to check all of them if len(numbered) % 3: third += 1 for part in utils.chunks(numbered, third): if not await _confirm_word(ctx, share_index, part, len(share_words)): return False return True
async def confirm_action_sellram(ctx, msg: EosActionSellRam): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Sell RAM" fields = [] fields.append("Receiver:") fields.append(helpers.eos_name_to_string(msg.account)) fields.append("Bytes:") fields.append(str(msg.bytes)) pages = list(chunks(fields, _TWO_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
def test_send_native_invalid_address(self): coin = coins.by_name('Testnet') seed = bip39.seed(' '.join(['all'] * 12), '') inp1 = TxInputType( # 49'/1'/0'/0/0" - tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s address_n=[49 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 0, 0], amount=12300000, prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'), prev_index=0, script_type=InputScriptType.SPENDWITNESS, sequence=0xffffffff, multisig=None, ) out1 = TxOutputType( address='TB1Q694CCP5QCC0UDMFWGP692U2S2HJPQ5H407URTU', # Error: should be lower case script_type=OutputScriptType.PAYTOADDRESS, amount=12300000 - 11000 - 5000000, address_n=[], multisig=None, ) tx = SignTx(coin_name='Testnet', version=None, lock_time=None, inputs_count=1, outputs_count=1) messages = [ None, # check fee TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxAck(tx=TransactionType(inputs=[inp1])), helpers.UiConfirmForeignAddress(address_n=inp1.address_n), True, TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=EMPTY_SERIALIZED), TxAck(tx=TransactionType(outputs=[out1])), None ] ns = get_namespaces_for_coin(coin) keychain = Keychain(seed, ns) signer = bitcoin.Bitcoin(tx, keychain, coin).signer() for request, response in chunks(messages, 2): if response is None: with self.assertRaises(wire.DataError): signer.send(request) else: self.assertEqual(signer.send(request), response)
def _split_share_into_pages(share_words): share = list(enumerate(share_words)) # we need to keep track of the word indices first = share[:2] # two words on the first page length = len(share_words) if length == 12 or length == 20 or length == 24: middle = share[2:-2] last = share[-2:] # two words on the last page elif length == 33: middle = share[2:] last = [] # no words at the last page, because it does not add up else: # Invalid number of shares. SLIP-39 allows 20 or 33 words, BIP-39 12 or 24 raise RuntimeError chunks = utils.chunks(middle, 4) # 4 words on the middle pages return first, list(chunks), last
def _show_xpub(xpub: str, title: str, cancel: str) -> Paginated: pages: list[ui.Component] = [] for lines in chunks(list(chunks_intersperse(xpub, 16)), TEXT_MAX_LINES * 2): text = Text(title, ui.ICON_RECEIVE, ui.GREEN, new_lines=False) 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 confirm_action_unknown(ctx, action, checksum): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Arbitrary data" fields = [] fields.append("Contract:") fields.append(helpers.eos_name_to_string(action.account)) fields.append("Action Name:") fields.append(helpers.eos_name_to_string(action.name)) fields.append("Checksum: ") fields += split_data(hexlify(checksum).decode("ascii")) pages = list(chunks(fields, _FIVE_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
async def confirm_action_newaccount(ctx, msg: EosActionNewAccount): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "New Account" fields = [] fields.append("Creator:") fields.append(helpers.eos_name_to_string(msg.creator)) fields.append("Name:") fields.append(helpers.eos_name_to_string(msg.name)) fields += authorization_fields(msg.owner) fields += authorization_fields(msg.active) pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
async def confirm_action_unlinkauth(ctx, msg: EosActionUnlinkAuth): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Unlink Auth" fields = [] fields.append("Account:") fields.append(helpers.eos_name_to_string(msg.account)) fields.append("Code:") fields.append(helpers.eos_name_to_string(msg.code)) fields.append("Type:") fields.append(helpers.eos_name_to_string(msg.type)) pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
async def confirm_action_buyrambytes(ctx, msg: EosActionBuyRamBytes): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Buy RAM" fields = [] fields.append("Payer:") fields.append(helpers.eos_name_to_string(msg.payer)) fields.append("Receiver:") fields.append(helpers.eos_name_to_string(msg.receiver)) fields.append("Bytes:") fields.append(str(msg.bytes)) pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
async def show_warning_tx_staking_key_hash( ctx: wire.Context, staking_key_hash: bytes, amount: int, ): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Change address staking") t1.normal("rights do not match") t1.normal("the current account.") t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("Staking key hash:") t2.mono(*chunks(hexlify(staking_key_hash), 17)) t3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t3.normal("Change amount:") t3.bold(format_coin_amount(amount)) await require_confirm(ctx, Paginated([t1, t2, t3]))
async def confirm_action_updateauth(ctx, msg: EosActionUpdateAuth): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Update Auth" fields = [] fields.append("Account:") fields.append(helpers.eos_name_to_string(msg.account)) fields.append("Permission:") fields.append(helpers.eos_name_to_string(msg.permission)) fields.append("Parent:") fields.append(helpers.eos_name_to_string(msg.parent)) fields += authorization_fields(msg.auth) pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
async def confirm_action_undelegate(ctx, msg: EosActionUndelegate): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Undelegate" fields = [] fields.append("Sender:") fields.append(helpers.eos_name_to_string(msg.sender)) fields.append("Receiver:") fields.append(helpers.eos_name_to_string(msg.receiver)) fields.append("CPU:") fields.append(helpers.eos_asset_to_string(msg.cpu_quantity)) fields.append("NET:") fields.append(helpers.eos_asset_to_string(msg.net_quantity)) pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)
async def _confirm_share_words( ctx: wire.GenericContext, share_index: int | None, share_words: Sequence[str], group_index: int | None = None, ) -> bool: # divide list into thirds, rounding up, so that chunking by `third` always yields # three parts (the last one might be shorter) third = (len(share_words) + 2) // 3 offset = 0 count = len(share_words) for part in utils.chunks(share_words, third): if not await confirm_word(ctx, share_index, part, offset, count, group_index): return False offset += len(part) return True
def _show_address( address: str, title: str, network: str | None = None, extra: str | None = None, ) -> ui.Layout: para = [(ui.NORMAL, f"{network} network")] if network is not None else [] if extra is not None: para.append((ui.BOLD, extra)) para.extend((ui.MONO, address_line) for address_line in chunks(address, MONO_ADDR_PER_LINE)) return paginate_paragraphs( para, header=title, header_icon=ui.ICON_RECEIVE, icon_color=ui.GREEN, confirm=lambda content: Confirm( content, cancel="QR", cancel_style=ButtonDefault), )
def _split_share_into_pages( share_words: Sequence[str], ) -> tuple[NumberedWords, list[NumberedWords], NumberedWords]: share = list( enumerate(share_words)) # we need to keep track of the word indices first = share[:2] # two words on the first page length = len(share_words) if length in (12, 20, 24): middle = share[2:-2] last = share[-2:] # two words on the last page elif length in (18, 33): middle = share[2:] last = [] # no words at the last page, because it does not add up else: # Invalid number of shares. SLIP-39 allows 20 or 33 words, BIP-39 12 or 24 raise RuntimeError chunks = utils.chunks(middle, 4) # 4 words on the middle pages return first, list(chunks), last
async def _bip39_show_mnemonic(ctx, words: list): # split mnemonic words into pages PER_PAGE = const(4) words = list(enumerate(words)) words = list(utils.chunks(words, PER_PAGE)) # display the pages, with a confirmation dialog on the last one pages = [_get_mnemonic_page(page) for page in words] paginated = Paginated(pages) if __debug__: def export_displayed_words(): # export currently displayed mnemonic words into debuglink debug.reset_current_words = [w for _, w in words[paginated.page]] paginated.on_change = export_displayed_words export_displayed_words() await hold_to_confirm(ctx, paginated, ButtonRequestType.ResetDevice)
async def confirm_sending( ctx: wire.Context, ada_amount: int, token_bundle: List[CardanoAssetGroupType], to: str, ) -> None: await confirm_sending_token_bundle(ctx, token_bundle) page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm sending:") page1.bold(format_coin_amount(ada_amount)) page1.normal("to") to_lines = list(chunks(to, 17)) page1.bold(to_lines[0]) comp: ui.Component = page1 # otherwise `[page1]` is of the wrong type pages = [comp] + _paginate_lines(to_lines, 1, "Confirm transaction", ui.ICON_SEND) await require_confirm(ctx, Paginated(pages))
async def confirm_sending( ctx: wire.Context, ada_amount: int, token_bundle: List[CardanoAssetGroupType], to: str, ) -> None: for token_group in token_bundle: await confirm_sending_token_group(ctx, token_group) page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm sending:") page1.bold(format_coin_amount(ada_amount)) page1.normal("to") to_lines = list(chunks(to, 17)) page1.bold(to_lines[0]) pages = [page1] + _paginate_lines(to_lines, 1, "Confirm transaction", ui.ICON_SEND) await require_confirm(ctx, Paginated(pages))
async def confirm_action_transfer(ctx, msg: EosActionTransfer, account: str): await ctx.call(ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck) text = "Transfer" fields = [] fields.append("From:") fields.append(helpers.eos_name_to_string(msg.sender)) fields.append("To:") fields.append(helpers.eos_name_to_string(msg.receiver)) fields.append("Amount:") fields.append(helpers.eos_asset_to_string(msg.quantity)) fields.append("Contract:") fields.append(account) if msg.memo is not None: fields.append("Memo:") fields += split_data(msg.memo[:512]) pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE)) paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text) await ctx.wait(paginator)