From 6d4a4f4eaae8d2dc07083046ca0baaf6689d7488 Mon Sep 17 00:00:00 2001 From: scgbckbone Date: Tue, 28 Jan 2025 11:42:22 +0100 Subject: [PATCH] option to show/export full multisig addresses; do not return to home menu after setting unsort_ms (cherry picked from commit 9b597592bc891c759a932e408e5f4d4a11810e68) --- releases/Next-ChangeLog.md | 1 + shared/address_explorer.py | 5 ++--- shared/multisig.py | 20 ++++++++------------ shared/nvstore.py | 3 ++- testing/conftest.py | 10 ++++++---- testing/test_multisig.py | 22 ++++++++++++++++------ 6 files changed, 35 insertions(+), 26 deletions(-) diff --git a/releases/Next-ChangeLog.md b/releases/Next-ChangeLog.md index 391bbda0..cfb26e92 100644 --- a/releases/Next-ChangeLog.md +++ b/releases/Next-ChangeLog.md @@ -17,6 +17,7 @@ This lists the new changes that have not yet been published in a normal release. - Enhancement: Catch more DeltaMode cases in XOR path. Thanks to [@dmonakhov](https://github.com/dmonakhov)) - Enhancement: BKPW override (for "developers") +- Enhancement: Add option to show/export full multisg addresses. Enable in `Settings->Multisig Wallets->Full Address View`. - Change: If derivation path is omitted during message signing, default is used based on address format (`m/44h/0h/0h/0/0` for p2pkh, and `m/84h/0h/0h/0/0` for p2wpkh). Default is no longer root (m). diff --git a/shared/address_explorer.py b/shared/address_explorer.py index 5e2ec7ee..140056bc 100644 --- a/shared/address_explorer.py +++ b/shared/address_explorer.py @@ -8,14 +8,13 @@ import chains, stash, version from ux import ux_show_story, the_ux, ux_enter_bip32_index from ux import export_prompt_builder, import_export_prompt_decode from menu import MenuSystem, MenuItem -from public_constants import AFC_BECH32, AFC_BECH32M, AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH, AF_P2TR +from public_constants import AFC_BECH32, AFC_BECH32M, AF_P2WPKH, AF_P2TR from multisig import MultisigWallet from miniscript import MiniScriptWallet from uasyncio import sleep_ms from uhashlib import sha256 from glob import settings from auth import write_sig_file -from utils import censor_address from charcodes import KEY_QR, KEY_NFC, KEY_PAGE_UP, KEY_PAGE_DOWN, KEY_HOME, KEY_LEFT, KEY_RIGHT from charcodes import KEY_CANCEL @@ -188,7 +187,7 @@ class AddressListMenu(MenuSystem): deriv = path.format(account=self.account_num, change=0, idx=self.start) node = sv.derive_path(deriv, register=False) address = chain.address(node, addr_fmt) - choices.append( (truncate_address(address), path, addr_fmt) ) + choices.append((truncate_address(address), path, addr_fmt)) dis.progress_sofar(len(choices), len(chains.CommonDerivations)) diff --git a/shared/multisig.py b/shared/multisig.py index 7ee60b72..24eefa81 100644 --- a/shared/multisig.py +++ b/shared/multisig.py @@ -13,7 +13,7 @@ from descriptor import Descriptor from miniscript import Key, Sortedmulti, Number, Multi from desc_utils import multisig_descriptor_template from public_constants import AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AFC_SCRIPT, MAX_SIGNERS, AF_P2TR -from menu import MenuSystem, MenuItem, NonDefaultMenuItem +from menu import MenuSystem, MenuItem, NonDefaultMenuItem, start_chooser, ToggleMenuItem from opcodes import OP_CHECKMULTISIG from exceptions import FatalPSBTIssue from glob import settings @@ -1172,7 +1172,6 @@ def disable_checks_chooser(): return int(MultisigWallet.disable_checks), ch, xset async def disable_checks_menu(*a): - from menu import start_chooser if not MultisigWallet.disable_checks: ch = await ux_show_story('''\ @@ -1205,7 +1204,6 @@ def psbt_xpubs_policy_chooser(): async def trust_psbt_menu(*a): # show a story then go into chooser - from menu import start_chooser ch = await ux_show_story('''\ This setting controls what the Coldcard does \ @@ -1230,18 +1228,16 @@ exists, otherwise 'Verify'.''') if ch == 'x': return start_chooser(psbt_xpubs_policy_chooser) -def unsorted_ms_chooser(): - ch = ['Do Not Allow', 'Allow'] - +def unsort_ms_chooser(): def xset(idx, text): - settings.set('unsort_ms', idx) - from actions import goto_top_menu - goto_top_menu() + if idx: + settings.set('unsort_ms', idx) + else: + settings.remove_key('unsort_ms') - return settings.get('unsort_ms', 0), ch, xset + return settings.get('unsort_ms', 0), ['Do Not Allow', 'Allow'], xset async def unsorted_ms_menu(*a): - from menu import start_chooser if not settings.get("unsort_ms", None): ch = await ux_show_story( @@ -1269,7 +1265,7 @@ async def unsorted_ms_menu(*a): ) return - start_chooser(unsorted_ms_chooser) + start_chooser(unsort_ms_chooser) class MultisigMenu(MenuSystem): diff --git a/shared/nvstore.py b/shared/nvstore.py index 0331fc99..7e2cc43b 100644 --- a/shared/nvstore.py +++ b/shared/nvstore.py @@ -65,6 +65,7 @@ from utils import call_later_ms # ptxurl = (str) URL for PushTx feature, clear to disable feature # hmx = (bool) Force display of current XFP in home menu, even w/o tmp seed active # unsort_ms = (bool) Allow unsorted multisig with BIP-67 disabled +# msas = multisig address show (do not censor multisig addresses) # Stored w/ key=00 for access before login # _skip_pin = hard code a PIN value (dangerous, only for debug) @@ -86,7 +87,7 @@ from utils import call_later_ms # keep these settings only if unspecified on the other end KEEP_IF_BLANK_SETTINGS = ["wa", "sighshchk", "emu", "rz", "b39skip", "axskip", "del", "pms", "idle_to", "batt_to", - "bright"] + "bright", "msas"] SEEDVAULT_FIELDS = ['seeds', 'seedvault', 'xfp', 'words', "bkpw"] diff --git a/testing/conftest.py b/testing/conftest.py index f9d9dff8..2109bb6d 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -945,8 +945,9 @@ def reset_seed_words(sim_exec, sim_execfile, simulator): @pytest.fixture() def settings_set(sim_exec): - def doit(key, val): - x = sim_exec("settings.set('%s', %r)" % (key, val)) + def doit(key, val, prelogin=False): + source = "from nvstore import SettingsObject;SettingsObject.prelogin()" if prelogin else "settings" + x = sim_exec("%s.set('%s', %r)" % (source, key, val)) assert x == '' return doit @@ -954,8 +955,9 @@ def settings_set(sim_exec): @pytest.fixture() def settings_get(sim_exec): - def doit(key, def_val=None): - cmd = f"RV.write(repr(settings.get('{key}', {def_val!r})))" + def doit(key, def_val=None, prelogin=False): + source = "from nvstore import SettingsObject;SettingsObject.prelogin()" if prelogin else "settings" + cmd = f"RV.write(repr({source}.get('{key}', {def_val!r})))" resp = sim_exec(cmd) assert 'Traceback' not in resp, resp return eval(resp) diff --git a/testing/test_multisig.py b/testing/test_multisig.py index 5a1f56cb..9b8184a0 100644 --- a/testing/test_multisig.py +++ b/testing/test_multisig.py @@ -12,7 +12,7 @@ from ckcc.protocol import CCProtocolPacker, MAX_TXN_LEN from pprint import pprint from base64 import b64encode, b64decode from base58 import encode_base58_checksum -from helpers import B2A, fake_dest_addr, xfp2str, detruncate_address +from helpers import B2A, fake_dest_addr, xfp2str from helpers import path_to_str, str_to_path, slip132undo, swab32, hash160 from struct import unpack, pack from constants import * @@ -1266,7 +1266,7 @@ def make_myself_wallet(dev, set_bip39_pw, offer_ms_import, press_select, clear_m title, story = offer_ms_import(config) #print(story) - # dont care if update or create; accept it. + # don't care if update or create; accept it. time.sleep(.1) press_select() @@ -2288,11 +2288,12 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke pick_menu_item, cap_menu, cap_story, make_multisig, import_ms_wallet, microsd_path, bitcoind_d_wallet_w_sk, use_regtest, load_export, way, is_q1, press_select, start_idx, settings_set, set_addr_exp_start_idx, - desc): + desc, garbage_collector, virtdisk_path): use_regtest() clear_ms() bitcoind = bitcoind_d_wallet_w_sk M, N = M_N + path_f = microsd_path if way == "sd" else virtdisk_path # whether to import as descriptor or old school to CC descriptor = random.choice([True, False]) bip67 = True @@ -2343,7 +2344,12 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke assert "change addresses." not in story assert "(0)" not in story - contents = load_export(way, label="Address summary", is_json=False, sig_check=False) + if way != "nfc": + contents, exp_fname = load_export(way, label="Address summary", is_json=False, + sig_check=False, ret_fname=True) + garbage_collector.append(path_f(exp_fname)) + else: + contents = load_export(way, label="Address summary", is_json=False, sig_check=False) addr_cont = contents.strip() goto_home() pick_menu_item('Settings') @@ -2351,7 +2357,12 @@ def test_bitcoind_ms_address(change, M_N, addr_fmt, clear_ms, goto_home, need_ke press_select() # only one enrolled multisig - choose it pick_menu_item('Descriptors') pick_menu_item("Bitcoin Core") - contents = load_export(way, label="Bitcoin Core multisig setup", is_json=False, sig_check=False) + if way != "nfc": + contents, exp_fname = load_export(way, label="Bitcoin Core multisig setup", is_json=False, + sig_check=False, ret_fname=True) + garbage_collector.append(path_f(exp_fname)) + else: + contents = load_export(way, label="Bitcoin Core multisig setup", is_json=False, sig_check=False) text = contents.replace("importdescriptors ", "").strip() # remove junk r1 = text.find("[") @@ -2521,7 +2532,6 @@ def bitcoind_multisig(bitcoind, bitcoind_d_sim_watch, need_keypress, cap_story, def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, microsd_wipe, goto_home, need_keypress, pick_menu_item, cap_story, load_export, microsd_path, cap_menu, try_sign, is_q1, press_select): - use_regtest() clear_ms() microsd_wipe()