From 8e90fe67b0aff5741512597eba51b42e1a138477 Mon Sep 17 00:00:00 2001 From: scgbckbone Date: Thu, 22 May 2025 12:00:05 +0200 Subject: [PATCH] docs & nits --- docs/bip-21-extensions.md | 11 +++++++++++ shared/address_explorer.py | 7 +++---- shared/display.py | 5 ++--- shared/ownership.py | 17 ++++++----------- shared/wallet.py | 14 +++++++++++--- testing/test_ownership.py | 19 ++++++++++++++----- 6 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 docs/bip-21-extensions.md diff --git a/docs/bip-21-extensions.md b/docs/bip-21-extensions.md new file mode 100644 index 00000000..0fc2ab5d --- /dev/null +++ b/docs/bip-21-extensions.md @@ -0,0 +1,11 @@ +## `wallet` Ownership address check + +Address ownership allows to specify particular multisig wallet in which to search, allowing to skip +useless searches in irrelevant wallets. `wallet` query parameter is provided via [BIP-21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) + +#### Examples: +``` +tb1q4d67p7stxml3kdudrgkg5mgaxsrgzcqzjrrj4gg62nxtvnsnvqjsxjkej0?wallet=my_wal + +'mtHSVByP9EYZmB26jASDdPVm19gvpecb5R?label=coldcard_purchase&amount=50&wallet=multi_wsh', +``` \ No newline at end of file diff --git a/shared/address_explorer.py b/shared/address_explorer.py index 7da874d4..783acb0c 100644 --- a/shared/address_explorer.py +++ b/shared/address_explorer.py @@ -402,10 +402,8 @@ def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0, cha from ownership import OWNERSHIP if ms_wallet: - if (start == 0) and (n > 100) and change in (0, 1): - saver = OWNERSHIP.saver(ms_wallet, change, start, n) - else: - saver = None + # saver will be None if we don't think it worth saving these addresses + saver = OWNERSHIP.saver(ms_wallet, change, start, n) for line in ms_wallet.generate_address_csv(start, n, change): yield line @@ -419,6 +417,7 @@ def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0, cha from wallet import MasterSingleSigWallet main = MasterSingleSigWallet(addr_fmt, path, account_num) + # saver will be None if we don't think it worth saving these addresses saver = OWNERSHIP.saver(main, change, start, n) yield '"Index","Payment Address","Derivation"\n' diff --git a/shared/display.py b/shared/display.py index 356fb186..67d218f3 100644 --- a/shared/display.py +++ b/shared/display.py @@ -161,9 +161,8 @@ class Display: self.text(None, y, msg, font=FontLarge) if line2: - y += FontLarge.height # add height of above - y += FontTiny.height # add space of size FontTiny height - self.text(None, y, line2, font=FontSmall) + # 21 + 6 ie. FontLarge.height of above text + FontTiny.height as space between + self.text(None, y + 27, line2, font=FontSmall) if percent is not None: self.progress_bar(percent) diff --git a/shared/ownership.py b/shared/ownership.py index 2dd16240..de1d8546 100644 --- a/shared/ownership.py +++ b/shared/ownership.py @@ -39,6 +39,7 @@ OWNERSHIP_MAGIC = 0x10A0 # "Address Ownership" v1.0 # target 3 flash blocks, max file size => 764 addresses MAX_ADDRS_STORED = const(764) # =((3*512) - OWNERSHIP_FILE_HDR_LEN) // HASH_ENC_LEN +BONUS_AFTER_MATCH = const(20) # number of addresses to still generate after match found def encode_addr(addr, salt): # Convert text address to something we can store while preserving privacy. @@ -161,7 +162,7 @@ class AddressCacheFile: self.count += 1 if bonus: - if bonus >= 20: + if bonus >= BONUS_AFTER_MATCH: # do (at most) 20 more - limited by 'start_idx' & 'count' break bonus += 1 @@ -317,11 +318,8 @@ class OwnershipCache: cachefs.append(AddressCacheFile(w, 1)) for cf in cachefs: - msg, l2 = "Searching...", "(change)" if cf.change_idx else None - if dis.has_lcd: - msg, l2 = 'Searching wallet(s)...', cf.nice_name() - - dis.fullscreen(msg, line2=l2) + msg = "Searching wallet(s)..." if dis.has_lcd else "Searching..." + dis.fullscreen(msg, line2=cf.nice_name()) wallet, subpath = OWNERSHIP.search_wallet_cache(addr, cf) if wallet: # first arg from_cache=True @@ -330,11 +328,8 @@ class OwnershipCache: # nothing found in existing cache files c = 0 for cf in cachefs: - msg, l2 = "Generating...", "(change)" if cf.change_idx else None - if dis.has_lcd: - msg, l2 = 'Generating addresses...', cf.nice_name() - - dis.fullscreen(msg, line2=l2) + msg = "Generating addresses..." if dis.has_lcd else "Generating..." + dis.fullscreen(msg, line2=cf.nice_name()) wallet, subpath = OWNERSHIP.search_build_wallet(addr, cf) c += cf.count if wallet: diff --git a/shared/wallet.py b/shared/wallet.py index 6f76bff9..b708c075 100644 --- a/shared/wallet.py +++ b/shared/wallet.py @@ -63,6 +63,13 @@ class MasterSingleSigWallet(WalletABC): # - path can be overriden when we come here via address explorer n = chains.addr_fmt_label(addr_fmt) + if not version.has_qwerty: + # Mk4 tiny display + # Classic P2PKH -> P2PKH + # Segwit P2WPKH -> P2WPKH + # P2SH-Segwit -> no change (should not be used that much) + n = n.split(" ")[-1] + purpose = chains.af_to_bip44_purpose(addr_fmt) prefix = path or 'm/%dh/{coin_type}h/{account}h' % purpose @@ -72,12 +79,13 @@ class MasterSingleSigWallet(WalletABC): self.chain = chains.current_chain() if account_idx != 0: - n += ' Account#%d' % account_idx + rv = " Account#%d" if version.has_qwerty else " Acct#%d" + n += rv % account_idx if self.chain.ctype == 'XTN': - n += ' (Testnet)' + n += ' (Testnet)' if version.has_qwerty else " XTN" if self.chain.ctype == 'XRT': - n += ' (Regtest)' + n += ' (Regtest)' if version.has_qwerty else " XRT" self.name = n self.addr_fmt = addr_fmt diff --git a/testing/test_ownership.py b/testing/test_ownership.py index 8ae8516d..64c82c21 100644 --- a/testing/test_ownership.py +++ b/testing/test_ownership.py @@ -59,15 +59,18 @@ def test_negative(addr_fmt, testnet, sim_exec): @pytest.mark.parametrize('from_empty', [ True, False] ) def test_positive(addr_fmt, offset, subaccount, chain, from_empty, change_idx, sim_exec, wipe_cache, make_myself_wallet, use_testnet, goto_home, pick_menu_item, - enter_number, press_cancel, settings_set, import_ms_wallet, clear_miniscript + enter_number, press_cancel, settings_set, import_ms_wallet, clear_miniscript, is_q1, ): # API/Unit test, limited UX + ms_addr_fmts = { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH } + if (addr_fmt in ms_addr_fmts) and subaccount: + raise pytest.skip('multisig with subaccount') if chain == "BTC": use_testnet(False) testnet = False - if addr_fmt in { AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH }: + if addr_fmt in ms_addr_fmts: # multisig jigs assume testnet raise pytest.skip('testnet only') @@ -168,10 +171,16 @@ def test_positive(addr_fmt, offset, subaccount, chain, from_empty, change_idx, from_cache, got_name, got_path = lst assert from_cache == (not from_empty) - assert expect_name in got_name - if subaccount and '...' not in path: + if is_q1: + assert expect_name in got_name + else: + assert expect_name.split(" ")[-1] in got_name + if subaccount: # not expected for multisig, since we have proper wallet name - assert f'Account#{subaccount}' in got_name + if is_q1: + assert f'Account#{subaccount}' in got_name + else: + assert f'Acct#{subaccount}' in got_name assert got_path == (change_idx, offset)