bugfix: default menu position in custom path address format menu
What changed, and why it matters
This commit fixes a UI bug in the COLDCARD address explorer. When a user entered a custom BIP32 path, the device was highlighting the wrong default address format on the selection menu. For example, a path meant for classic Bitcoin addresses (starting with m/44h) might have had Segwit highlighted by default. The fix corrects the menu's default cursor position and adds a warning not to reorder the underlying address-format list. There is no direct security vulnerability here—just a user-experience bug that could, in rare cases, lead a user to confirm an unintended address type.
No security patch required; treat as normal bugfix release. Users who generated custom-path addresses should verify the address type matches their intent, especially if they relied on the default highlighted option before this fix.
Security signals we found
UI default-selection bug in security-critical address-format picker
Potential user confusion leading to generation of unintended address type
No cryptographic, authorization, or memory-safety flaw evident
Evidence from the diff
PickAddrFmtMenu.init in shared/address_explorer.py pre-selects an address-format menu item based on the BIP44/BIP49/BIP84 path prefix. The original code used stale index logic relative to chains.SINGLESIG_AF, which is ordered (AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH). The old code set index 1 for m/84h and index 2 for m/49h, which was backwards/wrong. The patch changes it to set index 1 for m/44h (Classic) and index 2 for m/49h (P2SH-Segwit), leaving m/84h at the default index 0 (Segwit P2WPKH). A comment in chains.py now warns that SINGLESIG_AF order is load-bearing. A regression test verifies the cursor lands on the expected label for each prefix.
Changed components
shared/address_explorer.pyshared/chains.pytesting/test_address_explorer.pyInspect captured patch +45 / −3
diff --git a/releases/Next-ChangeLog.md b/releases/Next-ChangeLog.md
index b1bb3de..6d53202 100644
--- a/releases/Next-ChangeLog.md
+++ b/releases/Next-ChangeLog.md
@@ -4,6 +4,7 @@ This lists the new changes that have not yet been published in a normal release.
# Shared Improvements - Both Mk and Q
+- Bugfix: Custom address default menu position wrong
- Bugfix: Delta Mode Trick PIN was never restored from backup
- Bugfix: Proper error message for incorrect 7z headers
- Bugfix: Exiting nickname entry with nickname already saved deleted previous nickname
diff --git a/shared/address_explorer.py b/shared/address_explorer.py
index 838c815..0ea9082 100644
--- a/shared/address_explorer.py
+++ b/shared/address_explorer.py
@@ -126,9 +126,10 @@ class PickAddrFmtMenu(MenuSystem):
for af in chains.SINGLESIG_AF
]
super().__init__(items)
- if path.startswith("m/84h"):
+ # below is sensitive to order in chains.SINGLESIG_AF
+ if path.startswith("m/44h"):
self.goto_idx(1)
- if path.startswith("m/49h"):
+ elif path.startswith("m/49h"):
self.goto_idx(2)
async def done(self, _1, _2, item):
diff --git a/shared/chains.py b/shared/chains.py
index 9d53847..4833251 100644
--- a/shared/chains.py
+++ b/shared/chains.py
@@ -12,7 +12,7 @@ from serializations import hash160, ser_compact_size, disassemble
from ucollections import namedtuple
from opcodes import OP_RETURN, OP_1, OP_16
-
+# DO NOT CHANGE ORDER! PickAddrFmtMenu.__init__ expects correct order
SINGLESIG_AF = (AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH)
# See SLIP 132 <https://github.com/satoshilabs/slips/blob/master/slip-0132.md>
diff --git a/testing/test_address_explorer.py b/testing/test_address_explorer.py
index de35d86..2d3bcf5 100644
--- a/testing/test_address_explorer.py
+++ b/testing/test_address_explorer.py
@@ -555,6 +555,46 @@ def test_custom_path(path_sidx, which_fmt, addr_vs_path, pick_menu_item, goto_ad
addr_vs_path(a, p, addr_fmt=which_fmt)
+@pytest.mark.parametrize("prefix,label", [
+ ("m/84h", "Segwit P2WPKH"),
+ ("m/49h", "P2SH-Segwit"),
+ ("m/44h", "Classic P2PKH"),
+])
+def test_pick_addr_fmt_menu_default(prefix, label, goto_address_explorer, is_q1, sim_exec,
+ pick_menu_item, need_keypress, press_select, cap_screen,
+ cap_story, use_testnet):
+ # PickAddrFmtMenu must pre-select the natural address format for common BIP paths
+ use_testnet()
+ goto_address_explorer()
+ pick_menu_item("Custom Path")
+ pick_menu_item(prefix + "/⋯")
+ need_keypress("0")
+ press_select()
+ path_to_pick = prefix + "/0h" if is_q1 else "⋯/0h"
+ pick_menu_item(path_to_pick)
+ time.sleep(.2)
+ # currently sitting at address format choice menu
+ cur_label = sim_exec(
+ 'from ux import the_ux; top = the_ux.top_of_stack();'
+ 'RV.write(top.items[top.cursor].label)'
+ )
+ assert cur_label == label, ("For %s: expected cursor on '%s', got '%s'" % (prefix, label, cur_label))
+
+ # choose menu item we're currently at
+ press_select()
+ need_keypress("3")
+ time.sleep(.2)
+ title, story = cap_story()
+ addr = addr_from_display_format(story.split("\n\n")[1].split("\n")[1])
+ if prefix == "m/84h":
+ assert addr.startswith("tb1")
+ elif prefix == "m/49h":
+ assert addr.startswith("2")
+ else:
+ assert addr.startswith("m") or addr.startswith("n")
+
+ # EOF
+
def test_change_account_cancel(goto_address_explorer, pick_menu_item, press_cancel, cap_menu):
goto_address_explorer()
time.sleep(.2)
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.