diff --git a/shared/auth.py b/shared/auth.py index 5f89ad04..718e195d 100644 --- a/shared/auth.py +++ b/shared/auth.py @@ -1538,7 +1538,7 @@ class ShowAddressBase(UserAuthorizedAction): hint_icons=KEY_QR+(KEY_NFC if NFC else '')) if ch in '4'+KEY_QR: - await show_qr_code(self.address, (self.addr_fmt & AFC_BECH32)) + await show_qr_code(self.address, (self.addr_fmt & AFC_BECH32), is_addrs=True) continue if NFC and (ch in '3'+KEY_NFC): diff --git a/shared/lcd_display.py b/shared/lcd_display.py index ce1882cc..7920a85b 100644 --- a/shared/lcd_display.py +++ b/shared/lcd_display.py @@ -667,9 +667,13 @@ class Display: # With fancy display, no address, even classic can fit in single line, # so always split nicely in middle and at mod4 hh = len(msg) // 2 - hh = (hh + 3) & ~0x3 - parts = [msg[0:hh], msg[hh:]] - num_lines = 2 + if hh <= 20: + hh = (hh + 3) & ~0x3 + parts = [msg[0:hh], msg[hh:]] + num_lines = 2 + else: + # p2wsh address would need 3 lines to show, so we won't + num_lines = 0 elif msg: if len(msg) <= CHARS_W: parts = [msg] diff --git a/shared/ownership.py b/shared/ownership.py index 4b3700ab..cf17abc3 100644 --- a/shared/ownership.py +++ b/shared/ownership.py @@ -349,7 +349,7 @@ class OwnershipCache: await show_qr_code( addr, is_alnum=(wallet.addr_fmt & (AFC_BECH32 | AFC_BECH32M)), - msg=addr + msg=addr, is_addrs=True ) elif not is_complex and (ch == "0"): # only singlesig from auth import sign_with_own_address diff --git a/testing/conftest.py b/testing/conftest.py index d0e97a11..1a6bcdb8 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -595,6 +595,42 @@ def cap_screen_qr(cap_image): return doit +@pytest.fixture +def verify_qr_address(cap_screen_qr, cap_screen, is_q1): + # check we can read QR and that it has exact value expected + # plus text version of address, if any, is right. + from ckcc_protocol.constants import AFC_BECH32 + + def doit(addr_fmt, expect_addr=None): + qr = cap_screen_qr().decode('ascii') + + if addr_fmt & AFC_BECH32: + qr = qr.lower() + + # check text --if any-- matches QR contents + # - remove spaces and newlines + # - ok if no text, which happens when QR is productively using screen space + # - skips first line, which on Q shows the index number sometimes + # - insists on some spaces + full = cap_screen() + if is_q1: + txt = ''.join(full.split()[1:]).replace('~', '') + else: + txt = ''.join(full.split()) + + if txt: + assert txt == qr + if is_q1: + # addr is not spaced out on Mk4, but check it was on Q + assert (qr[0:4] + ' ' + qr[4:8]) in full, 'was not spaced out' + + if expect_addr is not None: + assert qr == expect_addr + + return qr + + return doit + @pytest.fixture(scope='module') def get_pp_sofar(sim_exec): # get entry value for bip39 passphrase diff --git a/testing/test_addr.py b/testing/test_addr.py index c25a5efd..fc245c54 100644 --- a/testing/test_addr.py +++ b/testing/test_addr.py @@ -30,7 +30,7 @@ def test_show_addr_usb(dev, press_select, addr_vs_path, path, addr_fmt, is_simul @pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'", "m/0h/500h"]) @pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR ]) def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, - cap_story, cap_screen_qr, qr_quality_check, + cap_story, verify_qr_address, qr_quality_check, press_cancel, is_q1): time.sleep(0.1) @@ -55,9 +55,8 @@ def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, need_keypress(KEY_QR if is_q1 else '4') time.sleep(0.1) - qr = cap_screen_qr().decode('ascii') - assert qr == addr or qr == addr.upper() + verify_qr_address(addr_fmt, addr) @pytest.mark.bitcoind @pytest.mark.parametrize("addr_fmt", [ diff --git a/testing/test_address_explorer.py b/testing/test_address_explorer.py index 4bf29595..496af687 100644 --- a/testing/test_address_explorer.py +++ b/testing/test_address_explorer.py @@ -374,7 +374,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, @pytest.mark.parametrize('which_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR]) def test_custom_path(path_sidx, which_fmt, addr_vs_path, pick_menu_item, goto_address_explorer, need_keypress, cap_menu, parse_display_screen, validate_address, - cap_screen_qr, qr_quality_check, nfc_read_text, get_setting, + verify_qr_address, qr_quality_check, nfc_read_text, get_setting, press_select, press_cancel, is_q1, press_nfc, cap_story, generate_addresses_file, settings_set, set_addr_exp_start_idx, sign_msg_from_address): @@ -474,11 +474,7 @@ def test_custom_path(path_sidx, which_fmt, addr_vs_path, pick_menu_item, goto_ad addr_vs_path(addr, path, addr_fmt=which_fmt) need_keypress(KEY_QR if is_q1 else '4') - qr = cap_screen_qr().decode('ascii') - if which_fmt in (AF_P2WPKH, AF_P2TR): - assert qr == addr.upper() - else: - assert qr == addr + qr = verify_qr_address(which_fmt, addr) if get_setting('nfc', 0): # this is actually testing NFC export in qr code menu @@ -534,9 +530,7 @@ def test_custom_path(path_sidx, which_fmt, addr_vs_path, pick_menu_item, goto_ad qr_addr_list = [] need_keypress(KEY_QR if is_q1 else '4') for i in range(n): - qr = cap_screen_qr().decode('ascii') - if which_fmt in (AF_P2WPKH, AF_P2TR): - qr = qr.lower() + qr = verify_qr_address(which_fmt) qr_addr_list.append(qr) need_keypress(KEY_RIGHT if is_q1 else "9") time.sleep(.5) diff --git a/testing/test_ownership.py b/testing/test_ownership.py index b97f9240..74e672cb 100644 --- a/testing/test_ownership.py +++ b/testing/test_ownership.py @@ -9,6 +9,7 @@ from helpers import hash160, taptweak, addr_from_display_format from bip32 import BIP32Node from constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR from constants import simulator_fixed_xprv, simulator_fixed_tprv, addr_fmt_names +from charcodes import KEY_QR @pytest.fixture def wipe_cache(sim_exec): @@ -173,7 +174,7 @@ def test_ux(valid, testnet, method, sim_exec, wipe_cache, make_myself_wallet, use_testnet, goto_home, pick_menu_item, press_cancel, press_select, settings_set, is_q1, nfc_write, need_keypress, cap_screen, cap_story, load_shared_mod, scan_a_qr, skip_if_useless_way, - sign_msg_from_address, multisig, import_ms_wallet, clear_ms, + sign_msg_from_address, multisig, import_ms_wallet, clear_ms, verify_qr_address ): skip_if_useless_way(method) addr_fmt = AF_CLASSIC @@ -192,6 +193,7 @@ def test_ux(valid, testnet, method, M, keys, is_change=0, idx=50, addr_fmt=AF_P2WSH, testnet=int(testnet), path_mapper=lambda cosigner: [HARD(45), 0, 50] ) + addr_fmt = AF_P2WSH else: mk = BIP32Node.from_wallet_key(simulator_fixed_tprv if testnet else simulator_fixed_xprv) path = "m/44h/{ct}h/{acc}h/0/3".format(acc=0, ct=(1 if testnet else 0)) @@ -242,6 +244,12 @@ def test_ux(valid, testnet, method, assert 'Found in wallet' in story assert 'Derivation path' in story + if is_q1: + # check it can display as QR from here + need_keypress(KEY_QR) + verify_qr_address(addr_fmt, addr) + press_cancel() + if multisig: assert expect_name in story assert "Press (0) to sign message with this key" not in story