verify addr in QR better, fix some related issues

(cherry picked from commit dd66cd8811)
This commit is contained in:
Peter D. Gray 2025-02-10 17:48:59 +01:00 committed by scgbckbone
parent 58c809fc3f
commit 1bc2a95a1b
7 changed files with 59 additions and 18 deletions

View File

@ -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):

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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", [

View File

@ -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)

View File

@ -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