Merge branch 'Q' of github.com:Coldcard/q1firmware into Q
This commit is contained in:
commit
92be45cdad
@ -189,7 +189,7 @@ Restore your seed words onto a new Coldcard.''' % num_fails
|
||||
ch = await ux_show_story(msg, title='I Am Brick!', escape='6')
|
||||
if ch == '6': break
|
||||
|
||||
async def confirm_attempt(self, attempts_left, num_fails, value):
|
||||
async def confirm_attempt(self, attempts_left, value):
|
||||
|
||||
ch = await ux_show_story('''You have %d attempts left before this Coldcard BRICKS \
|
||||
ITSELF FOREVER.
|
||||
@ -224,7 +224,7 @@ Press OK to continue, X to stop for now.
|
||||
|
||||
if pa.num_fails > 3:
|
||||
# they are approaching brickage, so warn them each attempt
|
||||
await self.confirm_attempt(pa.attempts_left, pa.num_fails, pin)
|
||||
await self.confirm_attempt(pa.attempts_left, pin)
|
||||
|
||||
dis.fullscreen("Loading...")
|
||||
pa.setup(pin)
|
||||
|
||||
@ -1801,10 +1801,11 @@ def load_export_and_verify_signature(microsd_path, virtdisk_path, verify_detache
|
||||
|
||||
@pytest.fixture
|
||||
def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_text, nfc_read_json,
|
||||
load_export_and_verify_signature, is_q1, press_cancel, press_select):
|
||||
load_export_and_verify_signature, is_q1, press_cancel, press_select, readback_bbqr,
|
||||
cap_screen_qr):
|
||||
def doit(way, label, is_json, sig_check=True, addr_fmt=AF_CLASSIC, ret_sig_addr=False,
|
||||
tail_check=None, sd_key=None, vdisk_key=None, nfc_key=None, ret_fname=False,
|
||||
fpattern=None):
|
||||
fpattern=None, qr_key=None):
|
||||
|
||||
s_label = None
|
||||
if label == "Address summary":
|
||||
@ -1814,6 +1815,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
"sd": sd_key or "1",
|
||||
"vdisk": vdisk_key or "2",
|
||||
"nfc": nfc_key or (KEY_NFC if is_q1 else "3"),
|
||||
"qr": qr_key or (KEY_QR if is_q1 else "4"),
|
||||
}
|
||||
time.sleep(0.2)
|
||||
title, story = cap_story()
|
||||
@ -1834,6 +1836,23 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
time.sleep(0.3)
|
||||
press_cancel() # exit NFC animation
|
||||
return nfc_export
|
||||
elif way == "qr":
|
||||
need_keypress(key_map["qr"])
|
||||
time.sleep(0.3)
|
||||
try:
|
||||
file_type, data = readback_bbqr()
|
||||
if file_type == "J":
|
||||
return json.loads(data)
|
||||
elif file_type == "U":
|
||||
return data
|
||||
else:
|
||||
raise NotImplementedError
|
||||
except:
|
||||
res = cap_screen_qr().decode('ascii')
|
||||
try:
|
||||
return json.loads(res)
|
||||
except:
|
||||
return res
|
||||
else:
|
||||
# virtual disk
|
||||
if f"({key_map['vdisk']}) to save to Virtual Disk" not in story:
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
# (c) Copyright 2024 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# to run it on both Mk4 and Q:
|
||||
# python login_settings_tests.py; sleep 10; python --Q login_settings_tests.py
|
||||
# pytest login_settings_tests.py; sleep 10; pytest --Q login_settings_tests.py
|
||||
#
|
||||
# or use test runner:
|
||||
# python run_sim_tests --login
|
||||
#
|
||||
# python run_sim_tests --q1 --login -k countdown --pdb
|
||||
#
|
||||
import pytest, time, pdb
|
||||
from charcodes import KEY_ENTER, KEY_DOWN, KEY_UP, KEY_HOME, KEY_DELETE
|
||||
from charcodes import KEY_ENTER, KEY_DOWN, KEY_UP, KEY_HOME
|
||||
from ckcc_protocol.client import ColdcardDevice, CCProtocolPacker, CKCC_SIMULATOR_PATH
|
||||
from run_sim_tests import ColdcardSimulator, clean_sim_data
|
||||
|
||||
@ -149,7 +154,7 @@ def _set_kill_key(device, val, is_Q):
|
||||
if is_Q:
|
||||
assert "press this key at any point during login" in story
|
||||
else:
|
||||
assert "press this key while the anti-phishing words are shown during login" in story
|
||||
assert "press this key while the anti- phishing words are shown during login" in story
|
||||
assert ("Best if this does not match the first number"
|
||||
" of the second half of your PIN.") in story
|
||||
|
||||
@ -167,9 +172,11 @@ def _remap_pin(pin, key_map):
|
||||
remap_pin += ch
|
||||
return remap_pin
|
||||
|
||||
def _login(device, pin, is_Q, scrambled=False, mk4_kbtn=None):
|
||||
def _login(device, pin, is_Q, scrambled=False, mk4_kbtn=None, num_failed=None):
|
||||
orig_pin = pin
|
||||
scr = _cap_screen(device)
|
||||
if num_failed:
|
||||
assert f"{num_failed} failures, {13-num_failed} tries left" in scr
|
||||
if is_Q:
|
||||
top = scr.split("\n")[0].split()
|
||||
is_scrambled = len(top) == 10
|
||||
@ -215,6 +222,7 @@ def _login(device, pin, is_Q, scrambled=False, mk4_kbtn=None):
|
||||
_need_keypress(device, ch)
|
||||
_press_select(device, is_Q)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nick", [100*"$", "$", 10*"20"+ " "+"8080"+ " " + "XX"+ " "+ "YY"])
|
||||
def test_set_nickname(nick, request):
|
||||
is_Q = request.config.getoption('--Q')
|
||||
@ -244,6 +252,7 @@ def test_set_nickname(nick, request):
|
||||
assert nick == target
|
||||
sim.stop()
|
||||
|
||||
|
||||
def test_randomize_pin_keys(request):
|
||||
is_Q = request.config.getoption('--Q')
|
||||
clean_sim_data() # remove all from previous
|
||||
@ -407,6 +416,64 @@ def test_terms_ok(request):
|
||||
sim.stop()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("brick", [True, False])
|
||||
def test_wrong_pin_input(request, brick):
|
||||
is_Q = request.config.getoption('--Q')
|
||||
clean_sim_data() # remove all from previous
|
||||
sim = ColdcardSimulator(args=["--early-usb", "--q1" if is_Q else "", "--pin", "22-22"])
|
||||
sim.start(start_wait=6)
|
||||
device = ColdcardDevice(sn=CKCC_SIMULATOR_PATH)
|
||||
time.sleep(.1)
|
||||
num_attmeptss = 13
|
||||
for ii, i in enumerate(range(31, 43), start=1):
|
||||
# pdb.set_trace()
|
||||
pin = f"{i}-{i}"
|
||||
scr_num_failed = (ii - 1) if ii > 1 else None
|
||||
_login(device, pin, is_Q, num_failed=scr_num_failed)
|
||||
time.sleep(.5)
|
||||
title, story = _cap_story(device)
|
||||
if ii > 4:
|
||||
assert title == "WARNING"
|
||||
assert pin in story # showing to user to double-check his input
|
||||
assert "BRICKS ITSELF FOREVER" in story
|
||||
assert f"{num_attmeptss - ii + 1} attempts left" in story
|
||||
_press_select(device, is_Q)
|
||||
time.sleep(.1)
|
||||
title, story = _cap_story(device)
|
||||
|
||||
assert "WRONG PIN" in title
|
||||
assert f"{num_attmeptss - ii} attempts left" in story
|
||||
assert f"{ii} failure" in story
|
||||
_press_select(device, is_Q)
|
||||
time.sleep(.1)
|
||||
|
||||
if brick:
|
||||
# one more wrong pin
|
||||
_login(device, "91-11", is_Q, num_failed=12)
|
||||
time.sleep(.5)
|
||||
title, story = _cap_story(device)
|
||||
assert "WARNING" == title
|
||||
_press_select(device, is_Q)
|
||||
time.sleep(.1)
|
||||
title, story = _cap_story(device)
|
||||
assert title == "I Am Brick!"
|
||||
assert "After 13 failed PIN attempts this Coldcard is locked forever" in story
|
||||
assert "no way to reset or recover the secure element" in story
|
||||
assert "forever inaccessible" in story
|
||||
assert "Restore your seed words onto a new Coldcard" in story
|
||||
else:
|
||||
_login(device, "22-22", is_Q, num_failed=12)
|
||||
time.sleep(.5)
|
||||
title, story = _cap_story(device)
|
||||
assert "WARNING" == title
|
||||
_press_select(device, is_Q)
|
||||
time.sleep(.1)
|
||||
m = _cap_menu(device)
|
||||
assert "Ready To Sign" in m
|
||||
|
||||
sim.stop()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("nick", [None, "In trust we trust NOT"])
|
||||
@pytest.mark.parametrize("randomize", [False, True])
|
||||
@pytest.mark.parametrize("login_ctdwn", [None, " 5 minutes", "15 minutes"])
|
||||
|
||||
@ -17,6 +17,7 @@ python run_sim_tests.py # same as with '-
|
||||
python run_sim_tests.py -m all --onetime --veryslow # run all tests (cca 252 minutes)
|
||||
python run_sim_tests.py -m test_multisig.py -k cosigning # run only tests that match expression from test_multisig.py
|
||||
python run_sim_tests.py -m test_export.py --pdb # run only export tests and attach debugger
|
||||
python run_sim_tests.py -m test_attended.py --q1 -w 6 --login # run attended test + all login tests
|
||||
|
||||
|
||||
Onetime/veryslow tests are completely separated form the rest of the test suite.
|
||||
@ -96,13 +97,11 @@ def is_ok(ec: ExitCode) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _run_tests_with_simulator(test_module: str, simulator_args: List[str], pytest_marks: str,
|
||||
pytest_k: str, pdb: bool, failed_first: bool, psbt2=False) -> ExitCode:
|
||||
sim = ColdcardSimulator(args=simulator_args)
|
||||
sim.start()
|
||||
time.sleep(1)
|
||||
def _run_pytest_tests(test_module: str, pytest_marks: str, pytest_k: str, pdb: bool,
|
||||
failed_first: bool, psbt2=False, is_Q=False) -> ExitCode:
|
||||
cmd_list = [
|
||||
"--cache-clear", "-m", pytest_marks, "--sim", test_module if test_module is not None else ""
|
||||
"--cache-clear", "-m", pytest_marks, "--sim",
|
||||
test_module if test_module is not None else ""
|
||||
]
|
||||
if pytest_k:
|
||||
cmd_list += ["-k", pytest_k]
|
||||
@ -112,28 +111,43 @@ def _run_tests_with_simulator(test_module: str, simulator_args: List[str], pytes
|
||||
cmd_list.append("--ff")
|
||||
if psbt2:
|
||||
cmd_list.append("--psbt2")
|
||||
if is_Q:
|
||||
cmd_list.insert(0, "--Q") # only changes behavior in login_settings_test
|
||||
|
||||
exit_code = pytest.main(cmd_list)
|
||||
sim.stop()
|
||||
time.sleep(1)
|
||||
clean_sim_data()
|
||||
return pytest.main(cmd_list)
|
||||
|
||||
def _run_coldcard_tests(test_module: str, simulator_args: List[str], pytest_marks: str,
|
||||
pytest_k: str, pdb: bool, failed_first: bool, psbt2=False,
|
||||
is_Q=False) -> ExitCode:
|
||||
if simulator_args:
|
||||
sim = ColdcardSimulator(args=simulator_args)
|
||||
sim.start()
|
||||
time.sleep(1)
|
||||
|
||||
exit_code = _run_pytest_tests(test_module, pytest_marks, pytest_k, pdb,
|
||||
failed_first, psbt2, is_Q)
|
||||
|
||||
if simulator_args:
|
||||
sim.stop()
|
||||
time.sleep(1)
|
||||
clean_sim_data()
|
||||
return exit_code
|
||||
|
||||
|
||||
def run_tests_with_simulator(test_module=None, simulator_args=None, pytest_k=None, pdb=False,
|
||||
failed_first=False, psbt2=False,
|
||||
pytest_marks="not onetime and not veryslow and not manual"):
|
||||
def run_coldcard_tests(test_module=None, simulator_args=None, pytest_k=None, pdb=False,
|
||||
failed_first=False, psbt2=False, is_Q=False,
|
||||
pytest_marks="not onetime and not veryslow and not manual"):
|
||||
failed = []
|
||||
exit_code = _run_tests_with_simulator(test_module, simulator_args, pytest_marks, pytest_k,
|
||||
pdb, failed_first, psbt2=psbt2)
|
||||
exit_code = _run_coldcard_tests(test_module, simulator_args, pytest_marks, pytest_k,
|
||||
pdb, failed_first, psbt2, is_Q)
|
||||
if not is_ok(exit_code):
|
||||
# no success, no nothing - give failed another try, each alone with its own simulator
|
||||
last_failed = get_last_failed()
|
||||
print("Running failed from last run", last_failed)
|
||||
exit_codes = []
|
||||
for failed_test in last_failed:
|
||||
exit_code_2 = _run_tests_with_simulator(failed_test, simulator_args, pytest_marks,
|
||||
pytest_k, pdb, failed_first, psbt2=psbt2)
|
||||
exit_code_2 = _run_coldcard_tests(failed_test, simulator_args, pytest_marks,
|
||||
pytest_k, pdb, failed_first, psbt2, is_Q)
|
||||
exit_codes.append(exit_code_2)
|
||||
if not is_ok(exit_code_2):
|
||||
failed.append(failed_test)
|
||||
@ -200,6 +214,8 @@ def main():
|
||||
parser.add_argument("--onetime", action="store_true", default=False,
|
||||
help="run tests marked as 'onetime'")
|
||||
parser.add_argument("--veryslow", action="store_true", default=False,
|
||||
help="run 'login_settings_tests.py'")
|
||||
parser.add_argument("--login", action="store_true", default=False,
|
||||
help="run tests marked as 'veryslow'")
|
||||
parser.add_argument("--collect", type=str, metavar="MARK",
|
||||
help="Collect marked test and print them to stdout")
|
||||
@ -216,7 +232,7 @@ def main():
|
||||
print(collect_marked_tests(args.collect))
|
||||
return
|
||||
|
||||
if args.module is None and (args.onetime is False and args.veryslow is False):
|
||||
if args.module is None and (args.onetime is False and args.veryslow is False and args.login is False):
|
||||
args.module = ["all"]
|
||||
|
||||
DEFAULT_SIMULATOR_ARGS = ["--eff", "--set", "nfc=1"]
|
||||
@ -262,9 +278,9 @@ def main():
|
||||
if args.q1 and '--q1' not in test_args:
|
||||
test_args.append('--q1')
|
||||
|
||||
ec, failed_tests = run_tests_with_simulator(test_module, simulator_args=test_args,
|
||||
pytest_k=args.pytest_k, pdb=args.pdb,
|
||||
failed_first=args.ff, psbt2=args.psbt2)
|
||||
ec, failed_tests = run_coldcard_tests(test_module, simulator_args=test_args,
|
||||
pytest_k=args.pytest_k, pdb=args.pdb,
|
||||
failed_first=args.ff, psbt2=args.psbt2)
|
||||
result.append((test_module, ec, failed_tests))
|
||||
print("Done", test_module)
|
||||
print(80 * "=")
|
||||
@ -272,10 +288,10 @@ def main():
|
||||
# run veryslow is specified
|
||||
if args.veryslow:
|
||||
print("started veryslow tests")
|
||||
ec, failed_tests = run_tests_with_simulator(test_module=None, pytest_marks="veryslow",
|
||||
pytest_k=args.pytest_k, pdb=args.pdb,
|
||||
simulator_args=DEFAULT_SIMULATOR_ARGS,
|
||||
failed_first=args.ff, psbt2=args.psbt2)
|
||||
ec, failed_tests = run_coldcard_tests(test_module=None, pytest_marks="veryslow",
|
||||
pytest_k=args.pytest_k, pdb=args.pdb,
|
||||
simulator_args=DEFAULT_SIMULATOR_ARGS,
|
||||
failed_first=args.ff, psbt2=args.psbt2)
|
||||
result.append(("veryslow", ec, failed_tests))
|
||||
|
||||
# run onetime is specified (each test against its own simulator)
|
||||
@ -283,12 +299,19 @@ def main():
|
||||
print("started onetime tests")
|
||||
onetime_tests = collect_marked_tests("onetime")
|
||||
for onetime_test in onetime_tests:
|
||||
ec, failed_tests = run_tests_with_simulator(test_module=onetime_test, pdb=args.pdb,
|
||||
failed_first=args.ff, pytest_marks="onetime",
|
||||
simulator_args=DEFAULT_SIMULATOR_ARGS,
|
||||
psbt2=args.psbt2)
|
||||
ec, failed_tests = run_coldcard_tests(test_module=onetime_test, pdb=args.pdb,
|
||||
failed_first=args.ff, pytest_marks="onetime",
|
||||
simulator_args=DEFAULT_SIMULATOR_ARGS,
|
||||
psbt2=args.psbt2)
|
||||
result.append((f"onetime: {onetime_test}", ec, failed_tests))
|
||||
|
||||
if args.login:
|
||||
print("start login settings tests")
|
||||
ec, failed_tests = run_coldcard_tests(test_module="login_settings_tests.py", pdb=args.pdb,
|
||||
failed_first=args.ff, pytest_k=args.pytest_k,
|
||||
is_Q=True if args.q1 else False)
|
||||
result.append((f"login_settings_tests", ec, failed_tests))
|
||||
|
||||
print("All done")
|
||||
|
||||
any_failed = False
|
||||
|
||||
@ -21,7 +21,7 @@ from charcodes import KEY_NFC
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('acct_num', [None, '0', '99', '123'])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"])
|
||||
def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home, cap_story,
|
||||
need_keypress, microsd_path, virtdisk_path, bitcoind_wallet, bitcoind_d_wallet,
|
||||
enter_number, nfc_read_text, load_export, bitcoind, press_select):
|
||||
@ -160,7 +160,7 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home,
|
||||
#assert x['hdkeypath'] == f"m/84'/1'/{acct_num}'/0/%d" % (len(addrs)-1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, press_select, microsd_path,
|
||||
nfc_read_json, virtdisk_path, testnet, use_mainnet, load_export):
|
||||
@ -199,7 +199,7 @@ def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, press_sel
|
||||
|
||||
@pytest.mark.parametrize('mode', [ "Classic P2PKH", "P2SH-Segwit", "Segwit P2WPKH"])
|
||||
@pytest.mark.parametrize('acct_num', [ None, '0', '9897'])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, cap_story, need_keypress,
|
||||
microsd_path, nfc_read_json, virtdisk_path, use_mainnet, testnet, load_export,
|
||||
@ -266,7 +266,7 @@ def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, ca
|
||||
|
||||
|
||||
@pytest.mark.parametrize('acct_num', [ None, '99', '1236'])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
@pytest.mark.parametrize('app', [
|
||||
("Generic JSON", "Generic Export"),
|
||||
@ -351,7 +351,7 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap
|
||||
else:
|
||||
assert False
|
||||
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
@pytest.mark.parametrize('acct_num', [None, '0', '99', '123'])
|
||||
def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_keypress, acct_num,
|
||||
@ -403,7 +403,7 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k
|
||||
assert node.hwif() == sk.hwif()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc", "qr"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_public_txt(way, dev, pick_menu_item, goto_home, press_select, microsd_path,
|
||||
addr_vs_path, virtdisk_path, nfc_read_text, cap_story, use_mainnet,
|
||||
@ -546,7 +546,7 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
press_cancel()
|
||||
|
||||
@pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"])
|
||||
@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc", "qr"])
|
||||
@pytest.mark.parametrize("addr_fmt", [AF_P2WPKH, AF_P2WPKH_P2SH, AF_CLASSIC])
|
||||
@pytest.mark.parametrize("acct_num", [None, 0, 1, (2 ** 31) - 1])
|
||||
@pytest.mark.parametrize("int_ext", [True, False])
|
||||
|
||||
Loading…
Reference in New Issue
Block a user