diff --git a/shared/actions.py b/shared/actions.py index fbc4635a..f67e448d 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -9,7 +9,7 @@ from uhashlib import sha256 from uasyncio import sleep_ms from ubinascii import hexlify as b2a_hex from utils import imported, pretty_short_delay, problem_file_line, import_prompt_builder -from utils import xfp2str, decrypt_tapsigner_backup, B2A +from utils import xfp2str, decrypt_tapsigner_backup, B2A, addr_fmt_label from ux import ux_show_story, the_ux, ux_confirm, ux_dramatic_pause, ux_aborted from ux import ux_enter_bip32_index, ux_input_text from export import make_json_wallet, make_summary_file, make_descriptor_wallet_export @@ -1095,9 +1095,12 @@ async def electrum_skeleton(*a): # 'classic' instead of 'legacy' personallly. rv = [] - rv.append(MenuItem("Legacy (P2PKH)", f=electrum_skeleton_step2, arg=(AF_CLASSIC, account_num))) - rv.append(MenuItem("P2SH-Segwit", f=electrum_skeleton_step2, arg=(AF_P2WPKH_P2SH, account_num))) - rv.append(MenuItem("Native Segwit", f=electrum_skeleton_step2, arg=(AF_P2WPKH, account_num))) + rv.append(MenuItem(addr_fmt_label(AF_CLASSIC), f=electrum_skeleton_step2, + arg=(AF_CLASSIC, account_num))) + rv.append(MenuItem(addr_fmt_label(AF_P2WPKH_P2SH), f=electrum_skeleton_step2, + arg=(AF_P2WPKH_P2SH, account_num))) + rv.append(MenuItem(addr_fmt_label(AF_P2WPKH), f=electrum_skeleton_step2, + arg=(AF_P2WPKH, account_num))) return MenuSystem(rv) @@ -1134,16 +1137,15 @@ async def ss_descriptor_skeleton(label, _, item): # 'classic' instead of 'legacy' personallly. rv = [] - rv.append(MenuItem("Legacy (P2PKH)", f=descriptor_skeleton_step2, + rv.append(MenuItem(addr_fmt_label(AF_CLASSIC), f=descriptor_skeleton_step2, arg=(AF_CLASSIC, account_num, int_ext))) - rv.append(MenuItem("P2SH-Segwit", f=descriptor_skeleton_step2, + rv.append(MenuItem(addr_fmt_label(AF_P2WPKH_P2SH), f=descriptor_skeleton_step2, arg=(AF_P2WPKH_P2SH, account_num, int_ext))) - rv.append(MenuItem("Native Segwit", f=descriptor_skeleton_step2, + rv.append(MenuItem(addr_fmt_label(AF_P2WPKH), f=descriptor_skeleton_step2, arg=(AF_P2WPKH, account_num, int_ext))) - rv.append(MenuItem("Taproot (P2TR)", f=descriptor_skeleton_step2, + rv.append(MenuItem(addr_fmt_label(AF_P2TR), f=descriptor_skeleton_step2, arg=(AF_P2TR, account_num, int_ext))) - return MenuSystem(rv) async def samourai_post_mix_descriptor_export(*a): diff --git a/shared/address_explorer.py b/shared/address_explorer.py index ee7ea8ff..84b4b865 100644 --- a/shared/address_explorer.py +++ b/shared/address_explorer.py @@ -14,6 +14,7 @@ from uhashlib import sha256 from ubinascii import hexlify as b2a_hex from glob import settings from auth import write_sig_file +from utils import addr_fmt_label def truncate_address(addr): # Truncates address to width of screen, replacing middle chars @@ -98,10 +99,10 @@ class PickAddrFmtMenu(MenuSystem): def __init__(self, path, parent): self.parent = parent items = [ - MenuItem("Classic P2PKH", f=self.done, arg=(path, AF_CLASSIC)), - MenuItem("Segwit P2WPKH", f=self.done, arg=(path, AF_P2WPKH)), - MenuItem("P2SH-P2WPKH", f=self.done, arg=(path, AF_P2WPKH_P2SH)), - MenuItem("Taproot P2TR", f=self.done, arg=(path, AF_P2TR)), + MenuItem(addr_fmt_label(AF_CLASSIC), f=self.done, arg=(path, AF_CLASSIC)), + MenuItem(addr_fmt_label(AF_P2WPKH), f=self.done, arg=(path, AF_P2WPKH)), + MenuItem(addr_fmt_label(AF_P2WPKH_P2SH), f=self.done, arg=(path, AF_P2WPKH_P2SH)), + MenuItem(addr_fmt_label(AF_P2TR), f=self.done, arg=(path, AF_P2TR)), ] super().__init__(items) if path.startswith("m/84'"): @@ -181,10 +182,15 @@ class AddressListMenu(MenuSystem): stash.blank_object(node) - items = [MenuItem(address, f=self.pick_single, arg=(path, addr_fmt)) - for i, (address, path, addr_fmt) in enumerate(choices)] + items = [] + for i, (address, path, addr_fmt) in enumerate(choices): + axi = address[-4:] # last 4 address characters + items.append(MenuItem(" "+addr_fmt_label(addr_fmt), f=self.pick_single, + arg=(path, addr_fmt, axi))) + items.append(MenuItem(address, f=self.pick_single, + arg=(path, addr_fmt, axi))) - # some other choices + # some other choices if self.account_num == 0: items.append(MenuItem("Applications", menu=ApplicationsMenu(self))) items.append(MenuItem("Account Number", f=self.change_account)) @@ -197,22 +203,27 @@ class AddressListMenu(MenuSystem): else: items.append(MenuItem("Account: %d" % self.account_num, f=self.change_account)) - self.goto_idx(settings.get('axi', 0)) # weak - self.replace_items(items) + axi = settings.get('axi', 0) + if isinstance(axi, str): + ok = self.goto_label(axi) + if not ok: + self.goto_idx(0) + else: + self.goto_idx(axi) async def change_account(self, *a): self.account_num = await ux_enter_bip32_index('Account Number:') or 0 await self.render() - async def pick_single(self, _1, menu_idx, item): - settings.put('axi', menu_idx) # update last clicked address - path, addr_fmt = item.arg + async def pick_single(self, _1, _2, item): + path, addr_fmt, axi = item.arg + settings.put('axi', axi) # update last clicked address await self.show_n_addresses(path, addr_fmt, None) - async def pick_multisig(self, _1, menu_idx, item): + async def pick_multisig(self, _1, _2, item): ms_wallet = item.arg - settings.put('axi', menu_idx) # update last clicked address + settings.put('axi', item.label) # update last clicked address await self.show_n_addresses(None, None, ms_wallet) async def make_custom(self, *a): diff --git a/shared/menu.py b/shared/menu.py index eb79cf55..82319e95 100644 --- a/shared/menu.py +++ b/shared/menu.py @@ -166,9 +166,17 @@ class MenuSystem: if not keep_position: self.cursor = 0 self.ypos = 0 + self.items = [m for m in menu_items if not getattr(m, 'predicate', None) or m.predicate()] self.count = len(self.items) + def goto_label(self, label): + for i, m in enumerate(self.items): + if m.label == label or m.label[-4:] == label: + self.goto_idx(i) + return True + return False + def show(self): # # Redraw the menu. diff --git a/shared/paper.py b/shared/paper.py index 98c4ad62..422d9a84 100644 --- a/shared/paper.py +++ b/shared/paper.py @@ -53,9 +53,9 @@ class PaperWalletMaker: self.is_taproot = False def atype(self): - if self.is_taproot: return 2, 'Taproot Address' - if self.is_segwit: return 1, 'Segwit Address' - return 0, 'Classic Address' + if self.is_taproot: return 2, 'Taproot P2TR' + if self.is_segwit: return 1, 'Segwit P2WPKH' + return 0, 'Classic P2PKPH' async def pick_template(self, *a): fn = await file_picker('Pick PDF template to use, or X for none.', @@ -71,7 +71,7 @@ class PaperWalletMaker: self.is_segwit = idx == 1 self.is_taproot = idx == 2 self.update_menu() - return self.atype()[0], ['Classic', 'Segwit/Bech32', 'Taproot'], set + return self.atype()[0], ['Classic P2PKPH', 'Segwit P2WPKH', 'Taproot P2TR'], set @staticmethod def can_do_qr(): diff --git a/shared/utils.py b/shared/utils.py index 7e5dc20e..e5196921 100644 --- a/shared/utils.py +++ b/shared/utils.py @@ -539,4 +539,12 @@ def decrypt_tapsigner_backup(backup_key, data): return decrypted.split("\n") +def addr_fmt_label(addr_fmt): + return { + AF_CLASSIC: "Classic P2PKH", + AF_P2WPKH_P2SH: "P2SH-Segwit", + AF_P2WPKH: "Segwit P2WPKH", + AF_P2TR: "Taproot P2TR" + }[addr_fmt] + # EOF diff --git a/testing/test_address_explorer.py b/testing/test_address_explorer.py index 9723e9a1..324169f9 100644 --- a/testing/test_address_explorer.py +++ b/testing/test_address_explorer.py @@ -3,6 +3,8 @@ import pytest, time, io, csv from ckcc_protocol.constants import * from pycoin.key.BIP32Node import BIP32Node +from pycoin.contrib.segwit_addr import encode as sw_encode +from pycoin.encoding import a2b_hashed_base58, hash160 @pytest.fixture @@ -154,14 +156,15 @@ def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress, cap_menu, need_keypress('4') time.sleep(.01) m = cap_menu() - + gap = iter(range(1, 10)) for idx, (path, addr_format) in enumerate(common_derivs): # derive index=0 address + _id = next(gap) + idx subpath = path.format(account=0, change=0, idx=0) # e.g. "m/44'/1'/0'/0/0" sk = node_prv.subkey_for_path(subpath[2:]) # capture full index=0 address from display screen & validate it - goto_address_explorer(click_idx=idx) + goto_address_explorer(click_idx=_id) addr_dict = parse_display_screen(0, 10) if subpath not in addr_dict: raise Exception('Subpath ("%s") not found in address explorer display' % subpath) @@ -169,7 +172,7 @@ def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress, cap_menu, validate_address(expected_addr, sk) # validate that stub is correct - [start, end] = m[idx].split('-') + [start, end] = m[_id].split('-') assert expected_addr.startswith(start) assert expected_addr.endswith(end) @@ -191,7 +194,7 @@ def test_applications_samourai(chain, change, option, goto_address_explorer, cap node_prv = BIP32Node.from_wallet_key( sim_execfile('devtest/dump_private.py').strip() ) - goto_address_explorer(click_idx=4) # "applications" at index 3 + goto_address_explorer(click_idx=6) # "applications" at index 3 menu = cap_menu() assert "Samourai" in menu pick_menu_item("Samourai") @@ -224,8 +227,10 @@ def test_address_display(goto_address_explorer, parse_display_screen, mk_common_ sim_execfile('devtest/dump_private.py').strip() ) common_derivs = mk_common_derivations(node_prv.netcode()) + gap = iter(range(1, 10)) for click_idx, (path, addr_format) in enumerate(common_derivs): # Click on specified derivation idx in explorer + _id = next(gap) + click_idx goto_address_explorer(click_idx=click_idx) # perform keypad press sequence @@ -239,7 +244,7 @@ def test_address_display(goto_address_explorer, parse_display_screen, mk_common_ sk = node_prv.subkey_for_path(subpath[2:]) validate_address(given_addr, sk) -@pytest.mark.parametrize('click_idx', range(4)) +@pytest.mark.parametrize('click_idx', [1,3,5,7]) @pytest.mark.parametrize("change", [True, False]) @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"]) def test_dump_addresses(way, change, generate_addresses_file, mk_common_derivations, sim_execfile, validate_address, @@ -286,7 +291,9 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, goto_addre assert f'Account: {account_num}' in m which = 0 + gap = iter(range(1,10)) for idx, (path, addr_format) in enumerate(common_derivs): + _id = next(gap) + idx # derive index=0 address assert '{account}' in path @@ -297,7 +304,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, goto_addre # go down menu to expected derivation spot m = cap_menu() - pick_menu_item(m[idx]) + pick_menu_item(m[_id]) time.sleep(0.1) addr_dict = parse_display_screen(0, 10) @@ -307,7 +314,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item, goto_addre validate_address(expected_addr, sk) # validate that stub is correct - [start, end] = m[idx].split('-') + [start, end] = m[_id].split('-') assert expected_addr.startswith(start) assert expected_addr.endswith(end) @@ -388,14 +395,14 @@ def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address m = cap_menu() assert m[0] == 'Classic P2PKH' assert m[1] == 'Segwit P2WPKH' - assert m[2] == 'P2SH-P2WPKH' + assert m[2] == 'P2SH-Segwit' assert m[3] == 'Taproot P2TR' fmts = { AF_CLASSIC: 'Classic P2PKH', AF_P2WPKH: 'Segwit P2WPKH', AF_P2TR: 'Taproot P2TR', - AF_P2WPKH_P2SH: 'P2SH-P2WPKH', + AF_P2WPKH_P2SH: 'P2SH-Segwit', } pick_menu_item(fmts[which_fmt]) @@ -486,7 +493,7 @@ def test_bitcoind_descriptor_address(addr_fmt, acct_num, bitcoind, goto_home, pi sig_check = True if addr_fmt == AF_P2WPKH: - menu_item = "Native Segwit" + menu_item = "Segwit P2WPKH" desc_prefix = "wpkh(" click_idx = 2 elif addr_fmt == AF_P2WPKH_P2SH: @@ -494,13 +501,13 @@ def test_bitcoind_descriptor_address(addr_fmt, acct_num, bitcoind, goto_home, pi desc_prefix = "sh(wpkh(" click_idx = 1 elif addr_fmt == AF_P2TR: - menu_item = "Taproot (P2TR)" + menu_item = "Taproot P2TR" desc_prefix = "tr(" click_idx = 3 sig_check = False else: # addr_fmt == AF_CLASSIC: - menu_item = "Legacy (P2PKH)" + menu_item = "Classic P2PKH" desc_prefix = "pkh(" click_idx = 0 diff --git a/testing/test_export.py b/testing/test_export.py index f9155d31..f1845a60 100644 --- a/testing/test_export.py +++ b/testing/test_export.py @@ -220,7 +220,7 @@ def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keyp assert got.sec() == expect.sec() -@pytest.mark.parametrize('mode', [ "Legacy (P2PKH)", "P2SH-Segwit", "Native Segwit"]) +@pytest.mark.parametrize('mode', [ "Classic P2PKH", "P2SH-Segwit", "Segwit P2WPKH"]) @pytest.mark.parametrize('acct_num', [ None, '0', '9897']) @pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"]) @pytest.mark.parametrize('testnet', [True, False]) @@ -606,7 +606,7 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting menu = cap_menu() if addr_fmt == AF_P2WPKH: - menu_item = "Native Segwit" + menu_item = "Segwit P2WPKH" desc_prefix = "wpkh(" bip44_purpose = 84 elif addr_fmt == AF_P2WPKH_P2SH: @@ -619,7 +619,7 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting bip44_purpose = 86 else: # addr_fmt == AF_CLASSIC: - menu_item = "Legacy (P2PKH)" + menu_item = "Classic P2PKH" desc_prefix = "pkh(" bip44_purpose = 44 @@ -673,7 +673,7 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_ need_keypress(ch) need_keypress("y") need_keypress("y") # int_ext <0;1> - pick_menu_item("Native Segwit") # both postmix and premix are p2wpkh only + pick_menu_item("Segwit P2WPKH") # both postmix and premix are p2wpkh only file_desc_generic = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH) need_keypress("y") # written need_keypress("x") # go back to advanced diff --git a/testing/test_paper.py b/testing/test_paper.py index e376e738..3b8a0d19 100644 --- a/testing/test_paper.py +++ b/testing/test_paper.py @@ -40,8 +40,8 @@ def test_generate(mode, chain, pdf, cap_menu, pick_menu_item, goto_home, cap_sto time.sleep(0.1) if mode == 'segwit': - pick_menu_item('Classic Address') - pick_menu_item('Segwit/Bech32') + pick_menu_item('Classic P2PKH') + pick_menu_item('Segwit P2WPKH') time.sleep(0.5) if mode == 'taproot':