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:
parent
bf96c43d32
commit
58ffe95369
1
core/.changelog.d/3047.added
Normal file
1
core/.changelog.d/3047.added
Normal file
@ -0,0 +1 @@
|
||||
QR code display when exporting XPUBs.
|
@ -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;
|
||||
|
@ -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(
|
||||
/// *,
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(
|
||||
/// *,
|
||||
|
@ -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."""
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user