Merge branch 'master' of github.com:Coldcard/firmware
This commit is contained in:
commit
2b5a99ed59
@ -264,16 +264,16 @@ def seconds2human_readable(s):
|
||||
hours = s % (3600 * 24) // 3600
|
||||
minutes = (s % 3600) // 60
|
||||
seconds = (s % 3600) % 60
|
||||
msg = ""
|
||||
msg = []
|
||||
if days:
|
||||
msg += "%dd" % days
|
||||
msg.append("%dd" % days)
|
||||
if hours:
|
||||
msg += " %dh" % hours
|
||||
msg.append("%dh" % hours)
|
||||
if minutes:
|
||||
msg += " %dm" % minutes
|
||||
msg.append("%dm" % minutes)
|
||||
if seconds:
|
||||
msg += " %ds" % seconds
|
||||
msg.append("%ds" % seconds)
|
||||
|
||||
return msg
|
||||
return " ".join(msg)
|
||||
|
||||
# EOF
|
||||
|
||||
@ -49,7 +49,7 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
assert "ignores passphrases and produces backup of main seed" in body
|
||||
assert "(2) to back-up BIP39 passphrase wallet" in body
|
||||
if st == "eph":
|
||||
assert "An temporary seed is in effect" in body
|
||||
assert "A temporary seed is in effect" in body
|
||||
assert "so backup will be of that seed" in body
|
||||
|
||||
need_keypress("y")
|
||||
@ -238,7 +238,7 @@ def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home
|
||||
pick_menu_item("Backup System")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert "An temporary seed is in effect" in story
|
||||
assert "A temporary seed is in effect" in story
|
||||
assert "so backup will be of that seed" in story
|
||||
need_keypress("y")
|
||||
time.sleep(.1)
|
||||
@ -540,6 +540,7 @@ def test_clone_start(reset_seed_words, pick_menu_item, cap_story, goto_home):
|
||||
num_7z = len([i for i in os.listdir(sd_dir) if i.endswith(".7z")])
|
||||
fname = "ccbk-start.json"
|
||||
reset_seed_words()
|
||||
goto_home()
|
||||
shutil.copy(f"data/{fname}", sd_dir)
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Backup")
|
||||
|
||||
@ -226,7 +226,8 @@ def test_bip_vectors(mode, index, entropy, expect, cap_story, need_keypress,
|
||||
])
|
||||
@pytest.mark.parametrize('index', [0, 1, 10, 100, 1000, 9999])
|
||||
def test_path_index(mode, pattern, index, need_keypress, cap_screen_qr,
|
||||
derive_bip85_secret):
|
||||
derive_bip85_secret, reset_seed_words):
|
||||
reset_seed_words()
|
||||
# Uses any key on Simulator; just checking for operation + entropy level
|
||||
_, story = derive_bip85_secret(mode, index)
|
||||
|
||||
@ -279,6 +280,8 @@ def test_path_index(mode, pattern, index, need_keypress, cap_screen_qr,
|
||||
elif 'WIF' in mode:
|
||||
assert qr == got
|
||||
|
||||
need_keypress("x")
|
||||
|
||||
|
||||
def test_type_passwords(dev, cap_menu, pick_menu_item,
|
||||
goto_home, cap_story, need_keypress, cap_screen
|
||||
|
||||
@ -119,11 +119,11 @@ def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story, nfc_r
|
||||
|
||||
if nfc:
|
||||
need_keypress("1") # show QR code
|
||||
time.sleep(.1)
|
||||
time.sleep(.2)
|
||||
need_keypress("3") # any QR can be exported via NFC
|
||||
time.sleep(.1)
|
||||
time.sleep(.2)
|
||||
str_words = nfc_read_text()
|
||||
time.sleep(.1)
|
||||
time.sleep(.5)
|
||||
need_keypress("y") # exit NFC animation
|
||||
return str_words.split(" ") # always truncated
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ from onetimepass import get_hotp
|
||||
from objstruct import ObjectStruct as DICT
|
||||
from txn import render_address, fake_txn
|
||||
from psbt import ser_prop_key
|
||||
from helpers import sign_msg, prandom
|
||||
from helpers import sign_msg, prandom, xfp2str
|
||||
from ckcc_protocol.constants import *
|
||||
from ckcc_protocol.protocol import CCProtocolPacker
|
||||
from ckcc_protocol.protocol import CCUserRefused, CCProtoError
|
||||
@ -776,6 +776,128 @@ def test_multiple_signings(dev, quick_start_hsm, is_simulator,
|
||||
attempt_psbt(psbt)
|
||||
|
||||
|
||||
@pytest.mark.veryslow
|
||||
@pytest.mark.parametrize("cc_first", [True, False])
|
||||
@pytest.mark.parametrize("M_N", [(2,3), (3,5), (15,15)])
|
||||
def test_multiple_signings_multisig(cc_first, M_N, dev, quick_start_hsm,
|
||||
is_simulator, attempt_psbt, fake_txn,
|
||||
load_hsm_users, auth_user, bitcoind,
|
||||
request):
|
||||
# signs 400 different PSBTs in loop beaing one leg of multisig
|
||||
# CC must be on regtest if testing with real thing
|
||||
af = "bech32"
|
||||
M, N = M_N
|
||||
bitcoind.delete_wallet_files(pattern="bitcoind--signer")
|
||||
bitcoind.delete_wallet_files(pattern="watch_only_")
|
||||
# create multiple bitcoin wallets (N-1) as one signer is CC
|
||||
bitcoind_signers = [
|
||||
bitcoind.create_wallet(wallet_name=f"bitcoind--signer{i}", disable_private_keys=False, blank=False,
|
||||
passphrase=None, avoid_reuse=False, descriptors=True)
|
||||
for i in range(N - 1)
|
||||
]
|
||||
for signer in bitcoind_signers:
|
||||
signer.keypoolrefill(405)
|
||||
# watch only wallet where multisig descriptor will be imported
|
||||
bitcoind_watch_only = bitcoind.create_wallet(
|
||||
wallet_name=f"watch_only_{af}_{M}of{N}", disable_private_keys=True,
|
||||
blank=True, passphrase=None, avoid_reuse=False, descriptors=True
|
||||
)
|
||||
# get keys from bitcoind signers
|
||||
bitcoind_signers_xpubs = []
|
||||
for signer in bitcoind_signers:
|
||||
target_desc = ""
|
||||
bitcoind_descriptors = signer.listdescriptors()["descriptors"]
|
||||
for desc in bitcoind_descriptors:
|
||||
if desc["desc"].startswith("pkh(") and desc["internal"] is False:
|
||||
target_desc = desc["desc"]
|
||||
core_desc, checksum = target_desc.split("#")
|
||||
# remove pkh(....)
|
||||
core_key = core_desc[4:-1]
|
||||
bitcoind_signers_xpubs.append(core_key)
|
||||
|
||||
cc_deriv = "m/9999h/1h/0h"
|
||||
cc_xpub = dev.send_recv(CCProtocolPacker.get_xpub(cc_deriv), timeout=None)
|
||||
xfp_str = xfp2str(dev.master_fingerprint).lower()
|
||||
cc_key_ext = f"[{xfp_str}/{cc_deriv.replace('m/','')}]{cc_xpub}/0/*"
|
||||
cc_key_int = f"[{xfp_str}/{cc_deriv.replace('m/','')}]{cc_xpub}/1/*"
|
||||
assert cc_xpub[1:4] == 'pub'
|
||||
all_external = bitcoind_signers_xpubs + [cc_key_ext]
|
||||
bitcoind_signers_xpubs_int = [i.replace("/0/*", "/1/*") for i in bitcoind_signers_xpubs]
|
||||
all_internal = bitcoind_signers_xpubs_int + [cc_key_int]
|
||||
template_ext = f"wsh(sortedmulti({M},{','.join(all_external)}))"
|
||||
template_int = f"wsh(sortedmulti({M},{','.join(all_internal)}))"
|
||||
desc_info_ext = bitcoind_watch_only.getdescriptorinfo(template_ext)
|
||||
desc_info_int = bitcoind_watch_only.getdescriptorinfo(template_int)
|
||||
desc_ext = desc_info_ext["descriptor"] # external with checksum
|
||||
desc_int = desc_info_int["descriptor"] # internal with checksum
|
||||
|
||||
desc_obj_ext = {
|
||||
"desc": desc_ext,
|
||||
"active": True,
|
||||
"timestamp": "now",
|
||||
"internal": False,
|
||||
"range": [0, 405],
|
||||
}
|
||||
desc_obj_int = {
|
||||
"desc": desc_int,
|
||||
"active": True,
|
||||
"timestamp": "now",
|
||||
"internal": True,
|
||||
"range": [0, 405],
|
||||
}
|
||||
# import multisig wallet to bitcoin core watch only wallet
|
||||
res = bitcoind_watch_only.importdescriptors([desc_obj_int, desc_obj_ext])
|
||||
for obj in res:
|
||||
assert obj["success"], obj
|
||||
|
||||
# uploading only external to CC
|
||||
file_len, sha = dev.upload_file(desc_ext.encode('ascii'))
|
||||
open('debug/last-config.txt', 'wt').write(desc_ext)
|
||||
dev.send_recv(CCProtocolPacker.multisig_enroll(file_len, sha), timeout=30000)
|
||||
|
||||
time.sleep(.2)
|
||||
if dev.is_simulator:
|
||||
need_keypress = request.getfixturevalue('need_keypress')
|
||||
need_keypress("y")
|
||||
else:
|
||||
import pdb;pdb.set_trace() # user interaction required on real CC
|
||||
|
||||
multi_addr = bitcoind_watch_only.getnewaddress("", af)
|
||||
# create spendable segwit utxo in multi wallet
|
||||
bitcoind.supply_wallet.generatetoaddress(5, bitcoind.supply_wallet.getnewaddress())
|
||||
bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=250)
|
||||
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
|
||||
|
||||
policy = DICT(warnings_ok=True, must_log=1, rules=[dict(users=['pw'])])
|
||||
|
||||
load_hsm_users()
|
||||
quick_start_hsm(policy)
|
||||
|
||||
for count in range(400):
|
||||
# do a peel chain
|
||||
dest_a = bitcoind.supply_wallet.getnewaddress("", af)
|
||||
psbt_resp = bitcoind_watch_only.walletcreatefundedpsbt(
|
||||
[], [{dest_a: 0.3}], 0, {"fee_rate": 10, "change_type": af}
|
||||
)
|
||||
psbt_str = psbt_resp.get("psbt")
|
||||
if not cc_first:
|
||||
signed = 0
|
||||
for signer in bitcoind_signers:
|
||||
resp = signer.walletprocesspsbt(psbt_str, True)
|
||||
psbt_str = resp.get("psbt")
|
||||
signed +=1
|
||||
# do not want to finalize this
|
||||
if signed == M - 1:
|
||||
break
|
||||
|
||||
psbt = base64.b64decode(psbt_str)
|
||||
|
||||
auth_user.psbt_hash = sha256(psbt).digest()
|
||||
auth_user("pw")
|
||||
|
||||
attempt_psbt(psbt)
|
||||
|
||||
|
||||
def test_sign_msg_good(quick_start_hsm, change_hsm, attempt_msg_sign, addr_fmt=AF_CLASSIC):
|
||||
# message signing, but only at certain derivations
|
||||
permit = ['m/73', "m/*'", 'm/1p/3h/4/5/6/7' ]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user