1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 07:28:10 +00:00

fix(core): display XPUBs in a similar way to addresses

[no changelog]
This commit is contained in:
Martin Milata 2023-07-26 16:45:10 +02:00
parent bf96c43d32
commit 58ffe95369
20 changed files with 821 additions and 648 deletions

View File

@ -0,0 +1 @@
QR code display when exporting XPUBs.

View File

@ -60,6 +60,7 @@ static void _librust_qstrs(void) {
MP_QSTR_data;
MP_QSTR_decode;
MP_QSTR_description;
MP_QSTR_details_title;
MP_QSTR_disable_animation;
MP_QSTR_draw_welcome_screen;
MP_QSTR_dry_run;
@ -96,6 +97,7 @@ static void _librust_qstrs(void) {
MP_QSTR_path;
MP_QSTR_progress_event;
MP_QSTR_prompt;
MP_QSTR_qr_title;
MP_QSTR_recipient;
MP_QSTR_request_bip39;
MP_QSTR_request_complete_repaint;

View File

@ -1090,15 +1090,16 @@ extern "C" fn new_show_passphrase() -> Obj {
unsafe { util::try_or_raise(block) }
}
extern "C" fn new_show_mismatch() -> Obj {
let block = move || {
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let get_page = move |page_index| {
assert!(page_index == 0);
let btn_layout = ButtonLayout::arrow_none_text("QUIT".into());
let btn_actions = ButtonActions::cancel_none_confirm();
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
.text_bold("ADDRESS MISMATCH?".into())
.text_bold(title.clone())
.newline()
.newline_half()
.text_normal("Please contact Trezor support at".into())
@ -1112,7 +1113,7 @@ extern "C" fn new_show_mismatch() -> Obj {
let obj = LayoutObj::new(Flow::new(pages))?;
Ok(obj.into())
};
unsafe { util::try_or_raise(block) }
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
@ -1737,9 +1738,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Show passphrase on host dialog."""
Qstr::MP_QSTR_show_passphrase => obj_fn_0!(new_show_passphrase).as_obj(),
/// def show_mismatch() -> object:
/// def show_mismatch(*, title: str) -> object:
/// """Warning modal, receiving address mismatch."""
Qstr::MP_QSTR_show_mismatch => obj_fn_0!(new_show_mismatch).as_obj(),
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
/// def confirm_with_info(
/// *,

View File

@ -30,8 +30,10 @@ where
T: StringType,
{
pub fn new(
qr_title: T,
qr_address: T,
case_sensitive: bool,
details_title: T,
account: Option<T>,
path: Option<T>,
) -> Result<Self, Error>
@ -53,14 +55,14 @@ where
let result = Self {
qr_code: Frame::left_aligned(
theme::label_title(),
"RECEIVE ADDRESS".into(),
qr_title,
Qr::new(qr_address, case_sensitive)?.with_border(7),
)
.with_cancel_button()
.with_border(theme::borders_horizontal_scroll()),
details: Frame::left_aligned(
theme::label_title(),
"RECEIVING TO".into(),
details_title,
para.into_paragraphs(),
)
.with_cancel_button()

View File

@ -119,15 +119,17 @@ where
}
}
pub fn with_text(mut self, style: &'static TextStyle, text: T) -> Self {
if !text.as_ref().is_empty() {
self.paragraphs
.inner_mut()
.add(Paragraph::new(style, text).centered());
pub fn with_paragraph(mut self, para: Paragraph<T>) -> Self {
if !para.content().as_ref().is_empty() {
self.paragraphs.inner_mut().add(para);
}
self
}
pub fn with_text(self, style: &'static TextStyle, text: T) -> Self {
self.with_paragraph(Paragraph::new(style, text).centered())
}
pub fn with_description(self, description: T) -> Self {
self.with_text(&theme::TEXT_NORMAL_OFF_WHITE, description)
}

View File

@ -730,6 +730,8 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
extern "C" fn new_show_address_details(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let qr_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_qr_title)?.try_into()?;
let details_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_details_title)?.try_into()?;
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
let case_sensitive: bool = kwargs.get(Qstr::MP_QSTR_case_sensitive)?.try_into()?;
let account: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
@ -737,7 +739,14 @@ extern "C" fn new_show_address_details(n_args: usize, args: *const Obj, kwargs:
let xpubs: Obj = kwargs.get(Qstr::MP_QSTR_xpubs)?;
let mut ad = AddressDetails::new(address, case_sensitive, account, path)?;
let mut ad = AddressDetails::new(
qr_title,
address,
case_sensitive,
details_title,
account,
path,
)?;
for i in IterBuf::new().try_iterate(xpubs)? {
let [xtitle, text]: [StrBuffer; 2] = iter_into_array(i)?;
@ -1051,9 +1060,9 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_mismatch() -> Obj {
let block = move || {
let title: StrBuffer = "Address mismatch?".into();
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: StrBuffer = "Please contact Trezor support at".into();
let url: StrBuffer = "trezor.io/support".into();
let button = "QUIT";
@ -1075,13 +1084,20 @@ extern "C" fn new_show_mismatch() -> Obj {
true,
),
)
.with_description(description)
.with_paragraph(
Paragraph::new(&theme::TEXT_NORMAL, description)
.centered()
.with_bottom_padding(
theme::TEXT_NORMAL.text_font.text_height()
- theme::TEXT_DEMIBOLD.text_font.text_height(),
),
)
.with_text(&theme::TEXT_DEMIBOLD, url),
)?;
Ok(obj.into())
};
unsafe { util::try_or_raise(block) }
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
@ -1680,8 +1696,10 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_address_details(
/// *,
/// qr_title: str,
/// address: str,
/// case_sensitive: bool,
/// details_title: str,
/// account: str | None,
/// path: str | None,
/// xpubs: list[tuple[str, str]],
@ -1799,9 +1817,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Info modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(),
/// def show_mismatch() -> object:
/// def show_mismatch(*, title: str) -> object:
/// """Warning modal, receiving address mismatch."""
Qstr::MP_QSTR_show_mismatch => obj_fn_0!(new_show_mismatch).as_obj(),
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
/// def show_simple(
/// *,

View File

@ -226,7 +226,7 @@ def show_passphrase() -> object:
# rust/src/ui/model_tr/layout.rs
def show_mismatch() -> object:
def show_mismatch(*, title: str) -> object:
"""Warning modal, receiving address mismatch."""
@ -524,8 +524,10 @@ def confirm_reset_device(
# rust/src/ui/model_tt/layout.rs
def show_address_details(
*,
qr_title: str,
address: str,
case_sensitive: bool,
details_title: str,
account: str | None,
path: str | None,
xpubs: list[tuple[str, str]],
@ -653,7 +655,7 @@ def show_info(
# rust/src/ui/model_tt/layout.rs
def show_mismatch() -> object:
def show_mismatch(*, title: str) -> object:
"""Warning modal, receiving address mismatch."""

View File

@ -22,8 +22,9 @@ async def get_public_key(
await paths.validate_path(keychain, msg.address_n)
node = keychain.derive(msg.address_n)
pubkey = node.public_key()
path = paths.address_n_to_str(msg.address_n)
if msg.show_display:
await show_pubkey(hexlify(pubkey).decode())
await show_pubkey(hexlify(pubkey).decode(), path=path)
return BinancePublicKey(public_key=pubkey)

View File

@ -79,9 +79,31 @@ async def get_public_key(
)
if msg.show_display:
from trezor.ui.layouts import show_xpub
from trezor.ui.layouts import confirm_path_warning, show_pubkey
await show_xpub(node_xpub, "XPUB")
from apps.common.paths import address_n_to_str
from .keychain import address_n_to_name
path = address_n_to_str(address_n)
account_name = address_n_to_name(
coin, address_n, script_type, account_level=True
)
if account_name is None:
account = None
await confirm_path_warning(path)
elif account_name == "":
account = coin.coin_shortcut
else:
account = f"{coin.coin_shortcut} {account_name}"
await show_pubkey(
node_xpub,
"XPUB",
account=account,
path=path,
mismatch_title="XPUB mismatch?",
br_type="show_xpub",
)
return PublicKey(
node=node_type,

View File

@ -7,7 +7,7 @@ from trezor.messages import AuthorizeCoinJoin, SignMessage
from apps.common.paths import PATTERN_BIP44, PATTERN_CASA, PathSchema, unharden
from . import authorization
from .common import BITCOIN_NAMES
from .common import BIP32_WALLET_DEPTH, BITCOIN_NAMES
if TYPE_CHECKING:
from typing import Awaitable, Callable, Iterable, TypeVar
@ -341,6 +341,7 @@ class AccountType:
require_segwit: bool,
require_bech32: bool,
require_taproot: bool,
account_level: bool = False,
):
self.account_name = account_name
self.pattern = pattern
@ -348,6 +349,7 @@ class AccountType:
self.require_segwit = require_segwit
self.require_bech32 = require_bech32
self.require_taproot = require_taproot
self.account_level = account_level
def get_name(
self,
@ -355,9 +357,15 @@ class AccountType:
address_n: Bip32Path,
script_type: InputScriptType | None,
) -> str | None:
pattern = self.pattern
if self.account_level:
# Discard the last two parts of the pattern. For bitcoin these generally are `change`
# and `address_index`. The result can be used to match XPUB paths.
pattern = "/".join(pattern.split("/")[:-BIP32_WALLET_DEPTH])
if (
(script_type is not None and script_type != self.script_type)
or not PathSchema.parse(self.pattern, coin.slip44).match(address_n)
or not PathSchema.parse(pattern, coin.slip44).match(address_n)
or (not coin.segwit and self.require_segwit)
or (not coin.bech32_prefix and self.require_bech32)
or (not coin.taproot and self.require_taproot)
@ -365,9 +373,9 @@ class AccountType:
return None
name = self.account_name
account_pos = self.pattern.find("/account'")
account_pos = pattern.find("/account'")
if account_pos >= 0:
i = self.pattern.count("/", 0, account_pos)
i = pattern.count("/", 0, account_pos)
account_number = unharden(address_n[i]) + 1
name += f" #{account_number}"
@ -378,6 +386,7 @@ def address_n_to_name(
coin: coininfo.CoinInfo,
address_n: Bip32Path,
script_type: InputScriptType | None = None,
account_level: bool = False,
) -> str | None:
ACCOUNT_TYPES = (
AccountType(
@ -387,6 +396,7 @@ def address_n_to_name(
require_segwit=True,
require_bech32=False,
require_taproot=False,
account_level=account_level,
),
AccountType(
"",
@ -395,6 +405,7 @@ def address_n_to_name(
require_segwit=False,
require_bech32=False,
require_taproot=False,
account_level=account_level,
),
AccountType(
"L. SegWit",
@ -403,6 +414,7 @@ def address_n_to_name(
require_segwit=True,
require_bech32=False,
require_taproot=False,
account_level=account_level,
),
AccountType(
"SegWit",
@ -411,6 +423,7 @@ def address_n_to_name(
require_segwit=True,
require_bech32=True,
require_taproot=False,
account_level=account_level,
),
AccountType(
"Taproot",
@ -419,6 +432,7 @@ def address_n_to_name(
require_segwit=False,
require_bech32=True,
require_taproot=True,
account_level=account_level,
),
AccountType(
"Coinjoin",
@ -427,6 +441,7 @@ def address_n_to_name(
require_segwit=False,
require_bech32=True,
require_taproot=True,
account_level=account_level,
),
)

View File

@ -24,7 +24,8 @@ async def get_public_key(msg: EosGetPublicKey, keychain: Keychain) -> EosPublicK
public_key = secp256k1.publickey(node.private_key(), True)
wif = public_key_to_wif(public_key)
path = paths.address_n_to_str(msg.address_n)
if msg.show_display:
await require_get_public_key(wif)
await require_get_public_key(wif, path)
return EosPublicKey(wif_public_key=wif, raw_public_key=public_key)

View File

@ -1,7 +1,7 @@
async def require_get_public_key(public_key: str) -> None:
async def require_get_public_key(public_key: str, path: str) -> None:
from trezor.ui.layouts import show_pubkey
await show_pubkey(public_key)
await show_pubkey(public_key, path=path)
async def require_sign_tx(num_actions: int) -> None:

View File

@ -452,31 +452,10 @@ async def confirm_homescreen(
)
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
return RustLayout(
trezorui2.confirm_blob(
title=title.upper(),
data=xpub,
verb_cancel=cancel,
description=None,
extra=None,
)
)
async def show_xpub(xpub: str, title: str) -> None:
await raise_if_not_confirmed(
interact(
_show_xpub(xpub, title, None),
"show_xpub",
ButtonRequestType.PublicKey,
)
)
async def show_address(
address: str,
*,
title: str | None = None,
address_qr: str | None = None,
case_sensitive: bool = True,
path: str | None = None,
@ -484,14 +463,18 @@ async def show_address(
network: str | None = None,
multisig_index: int | None = None,
xpubs: Sequence[str] = (),
mismatch_title: str = "ADDRESS MISMATCH?",
br_type: str = "show_address",
br_code: ButtonRequestType = ButtonRequestType.Address,
) -> None:
send_button_request = True
# Will be a marquee in case of multisig
title = (
"RECEIVE ADDRESS (MULTISIG)"
if multisig_index is not None
else "RECEIVE ADDRESS"
)
if title is None:
# Will be a marquee in case of multisig
title = (
"RECEIVE ADDRESS (MULTISIG)"
if multisig_index is not None
else "RECEIVE ADDRESS"
)
while True:
layout = RustLayout(
trezorui2.confirm_address(
@ -504,8 +487,8 @@ async def show_address(
if send_button_request:
send_button_request = False
await button_request(
"show_address",
ButtonRequestType.Address,
br_type,
br_code,
pages=layout.page_count(),
)
result = await ctx_wait(layout)
@ -526,8 +509,10 @@ async def show_address(
result = await ctx_wait(
RustLayout(
trezorui2.show_address_details(
qr_title="", # unused on this model
address=address if address_qr is None else address_qr,
case_sensitive=case_sensitive,
details_title="", # unused on this model
account=account,
path=path,
xpubs=[(xpub_title(i), xpub) for i, xpub in enumerate(xpubs)],
@ -539,19 +524,32 @@ async def show_address(
# User pressed left cancel button, show mismatch dialogue.
else:
result = await ctx_wait(RustLayout(trezorui2.show_mismatch()))
result = await ctx_wait(
RustLayout(trezorui2.show_mismatch(title=mismatch_title.upper()))
)
assert result in (CONFIRMED, CANCELLED)
# Right button aborts action, left goes back to showing address.
if result is CONFIRMED:
raise ActionCancelled
def show_pubkey(pubkey: str, title: str = "Confirm public key") -> Awaitable[None]:
return confirm_blob(
"show_pubkey",
title.upper(),
pubkey,
def show_pubkey(
pubkey: str,
title: str = "Public key",
*,
account: str | None = None,
path: str | None = None,
mismatch_title: str = "KEY MISMATCH?",
br_type="show_pubkey",
) -> Awaitable[None]:
return show_address(
address=pubkey,
title=title.upper(),
account=account,
path=path,
br_type=br_type,
br_code=ButtonRequestType.PublicKey,
mismatch_title=mismatch_title,
)

View File

@ -391,28 +391,10 @@ async def confirm_homescreen(
)
async def show_xpub(xpub: str, title: str) -> None:
await raise_if_not_confirmed(
interact(
RustLayout(
trezorui2.confirm_blob(
title=title,
data=xpub,
verb="CONFIRM",
verb_cancel=None,
extra=None,
description=None,
)
),
"show_xpub",
ButtonRequestType.PublicKey,
)
)
async def show_address(
address: str,
*,
title: str | None = None,
address_qr: str | None = None,
case_sensitive: bool = True,
path: str | None = None,
@ -420,13 +402,20 @@ async def show_address(
network: str | None = None,
multisig_index: int | None = None,
xpubs: Sequence[str] = (),
mismatch_title: str = "Address mismatch?",
br_type: str = "show_address",
br_code: ButtonRequestType = ButtonRequestType.Address,
) -> None:
send_button_request = True
title = (
"RECEIVE ADDRESS\n(MULTISIG)"
if multisig_index is not None
else "RECEIVE ADDRESS"
)
if title is None:
title = (
"RECEIVE ADDRESS\n(MULTISIG)"
if multisig_index is not None
else "RECEIVE ADDRESS"
)
details_title = "RECEIVING TO"
else:
details_title = title
while True:
layout = RustLayout(
trezorui2.confirm_address(
@ -439,8 +428,8 @@ async def show_address(
if send_button_request:
send_button_request = False
await button_request(
"show_address",
ButtonRequestType.Address,
br_type,
br_code,
pages=layout.page_count(),
)
result = await ctx_wait(layout)
@ -460,8 +449,10 @@ async def show_address(
result = await ctx_wait(
RustLayout(
trezorui2.show_address_details(
qr_title=title,
address=address if address_qr is None else address_qr,
case_sensitive=case_sensitive,
details_title=details_title,
account=account,
path=path,
xpubs=[(xpub_title(i), xpub) for i, xpub in enumerate(xpubs)],
@ -471,19 +462,32 @@ async def show_address(
assert result is CANCELLED
else:
result = await ctx_wait(RustLayout(trezorui2.show_mismatch()))
result = await ctx_wait(
RustLayout(trezorui2.show_mismatch(title=mismatch_title))
)
assert result in (CONFIRMED, CANCELLED)
# Right button aborts action, left goes back to showing address.
if result is CONFIRMED:
raise ActionCancelled
def show_pubkey(pubkey: str, title: str = "Confirm public key") -> Awaitable[None]:
return confirm_blob(
"show_pubkey",
title,
pubkey,
def show_pubkey(
pubkey: str,
title: str = "Public key",
*,
account: str | None = None,
path: str | None = None,
mismatch_title: str = "Key mismatch?",
br_type="show_pubkey",
) -> Awaitable[None]:
return show_address(
address=pubkey,
title=title.upper(),
account=account,
path=path,
br_type=br_type,
br_code=ButtonRequestType.PublicKey,
mismatch_title=mismatch_title,
)

View File

@ -20,6 +20,8 @@ from trezorlib import binance
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
from ...input_flows import InputFlowShowXpubQRCode
BINANCE_PATH = parse_path("m/44h/714h/0h/0/0")
@ -30,8 +32,11 @@ BINANCE_PATH = parse_path("m/44h/714h/0h/0/0")
mnemonic="offer caution gift cross surge pretty orange during eye soldier popular holiday mention east eight office fashion ill parrot vault rent devote earth cousin"
)
def test_binance_get_public_key(client: Client):
sig = binance.get_public_key(client, BINANCE_PATH, show_display=True)
assert (
sig.hex()
== "029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e"
)
with client:
IF = InputFlowShowXpubQRCode(client)
client.set_input_flow(IF.get())
sig = binance.get_public_key(client, BINANCE_PATH, show_display=True)
assert (
sig.hex()
== "029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e"
)

View File

@ -20,6 +20,8 @@ from trezorlib import messages
from trezorlib.cli import btc
from trezorlib.debuglink import TrezorClientDebugLink as Client
from ...input_flows import InputFlowShowXpubQRCode
VECTORS_DESCRIPTORS = ( # coin, account, script_type, descriptors
(
"Bitcoin",
@ -154,7 +156,10 @@ VECTORS_DESCRIPTORS = ( # coin, account, script_type, descriptors
"coin, account, purpose, script_type, descriptors", VECTORS_DESCRIPTORS
)
def test_descriptors(client: Client, coin, account, purpose, script_type, descriptors):
res = btc._get_descriptor(
client, coin, account, purpose, script_type, show_display=True
)
assert res == descriptors
with client:
IF = InputFlowShowXpubQRCode(client)
client.set_input_flow(IF.get())
res = btc._get_descriptor(
client, coin, account, purpose, script_type, show_display=True
)
assert res == descriptors

View File

@ -22,6 +22,7 @@ from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
from ... import bip32
from ...input_flows import InputFlowShowXpubQRCode
VECTORS_BITCOIN = ( # coin_name, xpub_magic, path, xpub
(
@ -115,6 +116,17 @@ def test_get_public_node(client: Client, coin_name, xpub_magic, path, xpub):
assert bip32.serialize(res.node, xpub_magic) == xpub
@pytest.mark.skip_t1
@pytest.mark.parametrize("coin_name, xpub_magic, path, xpub", VECTORS_BITCOIN)
def test_get_public_node_show(client: Client, coin_name, xpub_magic, path, xpub):
with client:
IF = InputFlowShowXpubQRCode(client)
client.set_input_flow(IF.get())
res = btc.get_public_node(client, path, coin_name=coin_name, show_display=True)
assert res.xpub == xpub
assert bip32.serialize(res.node, xpub_magic) == xpub
@pytest.mark.xfail(reason="Currently path validation on get_public_node is disabled.")
@pytest.mark.parametrize("coin_name, path", VECTORS_INVALID)
def test_invalid_path(client: Client, coin_name, path):

View File

@ -21,6 +21,7 @@ from trezorlib.eos import get_public_key
from trezorlib.tools import parse_path
from ...common import MNEMONIC12
from ...input_flows import InputFlowShowXpubQRCode
@pytest.mark.altcoin
@ -28,32 +29,35 @@ from ...common import MNEMONIC12
@pytest.mark.skip_t1
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
def test_eos_get_public_key(client: Client):
public_key = get_public_key(
client, parse_path("m/44h/194h/0h/0/0"), show_display=True
)
assert (
public_key.wif_public_key
== "EOS4u6Sfnzj4Sh2pEQnkXyZQJqH3PkKjGByDCbsqqmyq6PttM9KyB"
)
assert (
public_key.raw_public_key.hex()
== "02015fabe197c955036bab25f4e7c16558f9f672f9f625314ab1ec8f64f7b1198e"
)
public_key = get_public_key(client, parse_path("m/44h/194h/0h/0/1"))
assert (
public_key.wif_public_key
== "EOS5d1VP15RKxT4dSakWu2TFuEgnmaGC2ckfSvQwND7pZC1tXkfLP"
)
assert (
public_key.raw_public_key.hex()
== "02608bc2c431521dee0b9d5f2fe34053e15fc3b20d2895e0abda857b9ed8e77a78"
)
public_key = get_public_key(client, parse_path("m/44h/194h/1h/0/0"))
assert (
public_key.wif_public_key
== "EOS7UuNeTf13nfcG85rDB7AHGugZi4C4wJ4ft12QRotqNfxdV2NvP"
)
assert (
public_key.raw_public_key.hex()
== "035588a197bd5a7356e8a702361b2d535c6372f843874bed6617cd1afe1dfcb502"
)
with client:
IF = InputFlowShowXpubQRCode(client)
client.set_input_flow(IF.get())
public_key = get_public_key(
client, parse_path("m/44h/194h/0h/0/0"), show_display=True
)
assert (
public_key.wif_public_key
== "EOS4u6Sfnzj4Sh2pEQnkXyZQJqH3PkKjGByDCbsqqmyq6PttM9KyB"
)
assert (
public_key.raw_public_key.hex()
== "02015fabe197c955036bab25f4e7c16558f9f672f9f625314ab1ec8f64f7b1198e"
)
public_key = get_public_key(client, parse_path("m/44h/194h/0h/0/1"))
assert (
public_key.wif_public_key
== "EOS5d1VP15RKxT4dSakWu2TFuEgnmaGC2ckfSvQwND7pZC1tXkfLP"
)
assert (
public_key.raw_public_key.hex()
== "02608bc2c431521dee0b9d5f2fe34053e15fc3b20d2895e0abda857b9ed8e77a78"
)
public_key = get_public_key(client, parse_path("m/44h/194h/1h/0/0"))
assert (
public_key.wif_public_key
== "EOS7UuNeTf13nfcG85rDB7AHGugZi4C4wJ4ft12QRotqNfxdV2NvP"
)
assert (
public_key.raw_public_key.hex()
== "035588a197bd5a7356e8a702361b2d535c6372f843874bed6617cd1afe1dfcb502"
)

View File

@ -391,6 +391,64 @@ class InputFlowShowMultisigXPUBs(InputFlowBase):
self.debug.press_middle()
class InputFlowShowXpubQRCode(InputFlowBase):
def __init__(self, client: Client):
super().__init__(client)
def input_flow_tt(self) -> GeneratorType:
br = yield
layout = self.debug.wait_layout()
if "coinjoin" in layout.title().lower():
self.debug.press_yes()
br = yield
elif br.code == B.UnknownDerivationPath:
self.debug.press_yes()
br = yield
self.debug.click(buttons.CORNER_BUTTON, wait=True)
# synchronize; TODO get rid of this once we have single-global-layout
self.debug.synchronize_at("HorizontalPage")
self.debug.swipe_left(wait=True)
self.debug.swipe_right(wait=True)
self.debug.swipe_left(wait=True)
self.debug.click(buttons.CORNER_BUTTON, wait=True)
self.debug.press_no(wait=True)
self.debug.press_no(wait=True)
for _ in range(br.pages - 1):
self.debug.swipe_up(wait=True)
self.debug.press_yes()
def input_flow_tr(self) -> BRGeneratorType:
if self.passphrase:
br = yield
self.debug.press_right()
br = yield
self.debug.press_right()
br = yield
layout = self.debug.wait_layout()
if "coinjoin" in layout.title().lower():
self.debug.press_right()
br = yield
elif br.code == B.UnknownDerivationPath:
self.debug.press_right()
br = yield
# Go into details
self.debug.press_right()
# Go through details and back
self.debug.press_right()
self.debug.press_right()
self.debug.press_left()
self.debug.press_left()
assert br.pages is not None
for _ in range(br.pages - 1):
self.debug.press_right()
# Confirm
self.debug.press_middle()
class InputFlowPaymentRequestDetails(InputFlowBase):
def __init__(self, client: Client, outputs: list[messages.TxOutputType]):
super().__init__(client)

File diff suppressed because it is too large Load Diff