diff --git a/releases/Next-ChangeLog.md b/releases/Next-ChangeLog.md index 4cb3f508..c2124501 100644 --- a/releases/Next-ChangeLog.md +++ b/releases/Next-ChangeLog.md @@ -11,7 +11,7 @@ This lists the new changes that have not yet been published in a normal release. ## 5.4.? - 2025-05- -- tbd +- Bugfix: Mk4 with both NFC & Virtual Disk OFF cannot exit `Export Wallet` menu. Stuck in export loop - needs reboot. # Q Specific Changes diff --git a/shared/export.py b/shared/export.py index cd77086b..303a4d6e 100644 --- a/shared/export.py +++ b/shared/export.py @@ -39,7 +39,7 @@ async def export_contents(title, contents, fname_pattern, derive=None, addr_fmt= # produces signed export in case of SD/Vdisk (signed with key at deriv and addr_fmt) # checks if suitable to offer QR export on Mk4 # argument contents can support function that generates content - from glob import dis, NFC + from glob import dis, NFC, VD from files import CardSlot, CardMissingError, needs_microsd from qrs import MAX_V11_CHAR_LIMIT @@ -94,6 +94,9 @@ async def export_contents(title, contents, fname_pattern, derive=None, addr_fmt= msg += "\n\n%s signature file written:\n\n%s" % (title, sig_nice) await ux_show_story(msg) + if no_qr and (NFC is None) and (VD is None) and not force_prompt: + # user has no other ways enabled, we already exported to SD - done + return def generate_public_contents(): # Generate public details about wallet. diff --git a/shared/ux.py b/shared/ux.py index c74a3254..c1e2eeb2 100644 --- a/shared/ux.py +++ b/shared/ux.py @@ -400,7 +400,7 @@ def export_prompt_builder(what_it_is, no_qr=False, no_nfc=False, key0=None, offe prompt, escape = None, KEY_CANCEL+"x" - if (NFC or VD) or (num_sd_slots>1) or key0 or force_prompt or offer_kt: + if (NFC or VD) or (num_sd_slots>1) or key0 or force_prompt or offer_kt or txid or (not no_qr): # no need to spam with another prompt, only option is SD card prompt = "Press (1) to save %s to SD Card" % what_it_is diff --git a/testing/conftest.py b/testing/conftest.py index 4b10ae21..e0b70da7 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -1666,11 +1666,17 @@ def enable_nfc(needs_nfc, sim_exec, settings_set): return doit @pytest.fixture() -def nfc_disabled(needs_nfc, settings_get): +def nfc_disabled(settings_get): def doit(): return not bool(settings_get('nfc', 0)) return doit +@pytest.fixture() +def vdisk_disabled(settings_get): + def doit(): + return not bool(settings_get('vidsk', 0)) + return doit + @pytest.fixture() def scan_a_qr(sim_exec, is_q1): # simulate a QR being scanned @@ -2477,16 +2483,18 @@ def txout_explorer(cap_story, press_cancel, need_keypress, is_q1): @pytest.fixture -def skip_if_useless_way(is_q1, nfc_disabled): +def skip_if_useless_way(is_q1, nfc_disabled, vdisk_disabled): # when NFC is disabled, no point trying to do a PSBT via NFC # - important: run_sim_tests.py will enable NFC for complete testing # - similarly: the Mk4 and earlier had no QR scanner, so cannot use that as input def doit(way): if way == "qr" and not is_q1: raise pytest.skip("mk4 QR not supported") - if way == 'nfc' and nfc_disabled(): + elif way == 'nfc' and nfc_disabled(): # runner will test these cases, but fail faster otherwise raise pytest.skip("NFC disabled") + elif way == "vdisk" and vdisk_disabled(): + raise pytest.skip("VirtualDisk disabled") return doit diff --git a/testing/test_addr.py b/testing/test_addr.py index 36a58daf..3085060b 100644 --- a/testing/test_addr.py +++ b/testing/test_addr.py @@ -81,7 +81,10 @@ def test_addr_vs_bitcoind(use_regtest, press_select, dev, bitcoind_d_sim_sign): ("m/0/0/0/0/0/0/0/0/0/0/0/0/0\np2pkh", "too deep"), ("m/0/0/0/0/0/q/0/0/0\np2pkh", "invalid characters"), ]) -def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_text, cap_story): +def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_text, cap_story, + skip_if_useless_way): + skip_if_useless_way("nfc") + body, err = body_err goto_home() pick_menu_item('Advanced/Tools') @@ -96,7 +99,9 @@ def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_te @pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh"]) def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item, goto_home, cap_story, press_nfc, addr_vs_path, press_select, is_q1, - cap_screen): + cap_screen, skip_if_useless_way): + + skip_if_useless_way("nfc") # import pdb;pdb.set_trace() for _ in range(5): # need to wait for ApproveMessageSign to be popped from ux stack diff --git a/testing/test_export.py b/testing/test_export.py index 3495a95d..cfa0cef0 100644 --- a/testing/test_export.py +++ b/testing/test_export.py @@ -525,7 +525,7 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, press_select, mi def test_export_xpub(chain, acct_num, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, enter_number, cap_screen_qr, settings_set, nfc_read_text, is_q1, press_select, press_cancel, - press_nfc, expect_acctnum_captured): + press_nfc, expect_acctnum_captured, nfc_disabled): # XPUB's via QR settings_set("chain", chain) chain_num = 0 if chain == "BTC" else 1 @@ -553,12 +553,13 @@ def test_export_xpub(chain, acct_num, dev, cap_menu, pick_menu_item, goto_home, if is_xfp: got = cap_screen_qr().decode('ascii') time.sleep(.1) - press_nfc() - time.sleep(.2) - nfc_got = nfc_read_text() - time.sleep(.2) - assert nfc_got == got == xfp2str(simulator_fixed_xfp).upper() - press_cancel() # cancel animation + if not nfc_disabled(): + press_nfc() + time.sleep(.2) + nfc_got = nfc_read_text() + time.sleep(.2) + assert nfc_got == got == xfp2str(simulator_fixed_xfp).upper() + press_cancel() # cancel animation press_cancel() # cancel QR continue @@ -590,9 +591,10 @@ def test_export_xpub(chain, acct_num, dev, cap_menu, pick_menu_item, goto_home, got_nfc_pub = nfc_read_text() time.sleep(0.1) press_cancel() # cancel animation - press_cancel() # cancel QR assert got_nfc_pub == got_pub + press_cancel() # cancel QR + time.sleep(.1) _, story = cap_story() assert got_pub[0] in 'xt' @@ -641,7 +643,9 @@ def test_export_xpub(chain, acct_num, dev, cap_menu, pick_menu_item, goto_home, def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, settings_set, need_keypress, expect_acctnum_captured, OK, pick_menu_item, way, cap_story, cap_menu, int_ext, settings_get, - virtdisk_path, load_export, press_select): + virtdisk_path, load_export, press_select, skip_if_useless_way): + + skip_if_useless_way(way) settings_set('chain', chain) chain_num = 1 if chain in ["XTN", "XRT"] else 0