fixes & version bump
This commit is contained in:
parent
d3d301f48c
commit
3c016004c7
@ -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))`
|
||||
|
||||
@ -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`
|
||||
|
||||
|
||||
@ -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'))
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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=@",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user