fixes & version bump

This commit is contained in:
scgbckbone 2025-02-19 15:34:13 +01:00
parent d3d301f48c
commit 3c016004c7
21 changed files with 64 additions and 75 deletions

View File

@ -35,7 +35,7 @@ There are 2 methods to provide provably unspendable internal key, if users wish
`tr(unspend(77ec0c0fdb9733e6a3c753b1374c4a465cba80dff52fc196972640a26dd08b76)/<0:1>/*, sortedmulti_a(2,@0,@1))`
### Below option were deprecated in version 6.3.5X ?
### Below option were deprecated in version 6.3.5X & 6.3.5QX
3. use **static** provably unspendable internal key H from [BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs).
`tr(50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0, sortedmulti_a(2,@0,@1))`

View File

@ -6,24 +6,26 @@
- This preview version of firmware has not yet been qualified
- and tested to the same standard as normal Coinkite products.
- It is recommended only for developers and early adopters
- for experimental use. DO NOT use for large Bitcoin amounts.
- for experimental use.
```
This lists the changes in the most recent EDGE firmware, for each hardware platform.
# Shared Improvements - Both Mk4 and Q
Change: Allow origin-less extended keys in multisig & miniscript descriptors
Change: Static internal keys disallowed - all keys need to be ranged extended keys
# Mk4 Specific Changes
## 6.3.4X - 2024-07-04
## 6.3.5X - 2024-07-04
- all updates from `5.4.1`
# Q Specific Changes
## 6.3.4QX - 2024-07-04
## 6.3.5QX - 2024-07-04
- all updates from version `1.3.1Q`

View File

@ -874,9 +874,9 @@ async def start_login_sequence():
# Version warning before HSM is offered
if version.is_edge and not ckcc.is_simulator():
await ux_show_story("This firmware version is qualified for use with wallets (such as"
"AnchorWatch, Liana, etc) that keep redundant key schemas for recovery"
"independent of COLDCARD. We support the very latest Bitcoin innovations"
"in the Edge Version.", title="Edge Version")
" AnchorWatch) that keep redundant key schemas for recovery"
" independent of COLDCARD. We support the very latest Bitcoin innovations"
" in the Edge Version.", title="Edge Version")
dis.draw_status(xfp=settings.get('xfp'))

View File

@ -17,7 +17,18 @@ from glob import settings
from auth import write_sig_file
from charcodes import KEY_QR, KEY_NFC, KEY_PAGE_UP, KEY_PAGE_DOWN, KEY_HOME, KEY_LEFT, KEY_RIGHT
from charcodes import KEY_CANCEL
from utils import show_single_address, problem_file_line, truncate_address
from utils import show_single_address, problem_file_line
def truncate_address(addr):
# Truncates address to width of screen, replacing middle chars
if not version.has_qwerty:
# - 16 chars screen width
# - but 2 lost at left (menu arrow, corner arrow)
# - want to show not truncated on right side
return addr[0:6] + '' + addr[-6:]
else:
# tons of space on Q1
return addr[0:12] + '' + addr[-12:]
class KeypathMenu(MenuSystem):
def __init__(self, path=None, nl=0):
@ -316,8 +327,9 @@ Press (3) if you really understand and accept these risks.
if n:
msg += "Press RIGHT to see next group, LEFT to go back. X to quit."
else:
escape += "0"
msg += " Press (0) to sign message with this key."
if addr_fmt != AF_P2TR:
escape += "0"
msg += " Press (0) to sign message with this key."
return msg, addrs, escape

View File

@ -1616,6 +1616,7 @@ class ShowMiniscriptAddress(ShowAddressBase):
def get_msg(self):
return '''\
{addr}
Wallet:
{name}
@ -1623,7 +1624,8 @@ Index:
{idx}
Change:
{change}'''.format(addr=self.address, name=self.msc.name, idx=self.idx, change=bool(self.change))
{change}'''.format(addr=show_single_address(self.address), name=self.msc.name,
idx=self.idx, change=bool(self.change))
def start_show_miniscript_address(msc, change, index):

View File

@ -14,7 +14,7 @@ from ucollections import namedtuple
from opcodes import OP_RETURN, OP_1, OP_16
SINGLESIG_AF = (AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH, AF_P2TR)
SINGLESIG_AF = (AF_P2WPKH, AF_CLASSIC, AF_P2TR, AF_P2WPKH_P2SH)
# See SLIP 132 <https://github.com/satoshilabs/slips/blob/master/slip-0132.md>
# for background on these version bytes. Not to be confused with SLIP-32 which involves Bech32.
@ -468,11 +468,12 @@ def parse_addr_fmt_str(addr_fmt):
return AF_CLASSIC
elif addr_fmt == "p2wpkh":
return AF_P2WPKH
elif addr_fmt == "p2tr":
return AF_P2TR
else:
raise ValueError
except ValueError:
raise ValueError("Invalid address format: '%s'\n\n"
"Choose from p2pkh, p2wpkh, p2sh-p2wpkh." % addr_fmt)
raise ValueError("Unsupported address format: '%s'" % addr_fmt)
def af_to_bip44_purpose(addr_fmt):

View File

@ -179,7 +179,7 @@ XpubExportMenu = [
# xxxxxxxxxxxxxxxx
MenuItem("Segwit (BIP-84)", f=export_xpub, arg=84),
MenuItem("Classic (BIP-44)", f=export_xpub, arg=44),
MenuItem("Taproot/P2TR(86)", f=export_xpub, arg=86),
MenuItem("Taproot/P2TR"+("(BIP-86)" if version.has_qwerty else "(86)"), f=export_xpub, arg=86),
MenuItem("P2WPKH/P2SH "+("(BIP-49)"if version.has_qwerty else "(49)"), f=export_xpub, arg=49),
MenuItem("Master XPUB", f=export_xpub, arg=0),
MenuItem("Current XFP", f=export_xpub, arg=-1),

View File

@ -13,7 +13,7 @@ from wallet import BaseStorageWallet
from menu import MenuSystem, MenuItem
from ux import ux_show_story, ux_confirm, ux_dramatic_pause
from files import CardSlot, CardMissingError, needs_microsd
from utils import problem_file_line, xfp2str, truncate_address, to_ascii_printable, swab32
from utils import problem_file_line, xfp2str, to_ascii_printable, swab32, show_single_address
from charcodes import KEY_QR, KEY_CANCEL, KEY_NFC, KEY_ENTER
@ -416,7 +416,7 @@ class MiniScriptWallet(BaseStorageWallet):
msg += '.../%d =>\n' % idx
addrs.append(addr)
msg += truncate_address(addr) + '\n\n'
msg += show_single_address(addr) + '\n\n'
dis.progress_sofar(idx - start + 1, n)
return msg, addrs

View File

@ -5,7 +5,7 @@
import stash, chains, ustruct, ure, uio, sys, ngu, uos, ujson, version
from ubinascii import hexlify as b2a_hex
from utils import xfp2str, str2xfp, cleanup_deriv_path, keypath_to_str, to_ascii_printable
from utils import str_to_keypath, problem_file_line, check_xpub, truncate_address, get_filesize
from utils import str_to_keypath, problem_file_line, check_xpub, get_filesize, show_single_address
from ux import ux_show_story, ux_confirm, ux_dramatic_pause, ux_clear_keys
from ux import import_export_prompt, ux_enter_bip32_index, show_qr_code, ux_enter_number, OK, X
from files import CardSlot, CardMissingError, needs_microsd
@ -473,7 +473,7 @@ class MultisigWallet(BaseStorageWallet):
msg += '.../%d/%d =>\n' % (change, idx)
addrs.append(addr)
msg += truncate_address(addr) + '\n\n'
msg += show_single_address(addr) + '\n\n'
dis.progress_sofar(idx - start + 1, n)
return msg, addrs

View File

@ -2425,16 +2425,16 @@ class psbtObject(psbtProxy):
stash.blank_object(node)
del sk, node
# drop sighash if default (SIGHASH_ALL)
if inp.sighash == SIGHASH_ALL:
inp.sighash = None
success.add(in_idx)
gc.collect()
if self.is_v2:
self.set_modifiable_flag(inp)
# drop sighash if default (SIGHASH_ALL)
if inp.sighash == SIGHASH_ALL:
inp.sighash = None
# done.
dis.progress_bar_show(1)

View File

@ -532,23 +532,6 @@ def word_wrap(ln, w):
yield left
def parse_addr_fmt_str(addr_fmt):
# accepts strings and also integers if already parsed
if addr_fmt in [AF_P2WPKH_P2SH, AF_P2WPKH, AF_CLASSIC, AF_P2TR]:
return addr_fmt
addr_fmt = addr_fmt.lower()
if addr_fmt in ("p2sh-p2wpkh", "p2wpkh-p2sh"):
return AF_P2WPKH_P2SH
elif addr_fmt == "p2pkh":
return AF_CLASSIC
elif addr_fmt == "p2wpkh":
return AF_P2WPKH
elif addr_fmt == "p2tr":
return AF_P2TR
else:
raise ValueError("Unsupported address format: '%s'" % addr_fmt)
def parse_extended_key(ln, private=False):
# read an xpub/ypub/etc and return BIP-32 node and what chain it's on.
# - can handle any garbage line
@ -792,19 +775,6 @@ def check_xpub(xfp, xpub, deriv, expect_chain, my_xfp, disable_checks=False):
# - this has effect of stripping SLIP-132 confusion away
return xfp == my_xfp, (xfp, deriv, chain.serialize_public(node, AF_P2SH))
def truncate_address(addr):
# Truncates address to width of screen, replacing middle chars
if not version.has_qwerty:
# - 16 chars screen width
# - but 2 lost at left (menu arrow, corner arrow)
# - want to show not truncated on right side
return addr[0:6] + '' + addr[-6:]
else:
# tons of space on Q1
return addr[0:12] + '' + addr[-12:]
def encode_seed_qr(words):
return ''.join('%04d' % bip39.get_word_index(w) for w in words)

View File

@ -19,7 +19,7 @@ LATEST_RELEASE = $(shell ls -t1 ../releases/*-mk4-*.dfu | head -1)
# Our version for this release.
# - caution, the bootrom will not accept version < 3.0.0
VERSION_STRING = 5.4.2
VERSION_STRING = 6.3.5X
# keep near top, because defined default target (all)
include shared.mk

View File

@ -16,7 +16,7 @@ BOOTLOADER_DIR = q1-bootloader
LATEST_RELEASE = $(shell ls -t1 ../releases/*-q1-*.dfu | head -1)
# Our version for this release.
VERSION_STRING = 1.3.2Q
VERSION_STRING = 6.3.5QX
# Remove this closer to shipping.
#$(warning "Forcing debug build")

View File

@ -604,7 +604,7 @@ def verify_qr_address(cap_screen_qr, cap_screen, is_q1):
def doit(addr_fmt, expect_addr=None):
qr = cap_screen_qr().decode('ascii')
if addr_fmt & AFC_BECH32:
if (addr_fmt & AFC_BECH32) or (addr_fmt & AFC_BECH32M):
qr = qr.lower()
# check text --if any-- matches QR contents

View File

@ -502,11 +502,14 @@ def test_custom_path(path_sidx, which_fmt, addr_vs_path, pick_menu_item, goto_ad
# msg sign
time.sleep(.1)
title, body = cap_story()
assert "Press (0) to sign message with this key" in body
need_keypress('0')
msg = "COLDCARD the rock solid HWW"
sign_msg_from_address(msg, addr, path, which_fmt, "sd", True)
press_cancel()
if which_fmt == AF_P2TR:
assert "Press (0) to sign message with this key" not in body
else:
assert "Press (0) to sign message with this key" in body
need_keypress('0')
msg = "COLDCARD the rock solid HWW"
sign_msg_from_address(msg, addr, path, which_fmt, "sd", True)
press_cancel()
else:
n = 10
if (start_idx + n) > MAX_BIP32_IDX:

View File

@ -566,7 +566,7 @@ def test_export_xpub(chain, acct_num, dev, cap_menu, pick_menu_item, goto_home,
is_xfp = False
if '-84' in m:
expect = f"m/84h/{chain_num}h/{{acct}}h"
elif '-86' in m:
elif '86' in m:
expect = f"m/86h/{chain_num}h/{{acct}}h"
elif '-44' in m:
expect = f"m/44h/{chain_num}h/{{acct}}h"

View File

@ -3084,7 +3084,6 @@ def test_originless_keys(tmplt, offer_minsc_import, get_cc_key, bitcoin_core_sig
address_explorer_check("sd", af, wo, name)
@pytest.mark.parametrize("internal_key", [
H,
"r=@",

View File

@ -241,7 +241,7 @@ def sign_msg_from_text(pick_menu_item, enter_number, press_select,
signed_msg = msg_sign_export(way, qr_only)
ret_msg, addr, sig = parse_signed_message(signed_msg)
addr_vs_path(addr, path, addr_fmt, testnet=True if chain == "XTN" else False)
addr_vs_path(addr, path, addr_fmt, chain=chain)
assert verify_message(addr, sig, ret_msg) is True
if addr_fmt == AF_CLASSIC and chain == "XTN":
res = bitcoind.rpc.verifymessage(addr, sig, ret_msg)
@ -270,7 +270,7 @@ def sign_msg_from_address(need_keypress, scan_a_qr, press_select, enter_complex,
time.sleep(.1)
signed_msg = msg_sign_export(way)
ret_msg, addr, sig = parse_signed_message(signed_msg)
addr_vs_path(addr, subpath, addr_fmt, testnet=testnet)
addr_vs_path(addr, subpath, addr_fmt, chain="XTN" if testnet else "BTC")
return doit
@ -405,7 +405,7 @@ def test_sign_msg_microsd_good(sign_on_microsd, msg, path, addr_vs_path,
path = default_derivation_by_af(addr_fmt, testnet=testnet)
# check expected addr was used
addr_vs_path(addr, path, addr_fmt, testnet=testnet)
addr_vs_path(addr, path, addr_fmt, chain="XTN" if testnet else "BTC")
assert verify_message(addr, sig, msg) is True
if addr_fmt == AF_CLASSIC and testnet:
res = bitcoind.rpc.verifymessage(addr, sig, ret_msg)
@ -456,7 +456,7 @@ def sign_using_nfc(goto_home, pick_menu_item, nfc_write_text, cap_story, press_s
press_select() # exit NFC animation
pmsg, addr, sig = parse_signed_message(signed_msg)
assert pmsg == msg
addr_vs_path(addr, subpath, addr_fmt, testnet=testnet)
addr_vs_path(addr, subpath, addr_fmt, chain="XTN" if testnet else "BTC")
assert verify_message(addr, sig, msg) is True
time.sleep(0.5)
_, story = cap_story()
@ -505,9 +505,9 @@ def test_sign_msg_with_ascii_non_printable_chars(msg, way, sign_on_microsd, addr
('hello%20sworld'%'', "m", AF_CLASSIC, 'many spaces', 0, 0), # spaces
('hello%10sworld'%'', "m/1h/3h", AF_P2WPKH_P2SH, 'many spaces', 0, 0), # spaces
('hello%5sworld'%'', "m", AF_CLASSIC, 'many spaces', 0, 0), # spaces
("coinkite", "m", AF_P2WSH, "Invalid address format", 0, 0), # invalid address format
("coinkite", "m", AF_P2WSH_P2SH, "Invalid address format", 0, 0), # invalid address format
("coinkite", " m", AF_P2TR, "Invalid address format", 0, 0), # invalid address format
("coinkite", "m", AF_P2WSH, "Unsupported address format", 0, 0), # invalid address format
("coinkite", "m", AF_P2WSH_P2SH, "Unsupported address format", 0, 0), # invalid address format
("coinkite", " m", AF_P2TR, "Unsupported address format", 0, 0), # invalid address format
("coinkite", "m/0/0/0/0/0/0/0/0/0/0/0/0/0", AF_CLASSIC, "too deep", 0, 0), # invalid path
("coinkite", "m/0/0/0/0/0/q/0/0/0", AF_P2WPKH, "invalid characters in path", 0, 0), # invalid path
("coinkite ", "m", AF_CLASSIC, "trailing space(s)", 0, 0), # invalid msg - trailing space

View File

@ -2224,6 +2224,7 @@ def test_ms_addr_explorer(change, M_N, addr_fmt, start_idx, clear_ms, cap_menu,
assert int(subpath.split('/')[-2]) == chng_idx
#print('../0/%s => \n %s' % (idx, B2A(script)))
addr = addr_from_display_format(addr)
assert addr == expect == qr_addrs[c]
c += 1

View File

@ -332,10 +332,9 @@ def test_address_explorer_saver(af, wipe_cache, settings_set, goto_address_explo
assert addr == addr_from_display_format(story.split("\n\n")[0])
assert title == ('Verified Address' if is_q1 else "Verified!")
assert 'Found in wallet' in story
assert 'Derivation path' in story
if af == "P2SH-Segwit":
assert "P2WPKH-in-P2SH" in story
elif af == "Segwit P2WPKH":
if "msc" not in af:
assert 'Derivation path' in story
if af == "Segwit P2WPKH":
assert " P2WPKH " in story
else:
assert af in story

View File

@ -3120,7 +3120,7 @@ def test_taproot_keyspend(use_regtest, bitcoind_d_sim_watch, start_sign, end_sig
assert title == 'OK TO SEND?'
assert "Consolidating" in story # self-spend
assert " 1 input\n 2 outputs" in story
addrs = story.split("\n\n")[3].split("\n")[-2:]
addrs = [addr_from_display_format(l) for l in story.split("\n") if l and (l[0] == '\x02')]
assert len(addrs) == 2
for addr in addrs:
assert addr.startswith("bcrt1p")