fix test_multisig.py; proper handling of buttons
This commit is contained in:
parent
a51734237a
commit
b3c3d2d21a
@ -311,7 +311,7 @@ Press (3) if you really understand and accept these risks.
|
||||
|
||||
# export options
|
||||
k0 = 'to show change addresses' if allow_change and change == 0 else None
|
||||
export_msg, escape = export_prompt_builder('address summary file',
|
||||
export_msg, escape = export_prompt_builder('Address summary file',
|
||||
no_qr=bool(ms_wallet), key0=k0)
|
||||
escape += KEY_PAGE_UP+KEY_PAGE_DOWN+KEY_HOME+KEY_LEFT+KEY_RIGHT
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ from utils import B2A, parse_addr_fmt_str
|
||||
from psbt import psbtObject, FatalPSBTIssue, FraudulentChangeOutput
|
||||
from exceptions import HSMDenied
|
||||
from version import MAX_TXN_LEN
|
||||
from charcodes import KEY_QR, KEY_NFC
|
||||
from charcodes import KEY_QR, KEY_NFC, KEY_ENTER, KEY_CANCEL
|
||||
|
||||
# Where in SPI flash/PSRAM the two PSBT files are (in and out)
|
||||
TXN_INPUT_OFFSET = 0
|
||||
@ -1206,7 +1206,7 @@ class NewPassphrase(UserAuthorizedAction):
|
||||
|
||||
title = "Passphrase"
|
||||
bypass_tmp = True
|
||||
escape = "x2"
|
||||
escape = "x2" + KEY_CANCEL
|
||||
while 1:
|
||||
msg = ('BIP-39 passphrase (%d chars long) has been provided over '
|
||||
'USB connection. Should we switch to that wallet now?\n\n')
|
||||
@ -1215,7 +1215,7 @@ class NewPassphrase(UserAuthorizedAction):
|
||||
msg += "Press (1) to add passphrase to currently active temporary seed. "
|
||||
|
||||
if settings.master_get("words", True):
|
||||
escape += "y"
|
||||
escape += "y" + KEY_ENTER
|
||||
msg += "Press OK to add passphrase to master seed. "
|
||||
|
||||
msg += ('Press (2) to view the provided passphrase.\n\n'
|
||||
@ -1233,7 +1233,7 @@ class NewPassphrase(UserAuthorizedAction):
|
||||
break
|
||||
|
||||
try:
|
||||
if ch not in 'y1':
|
||||
if ch not in 'y1'+ KEY_ENTER:
|
||||
# they don't want to!
|
||||
self.refused = True
|
||||
await ux_dramatic_pause("Refused.", 1)
|
||||
|
||||
@ -368,7 +368,7 @@ def _import_prompt_builder(title, no_qr, no_nfc):
|
||||
escape += "2"
|
||||
if (NFC is not None) and not no_nfc:
|
||||
if has_qwerty:
|
||||
prompt += ", press (NFC) to import via NFC"
|
||||
prompt += ", press " + KEY_NFC + " to import via NFC"
|
||||
escape += KEY_NFC
|
||||
else:
|
||||
prompt += ", press (3) to import via NFC"
|
||||
|
||||
@ -157,13 +157,14 @@ def need_keypress(dev, request):
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def enter_number(need_keypress, is_q1):
|
||||
def enter_number(need_keypress, press_select):
|
||||
def doit(number):
|
||||
number = str(number) if not isinstance(number, str) else number
|
||||
for d in number:
|
||||
need_keypress(d)
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
return doit
|
||||
|
||||
@ -183,24 +184,21 @@ def enter_hex(need_keypress, enter_text, is_q1):
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def enter_pin(enter_number, need_keypress, cap_screen, is_q1):
|
||||
def enter_pin(enter_number, press_select, cap_screen, is_q1):
|
||||
def doit(pin):
|
||||
assert '-' in pin
|
||||
a,b = pin.split('-')
|
||||
enter_number(a)
|
||||
|
||||
need_keypress(KEY_ENTER)
|
||||
scr = cap_screen().split('\n')
|
||||
if is_q1:
|
||||
words = [i.strip() for i in scr[7].split()]
|
||||
else:
|
||||
# capture words? hard to know in general what they should be tho
|
||||
words = scr[2:4]
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
enter_number(b)
|
||||
need_keypress(KEY_ENTER)
|
||||
|
||||
return words
|
||||
|
||||
@ -640,18 +638,54 @@ def get_secrets(sim_execfile):
|
||||
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope='module')
|
||||
def press_select(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_ENTER if has_qwerty else 'y')
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope='module')
|
||||
def press_cancel(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_CANCEL if has_qwerty else 'x')
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def press_delete(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_DELETE if has_qwerty else 'x')
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def press_nfc(need_keypress, has_qwerty):
|
||||
def doit(num=3):
|
||||
need_keypress(KEY_NFC if has_qwerty else str(num))
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def press_up(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_UP if has_qwerty else "5")
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def press_down(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_DOWN if has_qwerty else "8")
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def press_left(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_LEFT if has_qwerty else "7")
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def press_right(need_keypress, has_qwerty):
|
||||
def doit():
|
||||
need_keypress(KEY_RIGHT if has_qwerty else "9")
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def goto_home(cap_menu, press_cancel, press_select, pick_menu_item, has_qwerty, cap_screen):
|
||||
|
||||
@ -688,7 +722,7 @@ def goto_home(cap_menu, press_cancel, press_select, pick_menu_item, has_qwerty,
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def pick_menu_item(cap_menu, need_keypress, has_qwerty, cap_screen):
|
||||
def pick_menu_item(cap_menu, need_keypress, has_qwerty, cap_screen, press_select, press_up, press_down):
|
||||
WRAP_IF_OVER = 16 # see ../shared/menu.py
|
||||
|
||||
def doit(text):
|
||||
@ -706,18 +740,18 @@ def pick_menu_item(cap_menu, need_keypress, has_qwerty, cap_screen):
|
||||
if len(m) > WRAP_IF_OVER and m_pos > (len(m)//2):
|
||||
# use wrap around, work up from bottom
|
||||
for n in range(len(m) - m_pos):
|
||||
need_keypress(KEY_UP if has_qwerty else '5')
|
||||
press_up()
|
||||
time.sleep(.01) # required
|
||||
|
||||
need_keypress(KEY_ENTER if has_qwerty else 'y')
|
||||
press_select()
|
||||
time.sleep(.01) # required
|
||||
else:
|
||||
# go down
|
||||
for n in range(m_pos):
|
||||
need_keypress(KEY_DOWN if has_qwerty else '8')
|
||||
press_down()
|
||||
time.sleep(.01) # required
|
||||
|
||||
need_keypress(KEY_ENTER if has_qwerty else 'y')
|
||||
press_select()
|
||||
time.sleep(.01) # required
|
||||
|
||||
return doit
|
||||
@ -1568,10 +1602,10 @@ def nfc_write(request, needs_nfc, is_q1):
|
||||
# WRITE data into NFC "chip"
|
||||
def doit_usb(ccfile):
|
||||
sim_exec = request.getfixturevalue('sim_exec')
|
||||
need_keypress = request.getfixturevalue('need_keypress')
|
||||
press_select = request.getfixturevalue('press_select')
|
||||
rv = sim_exec('list(glob.NFC.big_write(%r))' % ccfile)
|
||||
if 'Traceback' in rv: raise pytest.fail(rv)
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y') # to end the animation and have it check value immediately
|
||||
press_select() # to end the animation and have it check value immediately
|
||||
|
||||
try:
|
||||
raise NotImplementedError
|
||||
@ -1753,14 +1787,14 @@ 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):
|
||||
load_export_and_verify_signature, is_q1, press_cancel, press_select):
|
||||
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):
|
||||
key_map = {
|
||||
"sd": sd_key or "1",
|
||||
"vdisk": vdisk_key or "2",
|
||||
"nfc": nfc_key or (KEY_NFC if is_q1 else "(3)"),
|
||||
"nfc": nfc_key or (KEY_NFC if is_q1 else "3"),
|
||||
}
|
||||
time.sleep(0.2)
|
||||
title, story = cap_story()
|
||||
@ -1769,7 +1803,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
need_keypress(key_map['sd'])
|
||||
|
||||
elif way == "nfc":
|
||||
if f"{key_map['nfc']} to share via NFC" not in story:
|
||||
if f"{key_map['nfc'] if is_q1 else '(3)'} to share via NFC" not in story:
|
||||
pytest.skip("NFC disabled")
|
||||
else:
|
||||
need_keypress(key_map['nfc'])
|
||||
@ -1779,7 +1813,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
else:
|
||||
nfc_export = nfc_read_text()
|
||||
time.sleep(0.3)
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # exit NFC animation
|
||||
press_cancel() # exit NFC animation
|
||||
return nfc_export
|
||||
else:
|
||||
# virtual disk
|
||||
@ -1815,7 +1849,7 @@ def load_export(need_keypress, cap_story, microsd_path, virtdisk_path, nfc_read_
|
||||
if is_json:
|
||||
export = json.loads(export)
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
|
||||
if ret_sig_addr and sig_addr:
|
||||
return export, sig_addr
|
||||
|
||||
@ -253,7 +253,10 @@ def sign_msg(key, msg, addr_fmt, b64 = False):
|
||||
return sig
|
||||
|
||||
def detruncate_address(s):
|
||||
_idx = s.index("↳")
|
||||
try:
|
||||
_idx = s.index("↳")
|
||||
except ValueError:
|
||||
_idx = -1
|
||||
if _idx != -1:
|
||||
s = s[_idx+1:]
|
||||
start, end = s.strip().split('⋯')
|
||||
|
||||
@ -12,11 +12,11 @@ from constants import msg_sign_unmap_addr_fmt
|
||||
|
||||
@pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'"])
|
||||
@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ])
|
||||
def test_show_addr_usb(dev, need_keypress, addr_vs_path, path, addr_fmt, is_simulator):
|
||||
def test_show_addr_usb(dev, press_select, addr_vs_path, path, addr_fmt, is_simulator):
|
||||
|
||||
addr = dev.send_recv(CCProtocolPacker.show_address(path, addr_fmt), timeout=None)
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
if "'" in path and not is_simulator():
|
||||
raise pytest.skip('we cant confirm hardened-derived keypaths')
|
||||
@ -28,7 +28,9 @@ def test_show_addr_usb(dev, need_keypress, addr_vs_path, path, addr_fmt, is_simu
|
||||
@pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'"])
|
||||
@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ])
|
||||
@pytest.mark.parametrize('show_qr', [ False, True ])
|
||||
def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, cap_story, show_qr, cap_screen_qr, qr_quality_check):
|
||||
def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt,
|
||||
cap_story, show_qr, cap_screen_qr, qr_quality_check,
|
||||
press_cancel):
|
||||
time.sleep(0.1)
|
||||
|
||||
addr = dev.send_recv(CCProtocolPacker.show_address(path, addr_fmt), timeout=None)
|
||||
@ -36,7 +38,7 @@ def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, c
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
|
||||
#need_keypress('x')
|
||||
#press_cancel()
|
||||
|
||||
# check expected addr was used
|
||||
addr_vs_path(addr, path, addr_fmt)
|
||||
@ -56,7 +58,7 @@ def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, c
|
||||
assert qr == addr or qr == addr.upper()
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
def test_addr_vs_bitcoind(use_regtest, need_keypress, dev, bitcoind_d_sim_sign):
|
||||
def test_addr_vs_bitcoind(use_regtest, press_select, dev, bitcoind_d_sim_sign):
|
||||
# check our p2wpkh wrapped in p2sh is right
|
||||
use_regtest()
|
||||
for i in range(5):
|
||||
@ -68,7 +70,7 @@ def test_addr_vs_bitcoind(use_regtest, need_keypress, dev, bitcoind_d_sim_sign):
|
||||
path = resp['hdkeypath']
|
||||
|
||||
addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2WPKH_P2SH), timeout=None)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
assert addr == core_addr
|
||||
|
||||
@pytest.mark.parametrize("body_err", [
|
||||
@ -91,8 +93,9 @@ def test_show_addr_nfc_invalid(body_err, goto_home, pick_menu_item, nfc_write_te
|
||||
|
||||
@pytest.mark.parametrize("path", ["m/84'/0'/0'/300/0", "m/800'", "m/0/0/0/0/1/1/1"])
|
||||
@pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh"])
|
||||
def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item, goto_home, cap_story,
|
||||
need_keypress, addr_vs_path):
|
||||
def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item,
|
||||
goto_home, cap_story, press_nfc, addr_vs_path, press_select, is_q1,
|
||||
cap_screen):
|
||||
# import pdb;pdb.set_trace()
|
||||
for _ in range(5):
|
||||
# need to wait for ApproveMessageSign to be popped from ux stack
|
||||
@ -115,17 +118,19 @@ def test_show_addr_nfc(path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_m
|
||||
nfc_write_text(body)
|
||||
time.sleep(0.5)
|
||||
_, story = cap_story()
|
||||
|
||||
split_story = story.split("\n\n")
|
||||
story_addr = split_story[0]
|
||||
story_path = split_story[1][2:] # remove "= "
|
||||
assert "Press (3) to share via NFC" in story
|
||||
if not is_q1:
|
||||
assert "Press (3) to share via NFC" in story
|
||||
assert story_path == path
|
||||
need_keypress("3") # share over NFC
|
||||
press_nfc() # share over NFC
|
||||
addr = nfc_read_text()
|
||||
if addr == body:
|
||||
# missed it - again
|
||||
addr = nfc_read_text()
|
||||
need_keypress("y") # exit NFC animation
|
||||
press_select() # exit NFC animation
|
||||
assert story_addr == addr
|
||||
addr_vs_path(addr, path, addr_fmt)
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ from pycoin.key.BIP32Node import BIP32Node
|
||||
from pycoin.contrib.segwit_addr import encode as sw_encode
|
||||
from pycoin.encoding import a2b_hashed_base58, hash160
|
||||
from helpers import detruncate_address
|
||||
from charcodes import KEY_QR, KEY_NFC, KEY_LEFT, KEY_RIGHT, KEY_ENTER, KEY_CANCEL
|
||||
from charcodes import KEY_QR, KEY_LEFT, KEY_RIGHT
|
||||
|
||||
@pytest.fixture
|
||||
def mk_common_derivations():
|
||||
@ -51,7 +51,7 @@ def parse_display_screen(cap_story, is_mark3):
|
||||
lines = body.split('\n')
|
||||
if start == 0:
|
||||
# no header after first page
|
||||
assert 'to save address summary file' in body
|
||||
assert 'to save Address summary file' in body
|
||||
assert 'show QR code' in body
|
||||
|
||||
assert lines[0] == 'Addresses %d..%d:' % (start, start + n - 1)
|
||||
@ -85,8 +85,9 @@ def validate_address():
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, microsd_path, is_q1,
|
||||
virtdisk_path, nfc_read_text, load_export_and_verify_signature):
|
||||
def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, microsd_path,
|
||||
virtdisk_path, nfc_read_text, load_export_and_verify_signature,
|
||||
press_select, press_nfc):
|
||||
# Generates the address file through the simulator, reads the file and
|
||||
# returns a list of tuples of the form (subpath, address)
|
||||
def doit(expected_qty=250, way="sd", change=False):
|
||||
@ -97,18 +98,18 @@ def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, mic
|
||||
if way == "sd":
|
||||
need_keypress('1')
|
||||
elif way == "vdisk":
|
||||
if "save to Virtual Disk." not in story:
|
||||
if "save to Virtual Disk" not in story:
|
||||
raise pytest.skip("Vdisk disabled")
|
||||
need_keypress("2")
|
||||
else:
|
||||
# NFC
|
||||
if "share via NFC" not in story:
|
||||
raise pytest.skip("NFC disabled")
|
||||
need_keypress("3" if not is_q1 else KEY_NFC)
|
||||
press_nfc()
|
||||
time.sleep(0.3)
|
||||
addresses = nfc_read_text()
|
||||
time.sleep(0.3)
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
# nfc just returns 10 addresses
|
||||
assert len(addresses.split("\n")) == 10
|
||||
raise pytest.xfail("PASSED - different export format for NFC")
|
||||
@ -135,7 +136,7 @@ def generate_addresses_file(goto_address_explorer, need_keypress, cap_story, mic
|
||||
|
||||
def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress,
|
||||
cap_menu, mk_common_derivations, pick_menu_item,
|
||||
parse_display_screen, validate_address, is_q1):
|
||||
parse_display_screen, validate_address, press_cancel):
|
||||
# For a given wallet, ensure the explorer shows the correct stub addresses
|
||||
node_prv = BIP32Node.from_wallet_key(
|
||||
sim_execfile('devtest/dump_private.py').strip()
|
||||
@ -167,7 +168,7 @@ def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress,
|
||||
start, end = detruncate_address(m[_id])
|
||||
assert expected_addr.startswith(start)
|
||||
assert expected_addr.endswith(end)
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
press_cancel()
|
||||
|
||||
@pytest.mark.parametrize("chain", ["BTC", "XRT", "XTN"])
|
||||
@pytest.mark.parametrize("change", [True, False])
|
||||
@ -219,7 +220,7 @@ def test_applications_samourai(chain, change, option, goto_address_explorer, cap
|
||||
])
|
||||
def test_address_display(goto_address_explorer, parse_display_screen, mk_common_derivations,
|
||||
need_keypress, sim_execfile, validate_address, press_seq, expected_n,
|
||||
expected_start, pick_menu_item, cap_menu, is_q1):
|
||||
expected_start, pick_menu_item, cap_menu, is_q1, press_cancel):
|
||||
# The proper addresses are displayed
|
||||
# given the sequence of keys pressed
|
||||
node_prv = BIP32Node.from_wallet_key(
|
||||
@ -248,7 +249,7 @@ def test_address_display(goto_address_explorer, parse_display_screen, mk_common_
|
||||
sk = node_prv.subkey_for_path(subpath[2:])
|
||||
validate_address(given_addr, sk)
|
||||
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # back
|
||||
press_cancel() # back
|
||||
|
||||
@pytest.mark.parametrize('click_idx', ["Classic P2PKH", "P2SH-Segwit", "Segwit P2WPKH"])
|
||||
@pytest.mark.parametrize("change", [True, False])
|
||||
@ -274,7 +275,8 @@ def test_dump_addresses(way, change, generate_addresses_file, mk_common_derivati
|
||||
def test_account_menu(way, account_num, sim_execfile, pick_menu_item,
|
||||
goto_address_explorer, need_keypress, cap_menu,
|
||||
mk_common_derivations, parse_display_screen,
|
||||
validate_address, generate_addresses_file, is_q1):
|
||||
validate_address, generate_addresses_file,
|
||||
press_cancel, press_select):
|
||||
# Try a few sub-accounts
|
||||
node_prv = BIP32Node.from_wallet_key(
|
||||
sim_execfile('devtest/dump_private.py').strip()
|
||||
@ -295,7 +297,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item,
|
||||
time.sleep(0.1)
|
||||
for d in str(account_num):
|
||||
need_keypress(d)
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
time.sleep(0.1)
|
||||
|
||||
m = cap_menu()
|
||||
@ -334,8 +336,8 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item,
|
||||
sk = node_prv.subkey_for_path(subpath[2:])
|
||||
validate_address(addr, sk)
|
||||
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
press_cancel()
|
||||
press_cancel()
|
||||
|
||||
# NOTE: (2**31)-1 = 0x7fff_ffff = 2147483647
|
||||
|
||||
@ -351,7 +353,7 @@ def test_account_menu(way, account_num, sim_execfile, pick_menu_item,
|
||||
def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address_explorer,
|
||||
need_keypress, cap_menu, parse_display_screen, validate_address, cap_story,
|
||||
cap_screen_qr, qr_quality_check, is_mark4plus, nfc_read_text, get_setting,
|
||||
press_select, is_q1):
|
||||
press_select, press_cancel, is_q1, press_nfc):
|
||||
|
||||
is_single = '{idx}' not in path
|
||||
|
||||
@ -449,13 +451,13 @@ def test_custom_path(path, which_fmt, addr_vs_path, pick_menu_item, goto_address
|
||||
|
||||
if is_mark4plus and get_setting('nfc', 0):
|
||||
# this is actually testing NFC export in qr code menu
|
||||
need_keypress(KEY_NFC if is_q1 else '3')
|
||||
press_nfc()
|
||||
time.sleep(.1)
|
||||
assert nfc_read_text() == addr
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # leave NFC animation
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # leave QR code display
|
||||
press_cancel() # leave NFC animation
|
||||
press_cancel() # leave QR code display
|
||||
# test NFC export in address explorer
|
||||
need_keypress(KEY_NFC if is_q1 else '3')
|
||||
press_nfc()
|
||||
time.sleep(.1)
|
||||
assert nfc_read_text() == addr
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ def bkpw(settings_set):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("last_saved", [True, False])
|
||||
def test_backup_refuse(last_saved, dev, need_keypress, bkpw):
|
||||
def test_backup_refuse(last_saved, dev, press_cancel, bkpw):
|
||||
time.sleep(0.050)
|
||||
|
||||
if last_saved:
|
||||
@ -34,8 +34,8 @@ def test_backup_refuse(last_saved, dev, need_keypress, bkpw):
|
||||
assert r is None
|
||||
|
||||
if last_saved:
|
||||
need_keypress("x")
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
press_cancel()
|
||||
|
||||
with pytest.raises(CCUserRefused):
|
||||
done = None
|
||||
@ -45,7 +45,7 @@ def test_backup_refuse(last_saved, dev, need_keypress, bkpw):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("last_saved", [True, False])
|
||||
def test_backup_accept(last_saved, dev, need_keypress, bkpw):
|
||||
def test_backup_accept(last_saved, dev, need_keypress, press_select, bkpw):
|
||||
time.sleep(0.050)
|
||||
if last_saved:
|
||||
bkpw()
|
||||
@ -54,7 +54,7 @@ def test_backup_accept(last_saved, dev, need_keypress, bkpw):
|
||||
r = dev.send_recv(CCProtocolPacker.start_backup())
|
||||
assert r is None
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
if last_saved:
|
||||
time.sleep(1) # needed
|
||||
done = dev.send_recv(CCProtocolPacker.get_backup_file(), timeout=5000)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import pytest, time, json, os, shutil, re
|
||||
from collections import OrderedDict
|
||||
from constants import simulator_fixed_words, simulator_fixed_tprv
|
||||
from charcodes import KEY_QR, KEY_ENTER, KEY_CANCEL
|
||||
from charcodes import KEY_QR
|
||||
from pycoin.key.BIP32Node import BIP32Node
|
||||
from mnemonic import Mnemonic
|
||||
|
||||
@ -30,7 +29,8 @@ def decode_backup(txt):
|
||||
@pytest.fixture
|
||||
def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
cap_story, need_keypress, cap_screen_qr, pass_word_quiz,
|
||||
get_setting, seed_story_to_words, is_q1):
|
||||
get_setting, seed_story_to_words, press_cancel, is_q1,
|
||||
press_select):
|
||||
def doit(reuse_pw=False, save_pw=False, st=None, ct=False):
|
||||
# st -> seed type
|
||||
# ct -> cleartext backup
|
||||
@ -54,27 +54,27 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
assert "A temporary seed is in effect" in body
|
||||
assert "so backup will be of that seed" in body
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
title, body = cap_story()
|
||||
|
||||
if ct:
|
||||
# cleartext backup
|
||||
if ' 1: zoo' in body:
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
press_cancel()
|
||||
|
||||
need_keypress("6")
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "Are you SURE ?!?" in story
|
||||
assert "**NOT** be encrypted" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
return # nothing more to be done
|
||||
|
||||
if reuse_pw:
|
||||
assert ' 1: zoo' in body
|
||||
assert '12: zoo' in body
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
words = ['zoo'] * 12
|
||||
|
||||
time.sleep(0.1)
|
||||
@ -94,7 +94,7 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
need_keypress(KEY_QR if is_q1 else '1')
|
||||
got_qr = cap_screen_qr().decode('ascii').lower().split()
|
||||
assert [w[0:4] for w in words] == got_qr
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# pass the quiz!
|
||||
count, title, body = pass_word_quiz(words)
|
||||
@ -107,7 +107,7 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
|
||||
assert get_setting('bkpw') == ' '.join(words)
|
||||
else:
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.01)
|
||||
assert get_setting('bkpw', 'xxx') == 'xxx'
|
||||
|
||||
@ -128,7 +128,8 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre
|
||||
cap_screen_qr, reuse_pw, save_pw, settings_set, settings_remove,
|
||||
generate_ephemeral_words, set_bip39_pw, verify_backup_file,
|
||||
check_and_decrypt_backup, restore_backup_cs, clear_ms, seedvault,
|
||||
restore_main_seed, import_ephemeral_xprv, backup_system):
|
||||
restore_main_seed, import_ephemeral_xprv, backup_system,
|
||||
press_cancel, press_select):
|
||||
# Make an encrypted 7z backup, verify it, and even restore it!
|
||||
clear_ms()
|
||||
reset_seed_words()
|
||||
@ -138,7 +139,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre
|
||||
# need to make multisig in my main wallet
|
||||
if multisig and st != "eph":
|
||||
import_ms_wallet(15, 15)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
assert len(get_setting('multisig')) == 1
|
||||
|
||||
@ -151,7 +152,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre
|
||||
if multisig:
|
||||
# make multisig in ephemeral wallet
|
||||
import_ms_wallet(15, 15, dev_key=True, common="605'/0'/0'")
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
assert len(get_setting('multisig')) == 1
|
||||
else:
|
||||
@ -193,7 +194,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre
|
||||
|
||||
assert bk_a == bk_b, "contents mismatch"
|
||||
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.01)
|
||||
|
||||
verify_backup_file(fn)
|
||||
@ -209,7 +210,7 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre
|
||||
assert "seeds" not in decrypted
|
||||
|
||||
for i in range(10):
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.01)
|
||||
|
||||
# test verify on device (CRC check)
|
||||
@ -220,11 +221,11 @@ def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypre
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stype", ["words12", "words24", "xprv"])
|
||||
def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home,
|
||||
def test_backup_ephemeral_wallet(stype, pick_menu_item, press_select, goto_home,
|
||||
cap_story, pass_word_quiz, get_setting,
|
||||
verify_backup_file, microsd_path, check_and_decrypt_backup,
|
||||
sim_execfile, unit_test, word_menu_entry, cap_menu,
|
||||
restore_backup_cs, generate_ephemeral_words,
|
||||
restore_backup_cs, generate_ephemeral_words, press_cancel,
|
||||
import_ephemeral_xprv, reset_seed_words, seed_story_to_words):
|
||||
reset_seed_words()
|
||||
goto_home()
|
||||
@ -243,11 +244,11 @@ def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home
|
||||
title, story = cap_story()
|
||||
assert "A temporary seed is in effect" in story
|
||||
assert "so backup will be of that seed" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
if "Use same backup file password as last time?" in story:
|
||||
need_keypress("x")
|
||||
press_cancel()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert title == 'NO-TITLE'
|
||||
@ -262,7 +263,7 @@ def test_backup_ephemeral_wallet(stype, pick_menu_item, need_keypress, goto_home
|
||||
assert count >= 4
|
||||
assert "same words next time" in body
|
||||
assert "Press (1) to save" in body
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.01)
|
||||
assert get_setting('bkpw', 'xxx') == 'xxx'
|
||||
title, story = cap_story()
|
||||
@ -308,7 +309,7 @@ def test_backup_bip39_wallet(passphrase, set_bip39_pw, pick_menu_item, need_keyp
|
||||
verify_backup_file, microsd_path, check_and_decrypt_backup,
|
||||
sim_execfile, unit_test, word_menu_entry, cap_menu,
|
||||
restore_backup_cs, seedvault, settings_set, reset_seed_words,
|
||||
seed_story_to_words):
|
||||
seed_story_to_words, press_cancel):
|
||||
reset_seed_words()
|
||||
goto_home()
|
||||
settings_set("seedvault", int(seedvault))
|
||||
@ -328,7 +329,7 @@ def test_backup_bip39_wallet(passphrase, set_bip39_pw, pick_menu_item, need_keyp
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
if "Use same backup file password as last time?" in story:
|
||||
need_keypress("x")
|
||||
press_cancel()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert title == 'NO-TITLE'
|
||||
@ -341,7 +342,7 @@ def test_backup_bip39_wallet(passphrase, set_bip39_pw, pick_menu_item, need_keyp
|
||||
assert count >= 4
|
||||
assert "same words next time" in body
|
||||
assert "Press (1) to save" in body
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.01)
|
||||
assert get_setting('bkpw', 'xxx') == 'xxx'
|
||||
title, story = cap_story()
|
||||
@ -374,7 +375,8 @@ def test_backup_bip39_wallet(passphrase, set_bip39_pw, pick_menu_item, need_keyp
|
||||
|
||||
|
||||
def test_trick_backups(goto_trick_menu, clear_all_tricks, repl, unit_test,
|
||||
new_trick_pin, new_pin_confirmed, pick_menu_item, need_keypress):
|
||||
new_trick_pin, new_pin_confirmed, pick_menu_item,
|
||||
press_select):
|
||||
|
||||
from test_se2 import TC_REBOOT, TC_BLANK_WALLET
|
||||
|
||||
@ -390,7 +392,7 @@ def test_trick_backups(goto_trick_menu, clear_all_tricks, repl, unit_test,
|
||||
new_trick_pin(pin, 'Duress Wallet', None)
|
||||
item = 'BIP-85 Wallet #%d' % (n % 4) if (n % 4 != 0) else 'Legacy Wallet'
|
||||
pick_menu_item(item)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
new_pin_confirmed(pin, item, None, None)
|
||||
|
||||
for pin, op_mode, expect, _, xflags in [
|
||||
@ -433,7 +435,7 @@ def test_trick_backups(goto_trick_menu, clear_all_tricks, repl, unit_test,
|
||||
|
||||
def test_seed_vault_backup(settings_set, reset_seed_words, generate_ephemeral_words,
|
||||
import_ephemeral_xprv, restore_main_seed, settings_get,
|
||||
repl, pick_menu_item, need_keypress, cap_story, get_setting,
|
||||
repl, pick_menu_item, press_cancel, cap_story, get_setting,
|
||||
pass_word_quiz, verify_backup_file, check_and_decrypt_backup,
|
||||
restore_backup_cs, cap_menu, verify_ephemeral_secret_ui,
|
||||
seed_story_to_words):
|
||||
@ -470,7 +472,7 @@ def test_seed_vault_backup(settings_set, reset_seed_words, generate_ephemeral_wo
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
if "Use same backup file password as last time?" in story:
|
||||
need_keypress("x")
|
||||
press_cancel()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert title == 'NO-TITLE'
|
||||
@ -483,7 +485,7 @@ def test_seed_vault_backup(settings_set, reset_seed_words, generate_ephemeral_wo
|
||||
assert count >= 4
|
||||
assert "same words next time" in body
|
||||
assert "Press (1) to save" in body
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.01)
|
||||
assert get_setting('bkpw', 'xxx') == 'xxx'
|
||||
title, story = cap_story()
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
# BBQr and secure notes.
|
||||
#
|
||||
|
||||
import pytest, os, time, random
|
||||
from helpers import B2A, prandom
|
||||
from binascii import b2a_hex, a2b_hex
|
||||
import pytest, time, random
|
||||
from helpers import prandom
|
||||
from binascii import a2b_hex
|
||||
from bbqr import split_qrs, join_qrs
|
||||
from charcodes import KEY_QR
|
||||
|
||||
@ -17,7 +17,7 @@ def THIS_FILE_requires_q1(is_q1):
|
||||
raise pytest.skip('Q1 only')
|
||||
|
||||
@pytest.fixture
|
||||
def readback_bbqr_ll(need_keypress, cap_screen_qr, sim_exec):
|
||||
def readback_bbqr_ll(cap_screen_qr, sim_exec):
|
||||
# low level version
|
||||
def doit():
|
||||
num_parts = None
|
||||
@ -130,7 +130,7 @@ def render_bbqr(need_keypress, cap_screen_qr, sim_exec, readback_bbqr_ll):
|
||||
return doit
|
||||
|
||||
@pytest.mark.parametrize('size', [ 1, 20, 990, 2060*2, 5000, 65537] )
|
||||
def test_show_bbqr_sizes(size, need_keypress, cap_screen_qr, sim_exec, render_bbqr):
|
||||
def test_show_bbqr_sizes(size, cap_screen_qr, sim_exec, render_bbqr):
|
||||
# test lengths
|
||||
data, parts = render_bbqr(str_expr=f"'a'*{size}", msg=f'Size {size}', file_type='U')
|
||||
|
||||
@ -144,7 +144,7 @@ def test_show_bbqr_sizes(size, need_keypress, cap_screen_qr, sim_exec, render_bb
|
||||
assert ft == 'U'
|
||||
|
||||
@pytest.mark.parametrize('src', [ 'rng', 'gpu', 'bigger'] )
|
||||
def test_show_bbqr_contents(src, need_keypress, cap_screen_qr, sim_exec, render_bbqr, load_shared_mod):
|
||||
def test_show_bbqr_contents(src, cap_screen_qr, sim_exec, render_bbqr, load_shared_mod):
|
||||
|
||||
args = dict(msg=f'Test {src}', file_type='B')
|
||||
if src == 'rng':
|
||||
@ -171,10 +171,10 @@ def test_show_bbqr_contents(src, need_keypress, cap_screen_qr, sim_exec, render_
|
||||
@pytest.mark.parametrize('max_ver', [ 20 ] ) # 20 max due to 4k USB buffer limit
|
||||
@pytest.mark.parametrize('encoding', '2HZ' )
|
||||
@pytest.mark.parametrize('partial', [False, True])
|
||||
def test_bbqr_psbt(size, encoding, max_ver, partial,
|
||||
need_keypress, scan_a_qr, readback_bbqr,
|
||||
cap_screen_qr, render_bbqr, goto_home, use_regtest, decode_psbt_with_bitcoind,
|
||||
decode_with_bitcoind, fake_txn, dev, cap_story, start_sign, end_sign):
|
||||
def test_bbqr_psbt(size, encoding, max_ver, partial, scan_a_qr, readback_bbqr,
|
||||
cap_screen_qr, render_bbqr, goto_home, use_regtest, cap_story,
|
||||
decode_psbt_with_bitcoind, decode_with_bitcoind, fake_txn, dev,
|
||||
start_sign, end_sign, press_cancel, press_select, need_keypress):
|
||||
|
||||
num_in = size
|
||||
num_out = size*10
|
||||
@ -210,7 +210,7 @@ def test_bbqr_psbt(size, encoding, max_ver, partial,
|
||||
raise pytest.fail('never saw it?')
|
||||
|
||||
# approve it
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
@ -232,6 +232,6 @@ def test_bbqr_psbt(size, encoding, max_ver, partial,
|
||||
assert len(decoded['vin']) == num_in
|
||||
assert len(decoded['vout']) == num_out
|
||||
|
||||
need_keypress('x') # back to menu
|
||||
press_cancel() # back to menu
|
||||
|
||||
# EOF
|
||||
|
||||
@ -13,14 +13,13 @@ import json
|
||||
from mnemonic import Mnemonic
|
||||
from constants import simulator_fixed_xfp, simulator_fixed_words, simulator_fixed_tprv
|
||||
from helpers import xfp2str
|
||||
from charcodes import KEY_ENTER
|
||||
|
||||
|
||||
# add the BIP39 test vectors
|
||||
vectors = json.load(open('bip39-vectors.json'))['english']
|
||||
|
||||
@pytest.mark.parametrize('vector', vectors)
|
||||
def test_b9p_vectors(dev, set_seed_words, need_keypress, vector, pw='RoZert'[::-1].upper()):
|
||||
def test_b9p_vectors(dev, set_seed_words, press_select, vector, pw='RoZert'[::-1].upper()):
|
||||
# Test all BIP-39 vectors. Slow.
|
||||
_, words, cooked, xprv = vector
|
||||
|
||||
@ -31,7 +30,7 @@ def test_b9p_vectors(dev, set_seed_words, need_keypress, vector, pw='RoZert'[::-
|
||||
|
||||
dev.send_recv(CCProtocolPacker.bip39_passphrase(pw), timeout=None)
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
xpub = None
|
||||
while xpub == None:
|
||||
@ -55,7 +54,7 @@ def test_b9p_basic(pw, set_bip39_pw):
|
||||
|
||||
@pytest.fixture()
|
||||
def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story,
|
||||
sim_execfile):
|
||||
sim_execfile, press_select):
|
||||
|
||||
def doit(pw, reset=True, seed_vault=False, on_tmp=False):
|
||||
# reset from previous runs
|
||||
@ -89,7 +88,7 @@ def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story,
|
||||
time.sleep(0.050)
|
||||
title, body = cap_story()
|
||||
assert pw in body
|
||||
need_keypress("y") # go back
|
||||
press_select() # go back
|
||||
|
||||
time.sleep(.1)
|
||||
title, body = cap_story()
|
||||
@ -97,7 +96,7 @@ def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story,
|
||||
assert "Press (1)" in body
|
||||
need_keypress("1")
|
||||
else:
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
|
||||
time.sleep(.3)
|
||||
title, story = cap_story()
|
||||
@ -108,15 +107,15 @@ def set_bip39_pw(dev, need_keypress, reset_seed_words, cap_story,
|
||||
title, story = cap_story()
|
||||
assert "Saved to Seed Vault" in story
|
||||
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
else:
|
||||
need_keypress("y") # do not store
|
||||
press_select() # do not store
|
||||
|
||||
time.sleep(.2)
|
||||
title, story = cap_story()
|
||||
|
||||
assert "Above is the master key fingerprint" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
|
||||
done = None
|
||||
while done is None:
|
||||
@ -149,12 +148,12 @@ def test_b39_fails(dev, pw):
|
||||
with pytest.raises(CCProtoError):
|
||||
dev.send_recv(CCProtocolPacker.bip39_passphrase(pw), timeout=None)
|
||||
|
||||
def test_b39p_refused(dev, need_keypress, pw='testing 123'):
|
||||
def test_b39p_refused(dev, press_cancel, pw='testing 123'):
|
||||
# user can refuse the passphrase (cancel)
|
||||
|
||||
dev.send_recv(CCProtocolPacker.bip39_passphrase(pw), timeout=None)
|
||||
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
with pytest.raises(CCUserRefused):
|
||||
done = None
|
||||
@ -163,17 +162,17 @@ def test_b39p_refused(dev, need_keypress, pw='testing 123'):
|
||||
done = dev.send_recv(CCProtocolPacker.get_passphrase_done(), timeout=None)
|
||||
|
||||
|
||||
def test_cancel_on_empty_added_numbers(pick_menu_item, goto_home, need_keypress,
|
||||
cap_menu, is_q1):
|
||||
def test_cancel_on_empty_added_numbers(pick_menu_item, goto_home, press_select,
|
||||
cap_menu, is_q1, press_cancel):
|
||||
if is_q1:
|
||||
# there is no Enter Number dialog on Q1
|
||||
pytest.skip("'Enter Number' not available on Q1")
|
||||
|
||||
goto_home()
|
||||
pick_menu_item('Passphrase')
|
||||
need_keypress("y") # intro story
|
||||
press_select() # intro story
|
||||
pick_menu_item('Add Numbers')
|
||||
need_keypress("x") # do not add any numbers and cancel with x
|
||||
press_cancel() # do not add any numbers and cancel with x
|
||||
pick_menu_item('CANCEL')
|
||||
time.sleep(0.1)
|
||||
m = cap_menu()
|
||||
@ -182,9 +181,9 @@ def test_cancel_on_empty_added_numbers(pick_menu_item, goto_home, need_keypress,
|
||||
|
||||
@pytest.mark.parametrize('stype', ["bip39pw", "words", "xprv", None])
|
||||
def test_lockdown(stype, pick_menu_item, set_bip39_pw, goto_home, cap_story,
|
||||
need_keypress, sim_exec, get_settings, reset_seed_words,
|
||||
press_cancel, sim_exec, get_settings, reset_seed_words,
|
||||
get_setting, generate_ephemeral_words, import_ephemeral_xprv,
|
||||
is_q1):
|
||||
press_select, is_q1):
|
||||
# test UX and operation of the 'seed lockdown' option
|
||||
if stype:
|
||||
if stype == "bip39pw":
|
||||
@ -211,13 +210,13 @@ def test_lockdown(stype, pick_menu_item, set_bip39_pw, goto_home, cap_story,
|
||||
assert 'Are you SURE' in where
|
||||
else:
|
||||
assert 'do not have an active temporary seed' in story
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
return
|
||||
|
||||
# real code does reboot, which is poorly simulated; avoid that
|
||||
sim_exec('import callgate; callgate.show_logout = lambda x:0')
|
||||
# commit change
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
time.sleep(0.25)
|
||||
|
||||
@ -239,7 +238,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
|
||||
need_keypress, pick_menu_item, goto_home,
|
||||
reset_seed_words, goto_eph_seed_menu, stype,
|
||||
enter_complex, cap_story, cap_menu, on_eph,
|
||||
settings_set, seed_vault, is_q1):
|
||||
settings_set, seed_vault, press_select):
|
||||
passphrase = "@coinkite rulez!!"
|
||||
reset_seed_words()
|
||||
settings_set("seedvault", 1)
|
||||
@ -267,7 +266,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
|
||||
return
|
||||
|
||||
pick_menu_item("Passphrase")
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
enter_complex(passphrase)
|
||||
pick_menu_item("APPLY")
|
||||
time.sleep(.1)
|
||||
@ -303,7 +302,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
|
||||
assert master_fp == title_xfp
|
||||
assert f"master seed [{sim_fp}]" in story
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
|
||||
time.sleep(.3)
|
||||
title, story = cap_story()
|
||||
@ -315,9 +314,9 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
|
||||
assert "Saved to Seed Vault" in story
|
||||
assert title_xfp in story
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
else:
|
||||
need_keypress(KEY_ENTER if is_q1 else "y") # do not store
|
||||
press_select() # do not store
|
||||
|
||||
if seed_vault:
|
||||
# check correct meta in seed vault
|
||||
@ -331,7 +330,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
|
||||
pytest.fail("not in menu")
|
||||
|
||||
# choose first info item in submenu
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert title_xfp in story
|
||||
@ -343,7 +342,7 @@ def test_bip39pass_on_ephemeral_seed(generate_ephemeral_words, import_ephemeral_
|
||||
|
||||
@pytest.mark.parametrize("stype", ["words", "xprv", "b39pw"])
|
||||
def test_bip39pass_on_ephemeral_seed_usb(generate_ephemeral_words, import_ephemeral_xprv,
|
||||
need_keypress, pick_menu_item, goto_home,
|
||||
pick_menu_item, goto_home,
|
||||
reset_seed_words, goto_eph_seed_menu, stype,
|
||||
cap_story, cap_menu, set_bip39_pw,
|
||||
get_identity_story, settings_set):
|
||||
@ -384,7 +383,7 @@ def test_bip39pass_on_ephemeral_seed_usb(generate_ephemeral_words, import_epheme
|
||||
def test_tmp_on_xprv_master(generate_ephemeral_words, goto_home, cap_menu,
|
||||
pick_menu_item, need_keypress, enter_complex,
|
||||
cap_story, unit_test, microsd_path, expect_ftux,
|
||||
set_bip39_pw, usb):
|
||||
set_bip39_pw, usb, press_select):
|
||||
passphrase = "jfkdsfdks"
|
||||
fname = "ek.txt"
|
||||
fpath = microsd_path("ek.txt")
|
||||
@ -399,7 +398,7 @@ def test_tmp_on_xprv_master(generate_ephemeral_words, goto_home, cap_menu,
|
||||
if "Press (1)" in story:
|
||||
need_keypress("1")
|
||||
|
||||
need_keypress("y") # Select file containing...
|
||||
press_select() # Select file containing...
|
||||
pick_menu_item(fname)
|
||||
time.sleep(.2)
|
||||
expect_ftux()
|
||||
@ -421,7 +420,7 @@ def test_tmp_on_xprv_master(generate_ephemeral_words, goto_home, cap_menu,
|
||||
return
|
||||
|
||||
pick_menu_item("Passphrase")
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
enter_complex(passphrase)
|
||||
pick_menu_item("APPLY")
|
||||
time.sleep(.1)
|
||||
@ -430,11 +429,11 @@ def test_tmp_on_xprv_master(generate_ephemeral_words, goto_home, cap_menu,
|
||||
|
||||
assert parent_fp in title # no choice story
|
||||
assert "current active temporary seed" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
time.sleep(.2)
|
||||
title, story = cap_story()
|
||||
if "Press (1)" in story:
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
|
||||
m = cap_menu()
|
||||
assert "Passphrase" not in m
|
||||
|
||||
@ -53,7 +53,7 @@ def goto_pin_options(pick_menu_item, goto_home):
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def my_enter_pin(cap_screen, need_keypress, is_q1):
|
||||
def my_enter_pin(cap_screen, need_keypress, is_q1, press_right, press_select):
|
||||
def doit(pin):
|
||||
time.sleep(.01) # required?
|
||||
scr = cap_screen().split('\n')
|
||||
@ -67,7 +67,7 @@ def my_enter_pin(cap_screen, need_keypress, is_q1):
|
||||
time.sleep(.1)
|
||||
|
||||
# move second part
|
||||
need_keypress(KEY_RIGHT)
|
||||
press_right()
|
||||
time.sleep(.1)
|
||||
scr = cap_screen().split('\n')
|
||||
|
||||
@ -79,7 +79,7 @@ def my_enter_pin(cap_screen, need_keypress, is_q1):
|
||||
need_keypress(n)
|
||||
time.sleep(.1)
|
||||
|
||||
need_keypress(KEY_ENTER)
|
||||
press_select()
|
||||
|
||||
else:
|
||||
assert scr[2] == 'Enter PIN Prefix'
|
||||
@ -91,20 +91,20 @@ def my_enter_pin(cap_screen, need_keypress, is_q1):
|
||||
continue
|
||||
|
||||
if ch == '-':
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1) # required
|
||||
|
||||
scr = cap_screen().split('\n')
|
||||
|
||||
assert ('Recognize these?' in scr) or ('Write these down:' in scr)
|
||||
words = scr[2:4]
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.1) # required
|
||||
scr = cap_screen().split('\n')
|
||||
assert scr[-1] == 'Enter rest of PIN'
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(0.1)
|
||||
return title, words
|
||||
@ -113,7 +113,7 @@ def my_enter_pin(cap_screen, need_keypress, is_q1):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def change_pin(cap_screen, cap_story, cap_menu, need_keypress, my_enter_pin):
|
||||
def change_pin(cap_screen, cap_story, cap_menu, press_select, my_enter_pin, press_cancel):
|
||||
def doit(old_pin, new_pin, hdr_text, expect_fail=None):
|
||||
# use standard menus and UX to change a PIN
|
||||
title, story = cap_story()
|
||||
@ -121,7 +121,7 @@ def change_pin(cap_screen, cap_story, cap_menu, need_keypress, my_enter_pin):
|
||||
assert "changing the main PIN used to unlock your Coldcard" in story
|
||||
assert "ABSOLUTELY NO WAY TO RECOVER A FORGOTTEN PIN!" in story
|
||||
assert "Write it down" in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(0.01) # required
|
||||
|
||||
assert max(len(i) for i in new_pin.split('-')) <= 6
|
||||
@ -147,7 +147,7 @@ def change_pin(cap_screen, cap_story, cap_menu, need_keypress, my_enter_pin):
|
||||
title, story = cap_story()
|
||||
assert title == 'Try Again'
|
||||
assert expect_fail in story
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
return
|
||||
|
||||
# saving/verifying can take tens of seconds.
|
||||
@ -168,7 +168,7 @@ def change_pin(cap_screen, cap_story, cap_menu, need_keypress, my_enter_pin):
|
||||
return doit
|
||||
|
||||
@pytest.mark.parametrize('new_pin', ['77-77', '123456-654321', '79-654321', '123456-12'])
|
||||
def test_main_pin(goto_pin_options, pick_menu_item, cap_story, cap_screen, need_keypress,
|
||||
def test_main_pin(goto_pin_options, pick_menu_item, cap_story, cap_screen,
|
||||
change_pin, new_pin, verify_pin_set, under_duress):
|
||||
goto_pin_options()
|
||||
pick_menu_item("Change Main PIN")
|
||||
|
||||
@ -7,7 +7,7 @@ import pytest
|
||||
from binascii import a2b_hex, b2a_hex
|
||||
from base64 import b64encode
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
from helpers import prandom
|
||||
|
||||
|
||||
from mnemonic import Mnemonic
|
||||
wordlist = Mnemonic('english').wordlist
|
||||
@ -142,6 +142,6 @@ def test_urldecode(url, sim_exec):
|
||||
result = sim_exec(cmd)
|
||||
|
||||
assert result == unquote_plus(url)
|
||||
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
@ -17,8 +17,9 @@ EXAMPLE_XPRV = '011b67969d1ec69bdfeeae43213da8460ba34b92d0788c8f7bfcfa44906e8a58
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def derive_bip85_secret(goto_home, need_keypress, pick_menu_item, cap_story, enter_text,
|
||||
set_encoded_secret, set_seed_words, settings_set, seed_story_to_words, is_q1):
|
||||
def derive_bip85_secret(goto_home, press_select, pick_menu_item, cap_story, enter_text,
|
||||
set_encoded_secret, set_seed_words, settings_set, is_q1,
|
||||
seed_story_to_words):
|
||||
def doit(mode, index, expect=None, entropy=None, sim_sec=None, chain="BTC"):
|
||||
if sim_sec:
|
||||
if len(sim_sec.split(" ")) in (12,18,24):
|
||||
@ -41,11 +42,11 @@ def derive_bip85_secret(goto_home, need_keypress, pick_menu_item, cap_story, ent
|
||||
assert 'seed value' in story
|
||||
assert 'other wallet systems' in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
if "You have a temporary seed active - deriving from temporary" in story:
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
|
||||
time.sleep(0.1)
|
||||
pick_menu_item(mode)
|
||||
@ -183,7 +184,7 @@ def activate_bip85_ephemeral(need_keypress, cap_story, sim_exec, reset_seed_word
|
||||
])
|
||||
def test_bip_vectors(mode, index, entropy, expect, cap_story, need_keypress,
|
||||
load_export_and_verify_signature, derive_bip85_secret,
|
||||
activate_bip85_ephemeral):
|
||||
activate_bip85_ephemeral, press_select, press_cancel):
|
||||
|
||||
do_import, story = derive_bip85_secret(mode, index, expect, entropy, sim_sec=EXAMPLE_XPRV)
|
||||
|
||||
@ -196,7 +197,7 @@ def test_bip_vectors(mode, index, entropy, expect, cap_story, need_keypress,
|
||||
title, story = cap_story()
|
||||
contents,_ = load_export_and_verify_signature(story, "sd", fpattern="drv", label=None)
|
||||
assert contents.strip() == msg.strip()
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
|
||||
@ -205,7 +206,7 @@ def test_bip_vectors(mode, index, entropy, expect, cap_story, need_keypress,
|
||||
else:
|
||||
assert 'show QR code' in story
|
||||
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
|
||||
@pytest.mark.qrcode
|
||||
@ -221,7 +222,7 @@ 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, seed_story_to_words,
|
||||
derive_bip85_secret, reset_seed_words, is_q1):
|
||||
derive_bip85_secret, reset_seed_words, is_q1, press_cancel):
|
||||
reset_seed_words()
|
||||
# Uses any key on Simulator; just checking for operation + entropy level
|
||||
_, story = derive_bip85_secret(mode, index)
|
||||
@ -256,7 +257,7 @@ def test_path_index(mode, pattern, index, need_keypress, cap_screen_qr, seed_sto
|
||||
|
||||
if index == 0:
|
||||
assert 'show QR code' in story
|
||||
need_keypress('4' if not is_q1 else KEY_QR)
|
||||
need_keypress(KEY_QR if is_q1 else '4')
|
||||
|
||||
qr = cap_screen_qr().decode('ascii')
|
||||
|
||||
@ -275,19 +276,18 @@ def test_path_index(mode, pattern, index, need_keypress, cap_screen_qr, seed_sto
|
||||
elif 'WIF' in mode:
|
||||
assert qr == got
|
||||
|
||||
need_keypress("x")
|
||||
press_cancel()
|
||||
|
||||
|
||||
def test_type_passwords(dev, cap_menu, pick_menu_item,
|
||||
goto_home, cap_story, need_keypress, cap_screen, enter_text
|
||||
):
|
||||
def test_type_passwords(dev, cap_menu, pick_menu_item, goto_home,
|
||||
cap_story, press_select, cap_screen, enter_text):
|
||||
goto_home()
|
||||
pick_menu_item('Settings')
|
||||
pick_menu_item('Keyboard EMU')
|
||||
_, story = cap_story()
|
||||
story1 = "This mode adds a top-level menu item for typing deterministically-generated passwords (BIP-85), directly into an attached USB computer (as an emulated keyboard)."
|
||||
assert story1 == story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
pick_menu_item('Enable')
|
||||
time.sleep(0.3)
|
||||
goto_home()
|
||||
@ -308,7 +308,7 @@ def test_type_passwords(dev, cap_menu, pick_menu_item,
|
||||
assert path == f"m/83696968'/707764'/21'/{index}'"
|
||||
assert len(pwd) == 21
|
||||
assert "=" not in pwd
|
||||
need_keypress("y") # does nothing on simulator
|
||||
press_select() # does nothing on simulator
|
||||
time.sleep(0.2)
|
||||
|
||||
# exit Enter Password menu
|
||||
|
||||
@ -11,7 +11,7 @@ from txn import fake_txn
|
||||
from test_ux import word_menu_entry
|
||||
from pycoin.key.BIP32Node import BIP32Node
|
||||
from helpers import xfp2str, a2b_hex
|
||||
from charcodes import KEY_CLEAR, KEY_NFC, KEY_ENTER, KEY_DOWN, KEY_DELETE, KEY_SHIFT, KEY_CANCEL
|
||||
from charcodes import KEY_CLEAR, KEY_NFC
|
||||
|
||||
|
||||
WORDLISTS = {
|
||||
@ -33,10 +33,10 @@ SEEDVAULT_TEST_DATA = [
|
||||
]
|
||||
|
||||
@pytest.fixture
|
||||
def seed_vault_enable(cap_story, pick_menu_item, need_keypress, goto_home,
|
||||
settings_set, is_q1):
|
||||
def seed_vault_enable(cap_story, pick_menu_item, press_select, goto_home,
|
||||
settings_set):
|
||||
def doit(enable=True):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Danger Zone")
|
||||
@ -44,7 +44,7 @@ def seed_vault_enable(cap_story, pick_menu_item, need_keypress, goto_home,
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
if "Enable Seed Vault?" in story:
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
|
||||
if enable:
|
||||
pick_menu_item("Enable")
|
||||
@ -53,7 +53,7 @@ def seed_vault_enable(cap_story, pick_menu_item, need_keypress, goto_home,
|
||||
time.sleep(.2)
|
||||
_, story = cap_story()
|
||||
if "Please remove all seeds from the vault" in story:
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
settings_set("seeds", [])
|
||||
pick_menu_item("Seed Vault")
|
||||
time.sleep(.1)
|
||||
@ -90,9 +90,9 @@ def ephemeral_seed_disabled_ui(cap_menu):
|
||||
|
||||
@pytest.fixture
|
||||
def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story,
|
||||
nfc_read_text, seed_story_to_words, is_q1):
|
||||
nfc_read_text, seed_story_to_words, press_nfc, press_select):
|
||||
def doit(nfc=False):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Danger Zone")
|
||||
@ -102,18 +102,18 @@ def get_seed_value_ux(goto_home, pick_menu_item, need_keypress, cap_story,
|
||||
title, body = cap_story()
|
||||
assert ('Are you SURE' in body) or ('Are you SURE' in title)
|
||||
assert 'can control all funds' in body
|
||||
need_keypress(confirm) # skip warning
|
||||
press_select() # skip warning
|
||||
time.sleep(0.01)
|
||||
title, story = cap_story()
|
||||
|
||||
if nfc:
|
||||
need_keypress("1") # show QR code
|
||||
time.sleep(.2)
|
||||
need_keypress(KEY_NFC if is_q1 else "3") # any QR can be exported via NFC
|
||||
press_nfc() # any QR can be exported via NFC
|
||||
time.sleep(.2)
|
||||
str_words = nfc_read_text()
|
||||
time.sleep(.5)
|
||||
need_keypress(confirm) # exit NFC animation
|
||||
press_select() # exit NFC animation
|
||||
return str_words.split(" ") # always truncated
|
||||
|
||||
return seed_story_to_words(story)
|
||||
@ -160,7 +160,7 @@ def goto_eph_seed_menu(goto_home, pick_menu_item, cap_story, need_keypress):
|
||||
|
||||
@pytest.fixture
|
||||
def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
need_keypress, settings_slots, is_q1):
|
||||
need_keypress, settings_slots, press_select):
|
||||
|
||||
def doit(preserve_settings=False, seed_vault=False):
|
||||
if seed_vault:
|
||||
@ -174,7 +174,6 @@ def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
pick_menu_item("Restore Master")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
ch = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
assert "Restore main wallet and its settings?" in story
|
||||
if seed_vault:
|
||||
@ -184,10 +183,12 @@ def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
assert "Press OK to forget current temporary seed " in story
|
||||
assert "settings, or press (1) to save & keep " in story
|
||||
assert "those settings if same seed is later restored." in story
|
||||
if preserve_settings:
|
||||
ch = "1"
|
||||
|
||||
need_keypress(ch)
|
||||
if preserve_settings and not seed_vault:
|
||||
need_keypress("1")
|
||||
else:
|
||||
press_select()
|
||||
|
||||
time.sleep(.3)
|
||||
|
||||
menu = cap_menu()
|
||||
@ -204,9 +205,9 @@ def restore_main_seed(goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def confirm_tmp_seed(need_keypress, cap_story, is_q1):
|
||||
def confirm_tmp_seed(need_keypress, cap_story, press_select):
|
||||
def doit(seedvault=False, expect_xfp=None):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
time.sleep(0.3)
|
||||
title, story = cap_story()
|
||||
if "Press (1) to store temporary seed into Seed Vault" in story:
|
||||
@ -218,9 +219,9 @@ def confirm_tmp_seed(need_keypress, cap_story, is_q1):
|
||||
if expect_xfp is not None:
|
||||
assert expect_xfp in story
|
||||
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
else:
|
||||
need_keypress(confirm) # do not store
|
||||
press_select() # do not store
|
||||
|
||||
time.sleep(.2)
|
||||
title, story = cap_story()
|
||||
@ -231,7 +232,7 @@ def confirm_tmp_seed(need_keypress, cap_story, is_q1):
|
||||
expect_xfp = title[1:-1]
|
||||
|
||||
assert "New temporary master key is in effect now." in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
return expect_xfp
|
||||
|
||||
return doit
|
||||
@ -239,7 +240,7 @@ def confirm_tmp_seed(need_keypress, cap_story, is_q1):
|
||||
|
||||
@pytest.fixture
|
||||
def seed_vault_delete(pick_menu_item, need_keypress, cap_menu, cap_story,
|
||||
goto_home, is_q1):
|
||||
goto_home, press_select):
|
||||
def doit(xfp, wipe=True):
|
||||
# delete it from records
|
||||
goto_home()
|
||||
@ -261,7 +262,7 @@ def seed_vault_delete(pick_menu_item, need_keypress, cap_menu, cap_story,
|
||||
assert xfp in title
|
||||
assert "press (1)" in story
|
||||
if wipe:
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
else:
|
||||
# preserve settings - remove just from seed vaul
|
||||
need_keypress("1")
|
||||
@ -277,9 +278,9 @@ def seed_vault_delete(pick_menu_item, need_keypress, cap_menu, cap_story,
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn,
|
||||
def verify_ephemeral_secret_ui(cap_story, press_select, cap_menu, dev, fake_txn,
|
||||
get_identity_story, try_sign, get_seed_value_ux,
|
||||
pick_menu_item, goto_home, is_q1):
|
||||
pick_menu_item, goto_home):
|
||||
def doit(mnemonic=None, xpub=None, expected_xfp=None, seed_vault=False,
|
||||
testnet=True):
|
||||
|
||||
@ -331,14 +332,14 @@ def verify_ephemeral_secret_ui(cap_story, need_keypress, cap_menu, dev, fake_txn
|
||||
assert e_master_xpub == xpub
|
||||
psbt = fake_txn(2, 2, master_xpub=e_master_xpub, segwit_in=True)
|
||||
try_sign(psbt, accept=True, finalize=True) # MUST NOT raise
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
return in_effect_xfp
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item, is_q1,
|
||||
def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item, press_select,
|
||||
need_keypress, cap_story, settings_set, seed_story_to_words,
|
||||
ephemeral_seed_disabled_ui, confirm_tmp_seed):
|
||||
def doit(num_words, dice=False, from_main=False, seed_vault=None, testnet=True):
|
||||
@ -372,7 +373,7 @@ def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item, is_q1,
|
||||
assert len(e_seed_words) == num_words
|
||||
|
||||
need_keypress("6") # skip quiz
|
||||
need_keypress(KEY_ENTER if is_q1 else "y") # yes - I'm sure
|
||||
press_select() # yes - I'm sure
|
||||
confirm_tmp_seed(seedvault=seed_vault)
|
||||
|
||||
return e_seed_words
|
||||
@ -384,7 +385,7 @@ def generate_ephemeral_words(goto_eph_seed_menu, pick_menu_item, is_q1,
|
||||
def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu,
|
||||
pick_menu_item, need_keypress, cap_story, settings_set,
|
||||
nfc_write_text, ephemeral_seed_disabled_ui, confirm_tmp_seed,
|
||||
is_q1):
|
||||
press_nfc, press_select, is_q1):
|
||||
def doit(way, extended_key=None, testnet=True, seed_vault=False, from_main=False):
|
||||
from pycoin.key.BIP32Node import BIP32Node
|
||||
if testnet:
|
||||
@ -427,10 +428,10 @@ def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu,
|
||||
if "Press (1) to import extended private key file from SD Card" in story:
|
||||
need_keypress("1")
|
||||
elif way == "nfc":
|
||||
if f"press ({'NFC' if is_q1 else '3'}) to import via NFC" not in story:
|
||||
if f"press {KEY_NFC if is_q1 else '(3)'} to import via NFC" not in story:
|
||||
pytest.xfail("NFC disabled")
|
||||
else:
|
||||
need_keypress(KEY_NFC if is_q1 else "3")
|
||||
press_nfc()
|
||||
time.sleep(0.2)
|
||||
nfc_write_text(ek)
|
||||
time.sleep(0.3)
|
||||
@ -445,7 +446,7 @@ def import_ephemeral_xprv(microsd_path, virtdisk_path, goto_eph_seed_menu,
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Select file containing the extended private key" in story
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
pick_menu_item(fname)
|
||||
|
||||
confirm_tmp_seed(expect_xfp=node.fingerprint().hex().upper(),
|
||||
@ -543,8 +544,8 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
nfc_write_text, tapsigner_encrypted_backup, seed_vault,
|
||||
preserve_settings, seed_vault_enable, settings_set,
|
||||
seed_vault_delete, restore_main_seed, confirm_tmp_seed,
|
||||
is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
is_q1, press_select, press_nfc):
|
||||
|
||||
reset_seed_words()
|
||||
if testnet:
|
||||
netcode = "XTN"
|
||||
@ -567,10 +568,10 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
if "Press (1) to import TAPSIGNER encrypted backup file from SD Card" in story:
|
||||
need_keypress("1")
|
||||
elif way == "nfc":
|
||||
if f"press ({'NFC' if is_q1 else '3'}) to import via NFC" not in story:
|
||||
if f"press {KEY_NFC if is_q1 else '(3)'} to import via NFC" not in story:
|
||||
pytest.xfail("NFC disabled")
|
||||
else:
|
||||
need_keypress(KEY_NFC if is_q1 else "3")
|
||||
press_nfc()
|
||||
time.sleep(0.2)
|
||||
nfc_write_text(fname)
|
||||
time.sleep(0.3)
|
||||
@ -585,14 +586,14 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick TAPSIGNER encrypted backup file" in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "your TAPSIGNER" in story
|
||||
assert "back of the card" in story
|
||||
need_keypress(confirm) # yes I have backup key
|
||||
press_select() # yes I have backup key
|
||||
enter_hex(backup_key_hex)
|
||||
|
||||
confirm_tmp_seed(expect_xfp=node.fingerprint().hex().upper(),
|
||||
@ -607,12 +608,13 @@ def test_ephemeral_seed_import_tapsigner(way, testnet, pick_menu_item, cap_story
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fail", ["wrong_key", "key_len", "plaintext", "garbage"])
|
||||
def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, is_q1, cap_screen,
|
||||
def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, cap_screen,
|
||||
need_keypress, reset_seed_words, enter_hex,
|
||||
tapsigner_encrypted_backup, goto_eph_seed_menu,
|
||||
microsd_path, ephemeral_seed_disabled, settings_set):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
microsd_path, ephemeral_seed_disabled, is_q1,
|
||||
settings_set, press_select, press_cancel):
|
||||
|
||||
|
||||
reset_seed_words()
|
||||
settings_set("seedvault", None)
|
||||
fail_msg = "Decryption failed - wrong key?"
|
||||
@ -636,13 +638,13 @@ def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, i
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick TAPSIGNER encrypted backup file" in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Press OK to continue X to cancel." in story
|
||||
need_keypress(confirm) # yes I have backup key
|
||||
press_select() # yes I have backup key
|
||||
if fail == "wrong_key":
|
||||
backup_key_hex = os.urandom(16).hex()
|
||||
if fail == "key_len":
|
||||
@ -654,14 +656,14 @@ def test_ephemeral_seed_import_tapsigner_fail(pick_menu_item, cap_story, fail, i
|
||||
|
||||
if fail == "key_len" and is_q1:
|
||||
assert "Need 32 char" in cap_screen()
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
return
|
||||
|
||||
title, story = cap_story()
|
||||
assert title == "FAILURE"
|
||||
assert fail_msg in story
|
||||
need_keypress(cancel)
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
press_cancel()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data", [
|
||||
@ -680,8 +682,8 @@ def test_ephemeral_seed_import_tapsigner_real(data, pick_menu_item, cap_story, m
|
||||
need_keypress, reset_seed_words, enter_hex,
|
||||
goto_eph_seed_menu, verify_ephemeral_secret_ui,
|
||||
ephemeral_seed_disabled, settings_set,
|
||||
confirm_tmp_seed, restore_main_seed, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
confirm_tmp_seed, restore_main_seed, press_select):
|
||||
|
||||
fname, backup_key_hex, pub = data
|
||||
fpath = microsd_path(fname)
|
||||
shutil.copy(f"data/{fname}", fpath)
|
||||
@ -699,13 +701,13 @@ def test_ephemeral_seed_import_tapsigner_real(data, pick_menu_item, cap_story, m
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick TAPSIGNER encrypted backup file" in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert "Press OK to continue X to cancel." in story
|
||||
need_keypress(confirm) # yes I have backup key
|
||||
press_select() # yes I have backup key
|
||||
enter_hex(backup_key_hex)
|
||||
confirm_tmp_seed(seedvault=False)
|
||||
verify_ephemeral_secret_ui(xpub=pub)
|
||||
@ -739,7 +741,7 @@ def test_ephemeral_seed_import_xprv(way, testnet, reset_seed_words,
|
||||
@pytest.mark.parametrize("seed_vault", [True, False])
|
||||
def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu,
|
||||
ephemeral_seed_disabled, cap_story,
|
||||
pick_menu_item, need_keypress,
|
||||
pick_menu_item, press_select,
|
||||
word_menu_entry, settings_set,
|
||||
seed_vault, seed_vault_enable,
|
||||
confirm_tmp_seed, is_q1):
|
||||
@ -769,17 +771,18 @@ def test_activate_current_tmp_secret(reset_seed_words, goto_eph_seed_menu,
|
||||
assert "Temporary master key already in use" in story
|
||||
assert title == "FAILED"
|
||||
assert in_effect_xfp == expected_xfp
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('data', SEEDVAULT_TEST_DATA)
|
||||
def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_menu_item,
|
||||
need_keypress, cap_story, cap_menu, reset_seed_words,
|
||||
get_identity_story, get_seed_value_ux, fake_txn, try_sign,
|
||||
sim_exec, goto_home, seed_vault_enable, is_q1, enter_text):
|
||||
sim_exec, goto_home, seed_vault_enable, is_q1, enter_text,
|
||||
press_select, press_cancel, press_delete):
|
||||
# Verify "seed vault" feature works as intended
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
|
||||
|
||||
|
||||
reset_seed_words()
|
||||
xfp, entropy, mnemonic = data
|
||||
@ -818,13 +821,13 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert ('%d words' % (6 * (vlen // 8))) in story
|
||||
else:
|
||||
assert 'xprv' in story
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
|
||||
# rename
|
||||
pick_menu_item("Rename")
|
||||
if not is_q1:
|
||||
for _ in range(len(xfp) + 1): # [xfp]
|
||||
need_keypress(KEY_DELETE if is_q1 else "x")
|
||||
press_delete()
|
||||
|
||||
# below should yield AAAA
|
||||
need_keypress("1")
|
||||
@ -832,7 +835,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
need_keypress("9") # next char
|
||||
need_keypress("1") # letters
|
||||
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
else:
|
||||
need_keypress(KEY_CLEAR)
|
||||
enter_text('AAAA')
|
||||
@ -841,7 +844,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert m[0] == "AAAA"
|
||||
|
||||
# check parent menu - must be updated too
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
m = cap_menu()
|
||||
for item in m:
|
||||
if "AAAA" in item:
|
||||
@ -850,13 +853,13 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert False
|
||||
|
||||
# go back
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item("Use This Seed")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert xfp in title
|
||||
assert 'temporary master key is in effect now' in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
active = get_seed_value_ux()
|
||||
if mnemonic:
|
||||
assert active == mnemonic.split()
|
||||
@ -877,7 +880,7 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
assert e_master_xpub != simulator_fixed_tpub
|
||||
psbt = fake_txn(2, 2, master_xpub=e_master_xpub, segwit_in=True)
|
||||
try_sign(psbt, accept=True, finalize=True) # MUST NOT raise
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
|
||||
encoded = sim_exec('from pincodes import pa; RV.write(repr(pa.fetch()))')
|
||||
assert 'Error' not in encoded
|
||||
@ -900,11 +903,11 @@ def test_seed_vault_menus(dev, data, settings_set, master_settings_get, pick_men
|
||||
|
||||
|
||||
def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu_item,
|
||||
need_keypress, cap_story, reset_seed_words, fake_txn,
|
||||
generate_ephemeral_words, goto_home, get_secrets, master_settings_get,
|
||||
cap_story, reset_seed_words, fake_txn, master_settings_get,
|
||||
generate_ephemeral_words, goto_home, get_secrets,
|
||||
import_ephemeral_xprv, set_bip39_pw, restore_main_seed,
|
||||
restore_seed_xor, derive_bip85_secret, activate_bip85_ephemeral,
|
||||
seed_vault_enable, is_q1):
|
||||
seed_vault_enable, is_q1, press_select, press_down):
|
||||
# Capture seeds by all the different paths and verify correct values are captured.
|
||||
# - BIP-85 -> 12, 24 words
|
||||
# - BIP-85 -> xprv (BIP-32)
|
||||
@ -913,7 +916,7 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
# - Capture a BIP-39 passphrase into words
|
||||
# - Trick pin -> duress wallet * 4 options
|
||||
# Then, verify those can all co-exist and be recalled correctly.
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
|
||||
reset_seed_words()
|
||||
seed_vault_enable(True)
|
||||
@ -976,8 +979,8 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
xfp, encoded_sec, name, meta = obj
|
||||
pick_menu_item("Seed Vault")
|
||||
for _ in range(i):
|
||||
need_keypress(KEY_DOWN if is_q1 else "8") # go down
|
||||
need_keypress(confirm)
|
||||
press_down() # go down
|
||||
press_select()
|
||||
pick_menu_item('Use This Seed')
|
||||
time.sleep(0.1)
|
||||
|
||||
@ -985,7 +988,7 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
assert 'New temporary master key' in story
|
||||
assert 'power down' not in story
|
||||
assert xfp in title
|
||||
need_keypress(confirm) # confirm activation of ephemeral secret
|
||||
press_select() # confirm activation of ephemeral secret
|
||||
|
||||
assert xfp2str(settings_get('xfp')) == xfp
|
||||
|
||||
@ -1005,11 +1008,8 @@ def test_seed_vault_captures(request, dev, settings_set, settings_get, pick_menu
|
||||
def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item,
|
||||
generate_ephemeral_words, import_ephemeral_xprv,
|
||||
goto_home, cap_story, cap_menu, restore_main_seed,
|
||||
need_keypress, seed_vault_enable, is_q1, do_keypresses):
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
delete = KEY_DELETE if is_q1 else "x"
|
||||
k_down = KEY_DOWN if is_q1 else "8"
|
||||
need_keypress, seed_vault_enable, is_q1, do_keypresses,
|
||||
press_select, press_cancel, press_down, press_delete):
|
||||
reset_seed_words()
|
||||
seed_vault_enable(True)
|
||||
settings_set("seeds", [])
|
||||
@ -1036,7 +1036,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
# we are no longer in ephemral
|
||||
assert "Restore Master" not in m
|
||||
# first entry in menu
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
m = cap_menu()
|
||||
assert "Rename" in m
|
||||
assert "Use This Seed" in m # we are in master - so this must be there
|
||||
@ -1044,16 +1044,16 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
|
||||
# delete entry 0
|
||||
pick_menu_item("Delete")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert len(m) == 3
|
||||
|
||||
# first entry again
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item("Rename")
|
||||
for _ in range(11):
|
||||
need_keypress(delete)
|
||||
press_delete()
|
||||
if is_q1:
|
||||
do_keypresses("AA")
|
||||
else:
|
||||
@ -1062,7 +1062,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
need_keypress("1")
|
||||
# name changed to AA
|
||||
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
|
||||
m = cap_menu()
|
||||
assert m[0] == "AA"
|
||||
@ -1071,20 +1071,20 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
assert "Delete" in m
|
||||
|
||||
# go back
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
press_cancel()
|
||||
# second item
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
press_down()
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
pick_menu_item("Use This Seed")
|
||||
title, _ = cap_story()
|
||||
need_keypress(confirm) # confirm new eph
|
||||
press_select() # confirm new eph
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m[0] == title
|
||||
pick_menu_item("Seed Vault")
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
press_down()
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "Rename" in m
|
||||
@ -1093,7 +1093,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
|
||||
pick_menu_item("Rename")
|
||||
for _ in range(11):
|
||||
need_keypress(delete)
|
||||
press_delete()
|
||||
|
||||
if is_q1:
|
||||
do_keypresses("AAA")
|
||||
@ -1104,13 +1104,13 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
need_keypress("9")
|
||||
need_keypress("1")
|
||||
# name changed to AAA
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m[0] == "AAA"
|
||||
pick_menu_item("Delete")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
# after we delete from seed vault together with its settings
|
||||
@ -1121,17 +1121,17 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
m = cap_menu()
|
||||
assert len(m) == 2
|
||||
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
press_down()
|
||||
press_select()
|
||||
pick_menu_item("Use This Seed")
|
||||
title, _ = cap_story()
|
||||
need_keypress(confirm) # confirm new eph
|
||||
press_select() # confirm new eph
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert m[0] == title
|
||||
pick_menu_item("Seed Vault")
|
||||
need_keypress(k_down)
|
||||
need_keypress(confirm)
|
||||
press_down()
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "Rename" in m
|
||||
@ -1144,7 +1144,7 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
m = cap_menu()
|
||||
assert len(m) == 3
|
||||
assert "Add current tmp" in m
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
# this is now different eph - modification not allowed
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
@ -1159,8 +1159,8 @@ def test_seed_vault_modifications(settings_set, reset_seed_words, pick_menu_item
|
||||
|
||||
|
||||
def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
cap_story, need_keypress, pick_menu_item, cap_menu,
|
||||
seed_vault_enable, is_q1):
|
||||
cap_story, press_cancel, pick_menu_item, cap_menu,
|
||||
seed_vault_enable):
|
||||
|
||||
node = BIP32Node.from_master_secret(os.urandom(32), netcode="XTN")
|
||||
xfp = node.fingerprint().hex().upper()
|
||||
@ -1191,7 +1191,7 @@ def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
sm = cap_menu()
|
||||
assert "Seed In Use" in sm
|
||||
assert "Use This Seed" not in sm
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x") # go back
|
||||
press_cancel() # go back
|
||||
pick_menu_item(m[0])
|
||||
time.sleep(.1)
|
||||
sm = cap_menu()
|
||||
@ -1202,8 +1202,8 @@ def test_xfp_collision(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
@pytest.mark.parametrize("refuse", [False, True])
|
||||
def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xprv,
|
||||
goto_home, pick_menu_item, cap_story, cap_menu,
|
||||
need_keypress, verify_ephemeral_secret_ui,
|
||||
seed_vault_enable, refuse, is_q1):
|
||||
press_cancel, verify_ephemeral_secret_ui,
|
||||
seed_vault_enable, refuse, press_select):
|
||||
ADD_MI = "Add current tmp"
|
||||
|
||||
reset_seed_words()
|
||||
@ -1233,14 +1233,14 @@ def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xpr
|
||||
assert xfp in title
|
||||
assert "Add to Seed Vault?" in story
|
||||
if refuse:
|
||||
need_keypress(KEY_CANCEL if is_q1 else "x")
|
||||
press_cancel()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert ADD_MI in m
|
||||
for mi in m:
|
||||
assert xfp not in mi
|
||||
else:
|
||||
need_keypress(KEY_ENTER if is_q1 else "y")
|
||||
press_select()
|
||||
verify_ephemeral_secret_ui(xpub=node.hwif(), seed_vault=True)
|
||||
|
||||
|
||||
@ -1248,13 +1248,13 @@ def test_add_current_active(reset_seed_words, settings_set, import_ephemeral_xpr
|
||||
@pytest.mark.parametrize('seedvault', [False, True])
|
||||
@pytest.mark.parametrize('data', SEEDVAULT_TEST_DATA)
|
||||
def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_setting,
|
||||
data, need_keypress, cap_story, set_encoded_secret,
|
||||
reset_seed_words, check_and_decrypt_backup, is_q1,
|
||||
data, press_select, cap_story, set_encoded_secret,
|
||||
reset_seed_words, check_and_decrypt_backup,
|
||||
goto_eph_seed_menu, pick_menu_item, word_menu_entry,
|
||||
verify_ephemeral_secret_ui, seedvault, settings_set,
|
||||
seed_vault_enable, confirm_tmp_seed, settings_path,
|
||||
seed_vault_delete, restore_main_seed, set_seed_words):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
|
||||
xfp_str, encoded_str, mnemonic = data
|
||||
if mnemonic:
|
||||
set_seed_words(mnemonic)
|
||||
@ -1266,7 +1266,7 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se
|
||||
|
||||
if multisig:
|
||||
import_ms_wallet(15, 15, dev_key=True)
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
assert len(get_setting('multisig')) == 1
|
||||
|
||||
@ -1288,7 +1288,7 @@ def test_temporary_from_backup(multisig, backup_system, import_ms_wallet, get_se
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
if "Select file containing the backup" in story:
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
pick_menu_item(fname)
|
||||
|
||||
@ -1349,9 +1349,10 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
ephemeral_seed_disabled, pick_menu_item, goto_home,
|
||||
need_keypress, word_menu_entry, settings_set,
|
||||
confirm_tmp_seed, cap_menu, microsd_path,
|
||||
restore_main_seed, get_identity_story, is_q1):
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
restore_main_seed, get_identity_story, press_select,
|
||||
press_cancel):
|
||||
|
||||
|
||||
reset_seed_words()
|
||||
|
||||
goto_eph_seed_menu()
|
||||
@ -1369,7 +1370,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
title, story = cap_story()
|
||||
assert "FAILED" == title
|
||||
assert 'Cannot use master seed as temporary.' in story
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
|
||||
# go to ephemeral seed and then try to create new ephemeral seed from master
|
||||
# when in different temporary seed whatsoever
|
||||
@ -1379,7 +1380,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
pick_menu_item("Generate Words")
|
||||
pick_menu_item(f"12 Words")
|
||||
need_keypress("6") # skip quiz
|
||||
need_keypress(confirm) # yes - I'm sure
|
||||
press_select() # yes - I'm sure
|
||||
confirm_tmp_seed(seedvault=False)
|
||||
|
||||
goto_home()
|
||||
@ -1397,7 +1398,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
title, story = cap_story()
|
||||
assert "FAILED" == title
|
||||
assert 'Cannot use master seed as temporary.' in story
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
|
||||
# now import same seed but represented as master extended key
|
||||
# this works and does not delete master settings as encoded
|
||||
@ -1412,7 +1413,7 @@ def test_import_master_as_tmp(reset_seed_words, goto_eph_seed_menu, cap_story,
|
||||
if "Press (1)" in story:
|
||||
need_keypress("1")
|
||||
|
||||
need_keypress(confirm) # Select file containing...
|
||||
press_select() # Select file containing...
|
||||
pick_menu_item(fname)
|
||||
confirm_tmp_seed(seedvault=False) # allowed
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ from helpers import xfp2str, slip132undo
|
||||
from conftest import simulator_fixed_xfp, simulator_fixed_tprv, simulator_fixed_words
|
||||
from ckcc_protocol.constants import AF_CLASSIC, AF_P2WPKH
|
||||
from pprint import pprint
|
||||
from charcodes import KEY_ENTER, KEY_CANCEL, KEY_NFC
|
||||
from charcodes import KEY_NFC
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@ -24,7 +24,7 @@ from charcodes import KEY_ENTER, KEY_CANCEL, KEY_NFC
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
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, is_q1):
|
||||
enter_number, nfc_read_text, load_export, bitcoind, press_select):
|
||||
# test UX and operation of the 'bitcoin core' wallet export
|
||||
from pycoin.contrib.segwit_addr import encode as sw_encode
|
||||
use_regtest()
|
||||
@ -47,7 +47,7 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home,
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
export = load_export(way, label="Bitcoin Core", is_json=False, addr_fmt=AF_P2WPKH)
|
||||
fp = io.StringIO(export).readlines()
|
||||
@ -162,8 +162,8 @@ def test_export_core(way, dev, use_regtest, acct_num, pick_menu_item, goto_home,
|
||||
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path,
|
||||
nfc_read_json, virtdisk_path, testnet, use_mainnet, load_export, is_q1):
|
||||
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):
|
||||
# test UX and operation of the 'wasabi wallet export'
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -178,7 +178,7 @@ def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keyp
|
||||
title, story = cap_story()
|
||||
|
||||
assert 'This saves a skeleton Wasabi' in story
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
obj = load_export(way, label="Wasabi wallet", is_json=True, addr_fmt=AF_P2WPKH)
|
||||
|
||||
@ -203,7 +203,7 @@ def test_export_wasabi(way, dev, pick_menu_item, goto_home, cap_story, need_keyp
|
||||
@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,
|
||||
is_q1):
|
||||
press_select):
|
||||
# lightly test electrum wallet export
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -231,7 +231,7 @@ def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, ca
|
||||
for n in acct_num:
|
||||
need_keypress(n)
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
time.sleep(0.1)
|
||||
pick_menu_item(mode)
|
||||
@ -275,7 +275,7 @@ def test_export_electrum(way, dev, mode, acct_num, pick_menu_item, goto_home, ca
|
||||
])
|
||||
def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap_story, need_keypress,
|
||||
microsd_path, nfc_read_json, virtdisk_path, addr_vs_path, enter_number,
|
||||
load_export, testnet, use_mainnet, is_q1):
|
||||
load_export, testnet, use_mainnet, press_select):
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
|
||||
@ -298,7 +298,7 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
obj = load_export(way, label=app_f_name, is_json=True, addr_fmt=AF_CLASSIC)
|
||||
|
||||
@ -356,7 +356,7 @@ def test_export_coldcard(way, dev, acct_num, app, pick_menu_item, goto_home, cap
|
||||
@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,
|
||||
microsd_path, nfc_read_json, virtdisk_path, testnet, enter_number,
|
||||
load_export, settings_set, use_mainnet, is_q1):
|
||||
load_export, settings_set, use_mainnet, press_select):
|
||||
# test UX and operation of the 'unchained export'
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -378,7 +378,7 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
obj = load_export(way, label="Unchained", is_json=True, sig_check=False)
|
||||
|
||||
@ -405,9 +405,9 @@ def test_export_unchained(way, dev, pick_menu_item, goto_home, cap_story, need_k
|
||||
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_public_txt(way, dev, pick_menu_item, goto_home, need_keypress, microsd_path,
|
||||
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,
|
||||
load_export, testnet, is_q1):
|
||||
load_export, testnet):
|
||||
# test UX and values produced.
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
@ -421,7 +421,7 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, need_keypress, m
|
||||
title, story = cap_story()
|
||||
|
||||
assert 'Saves a text file' in story
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
contents = load_export(way, label="Summary", is_json=False, addr_fmt=AF_CLASSIC)
|
||||
fp = io.StringIO(contents).readlines()
|
||||
@ -470,12 +470,9 @@ def test_export_public_txt(way, dev, pick_menu_item, goto_home, need_keypress, m
|
||||
@pytest.mark.parametrize('use_nfc', [False, True])
|
||||
def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home,
|
||||
cap_story, need_keypress, enter_number, cap_screen_qr,
|
||||
use_mainnet, nfc_read_text, is_q1):
|
||||
use_mainnet, nfc_read_text, is_q1, press_select, press_cancel,
|
||||
press_nfc):
|
||||
# XPUB's via QR
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
k_nfc = KEY_NFC if is_q1 else "3"
|
||||
|
||||
use_mainnet()
|
||||
|
||||
goto_home()
|
||||
@ -502,9 +499,9 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
if is_xfp:
|
||||
got = cap_screen_qr().decode('ascii')
|
||||
if use_nfc:
|
||||
need_keypress(k_nfc)
|
||||
press_nfc()
|
||||
assert got == xfp2str(simulator_fixed_xfp).upper()
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
continue
|
||||
|
||||
time.sleep(0.3)
|
||||
@ -525,16 +522,16 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
|
||||
expect = expect.format(acct=0)
|
||||
if not use_nfc:
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
got_pub = cap_screen_qr().decode('ascii')
|
||||
else:
|
||||
assert f'Press {KEY_NFC if is_q1 else "(3)"}' in story
|
||||
assert 'NFC' in story
|
||||
need_keypress(k_nfc)
|
||||
press_nfc()
|
||||
time.sleep(0.2)
|
||||
got_pub = nfc_read_text()
|
||||
time.sleep(0.1)
|
||||
#need_keypress(confirm)
|
||||
#press_select()
|
||||
|
||||
if got_pub[0] not in 'xt':
|
||||
got_pub,*_ = slip132undo(got_pub)
|
||||
@ -546,7 +543,7 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
wallet = wallet.subkey_for_path(expect[2:])
|
||||
assert got.sec() == wallet.sec()
|
||||
|
||||
need_keypress(cancel)
|
||||
press_cancel()
|
||||
|
||||
@pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"])
|
||||
@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"])
|
||||
@ -555,8 +552,8 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
|
||||
@pytest.mark.parametrize("int_ext", [True, False])
|
||||
def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, settings_set, need_keypress,
|
||||
pick_menu_item, way, cap_story, cap_menu, nfc_read_text, int_ext,
|
||||
microsd_path, settings_get, virtdisk_path, load_export, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
microsd_path, settings_get, virtdisk_path, load_export, press_select):
|
||||
|
||||
|
||||
settings_set('chain', chain)
|
||||
chain_num = 1 if chain in ["XTN", "XRT"] else 0
|
||||
@ -576,16 +573,16 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting
|
||||
need_keypress("1") # chosse account number
|
||||
for ch in str(acct_num):
|
||||
need_keypress(ch) # input num
|
||||
need_keypress(confirm) # confirm selection
|
||||
press_select() # confirm selection
|
||||
else:
|
||||
need_keypress(confirm) # confirm story
|
||||
press_select() # confirm story
|
||||
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "To export receiving and change descriptors in one descriptor (<0;1> notation) press OK" in story
|
||||
assert "press (1) to export receiving and change descriptors separately" in story
|
||||
if int_ext:
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
else:
|
||||
need_keypress("1")
|
||||
|
||||
@ -632,10 +629,7 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting
|
||||
@pytest.mark.parametrize("account", ["Postmix", "Premix"])
|
||||
def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_home,
|
||||
need_keypress, cap_story, microsd_path, nfc_read_text,
|
||||
load_export, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
|
||||
load_export, press_select, press_cancel):
|
||||
if account == "Postmix":
|
||||
acct_num = 2147483646
|
||||
in_story = "Samourai POST-MIX"
|
||||
@ -652,12 +646,12 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_
|
||||
need_keypress("1")
|
||||
for ch in str(acct_num):
|
||||
need_keypress(ch)
|
||||
need_keypress(confirm)
|
||||
need_keypress(confirm) # int_ext <0;1>
|
||||
press_select()
|
||||
press_select() # int_ext <0;1>
|
||||
pick_menu_item("Segwit P2WPKH") # both postmix and premix are p2wpkh only
|
||||
file_desc_generic = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH)
|
||||
need_keypress(confirm) # written
|
||||
need_keypress(cancel) # go back to advanced
|
||||
press_select() # written
|
||||
press_cancel() # go back to advanced
|
||||
pick_menu_item("Export Wallet")
|
||||
pick_menu_item(f"Samourai {account}")
|
||||
time.sleep(.1)
|
||||
@ -668,7 +662,7 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_
|
||||
assert "Press 1 to enter a non-zero account number" not in story # NOT
|
||||
assert "sensitive--in terms of privacy" in story
|
||||
assert "not compromise your funds directly" in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
file_desc = load_export("sd", label="Descriptor", is_json=False, addr_fmt=AF_P2WPKH)
|
||||
assert file_desc.strip() == file_desc_generic.strip()
|
||||
|
||||
|
||||
@ -389,7 +389,7 @@ def quick_start_hsm(hsm_reset, start_hsm, hsm_status, change_hsm, sim_eval):
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def start_hsm(request, dev, hsm_reset, hsm_status, need_keypress):
|
||||
def start_hsm(request, dev, hsm_reset, hsm_status, need_keypress, press_select):
|
||||
|
||||
def doit(policy):
|
||||
try:
|
||||
@ -415,7 +415,7 @@ def start_hsm(request, dev, hsm_reset, hsm_status, need_keypress):
|
||||
|
||||
if cap_story:
|
||||
# approve it
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
|
||||
title, body2 = cap_story()
|
||||
@ -444,7 +444,7 @@ def start_hsm(request, dev, hsm_reset, hsm_status, need_keypress):
|
||||
|
||||
else:
|
||||
# do keypresses blindly
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
for ch in '12346':
|
||||
need_keypress(ch, timeout=10000)
|
||||
@ -857,8 +857,8 @@ def test_multiple_signings_multisig(cc_first, M_N, dev, quick_start_hsm,
|
||||
|
||||
time.sleep(.2)
|
||||
if dev.is_simulator:
|
||||
need_keypress = request.getfixturevalue('need_keypress')
|
||||
need_keypress("y")
|
||||
press_select = request.getfixturevalue('press_select')
|
||||
press_select()
|
||||
else:
|
||||
import pdb;pdb.set_trace() # user interaction required on real CC
|
||||
|
||||
@ -1547,7 +1547,7 @@ def test_op_return_output_bitcoind(op_return_data, start_hsm, attempt_psbt, bitc
|
||||
start_hsm(policy)
|
||||
attempt_psbt(base64.b64decode(psbt), refuse="non-whitelisted address: 6a") # 6a --> OP_RETURN
|
||||
|
||||
def test_hsm_commands_disabled(dev, goto_home, pick_menu_item, need_keypress, hsm_reset, start_hsm,
|
||||
def test_hsm_commands_disabled(dev, goto_home, pick_menu_item, hsm_reset, start_hsm,
|
||||
sim_exec, enable_hsm_commands):
|
||||
dev.send_recv(CCProtocolPacker.create_user(b"xxx", 3, 32 * b"y"))
|
||||
dev.send_recv(CCProtocolPacker.delete_user(b"xxx"))
|
||||
|
||||
@ -10,18 +10,17 @@ from base64 import b64encode, b64decode
|
||||
from ckcc_protocol.protocol import CCProtocolPacker, CCProtoError, CCUserRefused
|
||||
from ckcc_protocol.constants import *
|
||||
from constants import addr_fmt_names, msg_sign_unmap_addr_fmt
|
||||
from charcodes import KEY_ENTER, KEY_CANCEL, KEY_NFC
|
||||
|
||||
|
||||
@pytest.mark.parametrize('msg', [ 'aZ', 'hello', 'abc def eght', "x"*140, 'a'*240])
|
||||
@pytest.mark.parametrize('path', [ 'm', "m/1/2", "m/1'/100'", 'm/23H/22p'])
|
||||
@pytest.mark.parametrize('addr_fmt', [ AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH ])
|
||||
def test_sign_msg_good(dev, need_keypress, msg, path, addr_fmt, addr_vs_path, is_q1):
|
||||
def test_sign_msg_good(dev, press_select, msg, path, addr_fmt, addr_vs_path):
|
||||
|
||||
msg = msg.encode('ascii')
|
||||
dev.send_recv(CCProtocolPacker.sign_message(msg, path, addr_fmt=addr_fmt), timeout=None)
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
done = None
|
||||
while done == None:
|
||||
@ -40,14 +39,14 @@ def test_sign_msg_good(dev, need_keypress, msg, path, addr_fmt, addr_vs_path, is
|
||||
assert verify_message(addr, sig, msg.decode("ascii")) is True
|
||||
|
||||
|
||||
def test_sign_msg_refused(dev, need_keypress, is_q1):
|
||||
def test_sign_msg_refused(dev, press_cancel):
|
||||
# user can refuse to sign (cancel)
|
||||
|
||||
msg = b'testing 123'
|
||||
path = 'm'
|
||||
dev.send_recv(CCProtocolPacker.sign_message(msg, path), timeout=None)
|
||||
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
press_cancel()
|
||||
|
||||
with pytest.raises(CCUserRefused):
|
||||
done = None
|
||||
@ -80,12 +79,11 @@ def test_bad_paths(dev, path, expect):
|
||||
|
||||
@pytest.fixture
|
||||
def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home,
|
||||
need_keypress, microsd_path, is_q1):
|
||||
press_select, microsd_path):
|
||||
|
||||
# sign a file on the microSD card
|
||||
|
||||
def doit(msg, subpath=None, addr_fmt=None, expect_fail=False):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
fname = 't-msgsign.txt'
|
||||
result_fname = 't-msgsign-signed.txt'
|
||||
|
||||
@ -108,7 +106,7 @@ def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home,
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert 'Choose text file to be signed' in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
|
||||
try:
|
||||
@ -133,7 +131,7 @@ def sign_on_microsd(open_microsd, cap_story, pick_menu_item, goto_home,
|
||||
x_subpath = subpath.lower().replace('p', "'").replace('h', "'")
|
||||
assert ('%s =>' % x_subpath) in story
|
||||
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
|
||||
# wait for it to finish
|
||||
for r in range(10):
|
||||
@ -245,7 +243,7 @@ def test_sign_msg_fails(dev, sign_on_microsd, msg, concern, no_file, transport,
|
||||
('Test1', 3, 'IEt/v9K95YVFuRtRtWaabPVwWOFv1FSA/e874I8ABgYMbRyVvHhSwLFz0RZuO87ukxDd4TOsRdofQwMEA90LCgI='),
|
||||
])
|
||||
def test_low_R_cases(msg, num_iter, expect, dev, set_seed_words, use_mainnet,
|
||||
need_keypress, is_q1):
|
||||
press_select):
|
||||
# Thanks to @craigraw of Sparrow for this test case, copied from:
|
||||
# <https://github.com/sparrowwallet/drongo/blob/master/src/test/java/com/sparrowwallet/drongo/crypto/ECKeyTest.java>
|
||||
|
||||
@ -260,7 +258,7 @@ def test_low_R_cases(msg, num_iter, expect, dev, set_seed_words, use_mainnet,
|
||||
msg = msg.encode('ascii')
|
||||
dev.send_recv(CCProtocolPacker.sign_message(msg, path, addr_fmt=addr_fmt), timeout=None)
|
||||
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
|
||||
done = None
|
||||
while done == None:
|
||||
@ -308,10 +306,8 @@ def test_nfc_msg_signing_invalid(body, goto_home, pick_menu_item, nfc_write_text
|
||||
@pytest.mark.parametrize("path", ["", "m/84'/0'/0'/300/0", "m/800'", "m/0/0/0/0/1/1/1"])
|
||||
@pytest.mark.parametrize("str_addr_fmt", ["p2pkh", "", "p2wpkh", "p2wpkh-p2sh", "p2sh-p2wpkh"])
|
||||
def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text, pick_menu_item,
|
||||
goto_home, cap_story, need_keypress, addr_vs_path, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
# import pdb;pdb.set_trace()
|
||||
goto_home, cap_story, press_select, press_cancel, addr_vs_path):
|
||||
|
||||
for _ in range(5):
|
||||
# need to wait for ApproveMessageSign to be popped from ux stack
|
||||
try:
|
||||
@ -336,12 +332,12 @@ def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text,
|
||||
assert "Ok to sign this?" in story
|
||||
assert msg in story
|
||||
assert path in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
signed_msg = nfc_read_text()
|
||||
if "BITCOIN SIGNED MESSAGE" not in signed_msg:
|
||||
# missed it? again
|
||||
signed_msg = nfc_read_text()
|
||||
need_keypress(confirm) # exit NFC animation
|
||||
press_select() # exit NFC animation
|
||||
pmsg, addr, sig = parse_signed_message(signed_msg)
|
||||
assert pmsg == msg
|
||||
addr_vs_path(addr, path, addr_fmt)
|
||||
@ -349,15 +345,15 @@ def test_nfc_msg_signing(msg, path, str_addr_fmt, nfc_write_text, nfc_read_text,
|
||||
time.sleep(0.5)
|
||||
_, story = cap_story()
|
||||
assert "Press OK to share again" in story
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
signed_msg_again = nfc_read_text()
|
||||
assert signed_msg == signed_msg_again
|
||||
need_keypress(cancel) # exit NFC animation
|
||||
need_keypress(cancel) # do not want to share again
|
||||
press_cancel() # exit NFC animation
|
||||
press_cancel() # do not want to share again
|
||||
|
||||
@pytest.fixture
|
||||
def verify_armored_signature(pick_menu_item, nfc_write_text, need_keypress,
|
||||
cap_story, goto_home, is_q1):
|
||||
def verify_armored_signature(pick_menu_item, nfc_write_text, press_select,
|
||||
cap_story, goto_home):
|
||||
def doit(way, fname=None, signed_msg=None):
|
||||
goto_home()
|
||||
pick_menu_item('Advanced/Tools')
|
||||
@ -372,7 +368,7 @@ def verify_armored_signature(pick_menu_item, nfc_write_text, need_keypress,
|
||||
else:
|
||||
_, story = cap_story()
|
||||
assert 'Choose signature file.' in story
|
||||
need_keypress(KEY_ENTER if is_q1 else 'y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
pick_menu_item(fname)
|
||||
|
||||
@ -473,9 +469,7 @@ def test_verify_signature_file_fail(way, addr_sig, microsd_path, cap_story, goto
|
||||
|
||||
@pytest.mark.parametrize("binary", [True, False])
|
||||
def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick_menu_item,
|
||||
need_keypress, goto_home, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
need_keypress, goto_home, press_select, press_cancel):
|
||||
|
||||
fpattern = "to_sign"
|
||||
if binary:
|
||||
@ -499,19 +493,19 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("File Management")
|
||||
pick_menu_item("List Files")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(fname)
|
||||
need_keypress("4") # create detached sig
|
||||
need_keypress(confirm)
|
||||
need_keypress(cancel)
|
||||
press_select()
|
||||
press_cancel()
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CORRECT"
|
||||
assert "Good signature" in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
# modify contents of the file
|
||||
with open(fpath, mode) as f:
|
||||
@ -520,7 +514,7 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
|
||||
mod_digest = hashlib.sha256(mod_contents if binary else mod_contents.encode()).digest().hex()
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -529,12 +523,12 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
assert ("'%s' has wrong contents" % fname) in story
|
||||
assert ("Got:\n%s" % orig_digest) in story
|
||||
assert ("Expected:\n%s" % mod_digest) in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
# remove file
|
||||
os.remove(fpath)
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -542,13 +536,12 @@ def test_verify_signature_file_digest_prob(binary, microsd_path, cap_story, pick
|
||||
assert "Good signature" in story # sig is still correct
|
||||
assert ("'%s' is not present" % fname) in story
|
||||
assert 'Contents verification not possible' in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
|
||||
@pytest.mark.parametrize("f_num", [2, 10, 20])
|
||||
def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story, pick_menu_item,
|
||||
need_keypress, goto_home, is_q1):
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
press_select, goto_home):
|
||||
files = []
|
||||
msg = ""
|
||||
for i in range(f_num):
|
||||
@ -580,13 +573,13 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("File Management")
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CORRECT"
|
||||
assert "Good signature" in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
# change contents of 0th file
|
||||
fname, orig_digest, fpath, _, _ = files[0]
|
||||
@ -596,7 +589,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
f.write(new_contetns)
|
||||
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -605,7 +598,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
assert ("'%s' has wrong contents" % fname) in story
|
||||
assert ("Got:\n%s" % orig_digest) in story
|
||||
assert ("Expected:\n%s" % mod_digest) in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
# change contents of 1st file remove 0th file
|
||||
# both warnings must be visible
|
||||
@ -618,7 +611,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
f.write(new_contetns)
|
||||
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -629,12 +622,12 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
assert ("Expected:\n%s" % mod_digest) in story
|
||||
assert ("'%s' is not present" % fname0) in story
|
||||
assert 'Contents verification not possible' in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
# remove 1st file too
|
||||
os.remove(fpath)
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -643,7 +636,7 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
warn_msg = "Files:\n" + "\n".join("> %s" % fname for fname in (fname0, fname1))
|
||||
assert warn_msg in story
|
||||
assert 'Contents verification not possible' in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
# reboult valid signed files
|
||||
for tup in files:
|
||||
@ -652,13 +645,13 @@ def test_verify_signature_file_digest_prob_multi(f_num, microsd_path, cap_story,
|
||||
f.write(conts)
|
||||
|
||||
pick_menu_item("Verify Sig File")
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
pick_menu_item(sig_name)
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
assert title == "CORRECT"
|
||||
assert "Good signature" in story
|
||||
need_keypress(confirm) # back in File Management
|
||||
press_select() # back in File Management
|
||||
|
||||
@pytest.mark.parametrize("way", ("sd", "nfc"))
|
||||
@pytest.mark.parametrize("truncation_len", (0, 1))
|
||||
|
||||
@ -143,7 +143,7 @@ def make_multisig(dev, sim_execfile):
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def offer_ms_import(cap_story, dev, need_keypress):
|
||||
def offer_ms_import(cap_story, dev):
|
||||
def doit(config):
|
||||
# upload the file, trigger import
|
||||
file_len, sha = dev.upload_file(config.encode('ascii'))
|
||||
@ -161,7 +161,7 @@ def offer_ms_import(cap_story, dev, need_keypress):
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def import_ms_wallet(dev, make_multisig, offer_ms_import, need_keypress):
|
||||
def import_ms_wallet(dev, make_multisig, offer_ms_import, press_select, is_q1):
|
||||
|
||||
def doit(M, N, addr_fmt=None, name=None, unique=0, accept=False, common=None,
|
||||
keys=None, do_import=True, derivs=None, descriptor=False,
|
||||
@ -223,7 +223,7 @@ def import_ms_wallet(dev, make_multisig, offer_ms_import, need_keypress):
|
||||
|
||||
if accept:
|
||||
time.sleep(.1)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# Test it worked.
|
||||
time.sleep(.1) # required
|
||||
@ -238,9 +238,10 @@ def import_ms_wallet(dev, make_multisig, offer_ms_import, need_keypress):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('N', [ 3, 15])
|
||||
def test_ms_import_variations(N, make_multisig, offer_ms_import, need_keypress):
|
||||
def test_ms_import_variations(N, make_multisig, offer_ms_import, press_cancel, is_q1):
|
||||
# all the different ways...
|
||||
keys = make_multisig(N, N)
|
||||
|
||||
|
||||
# bare, no fingerprints
|
||||
# - no xfps
|
||||
@ -248,7 +249,7 @@ def test_ms_import_variations(N, make_multisig, offer_ms_import, need_keypress):
|
||||
config = '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys)
|
||||
title, story = offer_ms_import(config)
|
||||
assert f'Policy: {N} of {N}\n' in story
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
# exclude myself (expect fail)
|
||||
config = '\n'.join(sk.hwif(as_private=False)
|
||||
@ -264,7 +265,7 @@ def test_ms_import_variations(N, make_multisig, offer_ms_import, need_keypress):
|
||||
config = f'name: {name}\n'
|
||||
config += '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys)
|
||||
title, story = offer_ms_import(config)
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
assert name in story
|
||||
|
||||
# too long name
|
||||
@ -284,14 +285,14 @@ def test_ms_import_variations(N, make_multisig, offer_ms_import, need_keypress):
|
||||
config.insert(i, '')
|
||||
title, story = offer_ms_import('\n'.join(config))
|
||||
assert f'Policy: {N} of {N}\n' in story
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
# the different addr formats
|
||||
for af in unmap_addr_fmt.keys():
|
||||
config = f'format: {af}\n'
|
||||
config += '\n'.join(sk.hwif(as_private=False) for xfp,m,sk in keys)
|
||||
title, story = offer_ms_import(config)
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
assert f'Policy: {N} of {N}\n' in story
|
||||
|
||||
def make_redeem(M, keys, path_mapper=None,
|
||||
@ -394,7 +395,8 @@ def make_ms_address(M, keys, idx=0, is_change=0, addr_fmt=AF_P2SH, testnet=1, **
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_ms_show_addr(dev, cap_story, need_keypress, addr_vs_path, bitcoind_p2sh, has_ms_checks):
|
||||
def test_ms_show_addr(dev, cap_story, press_select, addr_vs_path, bitcoind_p2sh,
|
||||
has_ms_checks, is_q1):
|
||||
def doit(M, keys, addr_fmt=AF_P2SH, bip45=True, **make_redeem_args):
|
||||
# test we are showing addresses correctly
|
||||
# - verifies against bitcoind as well
|
||||
@ -424,7 +426,7 @@ def test_ms_show_addr(dev, cap_story, need_keypress, addr_vs_path, bitcoind_p2sh
|
||||
else:
|
||||
assert 'UNVERIFIED' in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# check expected addr was generated based on my math
|
||||
addr_vs_path(got_addr, addr_fmt=addr_fmt, script=scr)
|
||||
@ -434,7 +436,6 @@ def test_ms_show_addr(dev, cap_story, need_keypress, addr_vs_path, bitcoind_p2sh
|
||||
assert B2A(scr) == core_scr
|
||||
assert core_addr == got_addr
|
||||
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@ -459,7 +460,8 @@ def test_import_ranges(m_of_n, use_regtest, addr_fmt, clear_ms, import_ms_wallet
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.ms_danger
|
||||
def test_violate_bip67(clear_ms, use_regtest, import_ms_wallet, need_keypress, test_ms_show_addr, has_ms_checks):
|
||||
def test_violate_bip67(clear_ms, use_regtest, import_ms_wallet,
|
||||
test_ms_show_addr, has_ms_checks):
|
||||
# detect when pubkeys are not in order in the redeem script
|
||||
M, N = 1, 15
|
||||
|
||||
@ -477,7 +479,8 @@ def test_violate_bip67(clear_ms, use_regtest, import_ms_wallet, need_keypress, t
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('which_pubkey', [0, 1, 14])
|
||||
def test_bad_pubkey(has_ms_checks, use_regtest, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr, which_pubkey):
|
||||
def test_bad_pubkey(has_ms_checks, use_regtest, clear_ms, import_ms_wallet,
|
||||
test_ms_show_addr, which_pubkey):
|
||||
# give incorrect pubkey inside redeem script
|
||||
M, N = 1, 15
|
||||
keys = import_ms_wallet(M, N, accept=1)
|
||||
@ -498,7 +501,8 @@ def test_bad_pubkey(has_ms_checks, use_regtest, clear_ms, import_ms_wallet, need
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ])
|
||||
def test_zero_depth(clear_ms, use_regtest, addr_fmt, import_ms_wallet, need_keypress, test_ms_show_addr, make_multisig):
|
||||
def test_zero_depth(clear_ms, use_regtest, addr_fmt, import_ms_wallet
|
||||
, test_ms_show_addr, make_multisig):
|
||||
# test having a co-signer with "m" only key ... ie. depth=0
|
||||
|
||||
M, N = 1, 2
|
||||
@ -524,7 +528,8 @@ def test_zero_depth(clear_ms, use_regtest, addr_fmt, import_ms_wallet, need_keyp
|
||||
@pytest.mark.parametrize('mode', ['wrong-xfp', 'long-path', 'short-path', 'zero-path'])
|
||||
@pytest.mark.ms_danger
|
||||
@pytest.mark.bitcoind
|
||||
def test_bad_xfp(mode, clear_ms, use_regtest, import_ms_wallet, need_keypress, test_ms_show_addr, has_ms_checks, request):
|
||||
def test_bad_xfp(mode, clear_ms, use_regtest, import_ms_wallet
|
||||
, test_ms_show_addr, has_ms_checks, request):
|
||||
# give incorrect xfp+path args during show_address
|
||||
|
||||
if has_ms_checks and (mode in {'zero-path', 'wrong-xfp'}):
|
||||
@ -572,7 +577,8 @@ def test_bad_xfp(mode, clear_ms, use_regtest, import_ms_wallet, need_keypress, t
|
||||
"m/1/2/3/4/5/6/7/8/9/10/11/12/13", # assuming MAX_PATH_DEPTH==12
|
||||
])
|
||||
@pytest.mark.bitcoind
|
||||
def test_bad_common_prefix(cpp, use_regtest, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
def test_bad_common_prefix(cpp, use_regtest, clear_ms, import_ms_wallet,
|
||||
test_ms_show_addr):
|
||||
# give some incorrect path values as the common prefix derivation
|
||||
|
||||
M, N = 1, 15
|
||||
@ -581,9 +587,10 @@ def test_bad_common_prefix(cpp, use_regtest, clear_ms, import_ms_wallet, need_ke
|
||||
assert 'bad derivation line' in str(ee)
|
||||
|
||||
|
||||
def test_import_detail(clear_ms, import_ms_wallet, need_keypress, cap_story):
|
||||
def test_import_detail(clear_ms, import_ms_wallet, need_keypress,
|
||||
cap_story, is_q1, press_cancel):
|
||||
# check all details are shown right
|
||||
|
||||
|
||||
M,N = 14, 15
|
||||
|
||||
keys = import_ms_wallet(M, N)
|
||||
@ -600,17 +607,20 @@ def test_import_detail(clear_ms, import_ms_wallet, need_keypress, cap_story):
|
||||
for xp in xpubs:
|
||||
assert xp in story
|
||||
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
time.sleep(.1)
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('acct_num', [0, 99, 123])
|
||||
@pytest.mark.parametrize('testnet', [True, False])
|
||||
def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu, need_keypress,
|
||||
microsd_path, load_export, use_mainnet, testnet, way):
|
||||
def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu,
|
||||
need_keypress, microsd_path, load_export, use_mainnet,
|
||||
testnet, way, is_q1, press_select):
|
||||
|
||||
|
||||
if not testnet:
|
||||
use_mainnet()
|
||||
|
||||
@ -626,13 +636,13 @@ def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu,
|
||||
assert f"m/48'/{int(testnet)}'" in story
|
||||
assert "acct'" in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# enter account number every time
|
||||
time.sleep(.1)
|
||||
for n in str(acct_num):
|
||||
need_keypress(n)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
rv = load_export(way, is_json=True, label="Multisig XPUB", fpattern="ccxp-", sig_check=False)
|
||||
|
||||
@ -677,8 +687,9 @@ def test_export_airgap(acct_num, goto_home, cap_story, pick_menu_item, cap_menu,
|
||||
|
||||
@pytest.mark.parametrize('N', [ 3, 15])
|
||||
@pytest.mark.parametrize('vdisk', [True, False])
|
||||
def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, need_keypress, microsd_path, make_multisig,
|
||||
virtdisk_path):
|
||||
def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item,
|
||||
need_keypress, microsd_path, make_multisig,
|
||||
virtdisk_path, is_q1, press_cancel, press_select):
|
||||
# test menu-based UX for importing wallet file from SD
|
||||
M = N-1
|
||||
|
||||
@ -715,7 +726,7 @@ def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, need_keypress
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "Pick multisig wallet" in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
pick_menu_item(fname.rsplit('/', 1)[1])
|
||||
@ -728,7 +739,7 @@ def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, need_keypress
|
||||
assert f'Policy: {M} of {N}\n' in story
|
||||
|
||||
# abort install
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
|
||||
finally:
|
||||
# cleanup
|
||||
@ -738,8 +749,8 @@ def test_import_ux(N, vdisk, goto_home, cap_story, pick_menu_item, need_keypress
|
||||
@pytest.mark.parametrize("way", ["sd", "vdisk", "nfc"])
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ])
|
||||
@pytest.mark.parametrize('comm_prefix', ['m/1/2/3/4/5/6/7/8/9/10/11/12', None, "m/45'"])
|
||||
def test_export_single_ux(goto_home, comm_prefix, cap_story, pick_menu_item, cap_menu, need_keypress,
|
||||
microsd_path, import_ms_wallet, addr_fmt, clear_ms, way, load_export):
|
||||
def test_export_single_ux(goto_home, comm_prefix, cap_story, pick_menu_item, cap_menu, press_select,
|
||||
microsd_path, import_ms_wallet, addr_fmt, clear_ms, way, load_export, is_q1):
|
||||
|
||||
# create a wallet, export to SD card, check file created.
|
||||
# - checks some values for derivation path, assuming MAX_PATH_DEPTH==12
|
||||
@ -803,11 +814,12 @@ def test_export_single_ux(goto_home, comm_prefix, cap_story, pick_menu_item, cap
|
||||
pick_menu_item('Delete')
|
||||
|
||||
time.sleep(.2)
|
||||
_, story = cap_story()
|
||||
assert 'you SURE' in story
|
||||
title, story = cap_story()
|
||||
where = title if is_q1 else story
|
||||
assert 'you SURE' in where
|
||||
assert name in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
menu = cap_menu()
|
||||
assert not [i for i in menu if name in i]
|
||||
@ -815,7 +827,8 @@ def test_export_single_ux(goto_home, comm_prefix, cap_story, pick_menu_item, cap
|
||||
|
||||
|
||||
@pytest.mark.parametrize('N', [ 3, 15])
|
||||
def test_overflow(N, import_ms_wallet, clear_ms, need_keypress, cap_story, mk_num):
|
||||
def test_overflow(N, import_ms_wallet, clear_ms, press_select, cap_story, mk_num, is_q1):
|
||||
|
||||
clear_ms()
|
||||
M = N
|
||||
name = 'a'*20 # longest possible
|
||||
@ -824,7 +837,7 @@ def test_overflow(N, import_ms_wallet, clear_ms, need_keypress, cap_story, mk_nu
|
||||
common="m/45'/0'/34'")
|
||||
|
||||
time.sleep(.1)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.2)
|
||||
title, story = cap_story()
|
||||
@ -842,7 +855,7 @@ def test_overflow(N, import_ms_wallet, clear_ms, need_keypress, cap_story, mk_nu
|
||||
if N == 15:
|
||||
assert count == 2, "Expect fail at 2"
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
clear_ms()
|
||||
|
||||
@pytest.fixture
|
||||
@ -869,8 +882,11 @@ def test_make_example_file(microsd_path, make_multisig):
|
||||
return doit
|
||||
|
||||
@pytest.mark.parametrize('N', [ 5, 10])
|
||||
def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import, need_keypress, cap_story, goto_home, pick_menu_item, cap_menu):
|
||||
def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import,
|
||||
need_keypress, cap_story, goto_home, pick_menu_item,
|
||||
cap_menu, is_q1, press_select):
|
||||
# import wallet, rename it, (check that indicated, works), attempt same w/ addr fmt different
|
||||
|
||||
M = N
|
||||
|
||||
clear_ms()
|
||||
@ -898,7 +914,7 @@ def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import, need_keypr
|
||||
assert 'Create new multisig wallet' in story
|
||||
assert 'xxx-orig' in story
|
||||
assert 'P2SH' in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
has_name('xxx-orig')
|
||||
|
||||
# just simple rename
|
||||
@ -906,7 +922,7 @@ def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import, need_keypr
|
||||
assert 'update name only' in story.lower()
|
||||
assert 'xxx-new' in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
has_name('xxx-new')
|
||||
|
||||
assert N < 15, 'cant make more, no space'
|
||||
@ -921,9 +937,10 @@ def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import, need_keypr
|
||||
assert 'P2WSH' in story
|
||||
|
||||
# should be 2 now, slightly different
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
has_name('xxx-newer', 2)
|
||||
|
||||
# TODO
|
||||
# repeat last one, should still be two
|
||||
for keys in ['yn', 'n']:
|
||||
title, story = offer_ms_import(newer)
|
||||
@ -939,7 +956,9 @@ def test_import_dup_safe(N, clear_ms, make_multisig, offer_ms_import, need_keypr
|
||||
clear_ms()
|
||||
|
||||
@pytest.mark.parametrize('N', [ 5])
|
||||
def test_import_dup_diff_xpub(N, clear_ms, make_multisig, offer_ms_import, need_keypress, cap_story, goto_home, pick_menu_item, cap_menu):
|
||||
def test_import_dup_diff_xpub(N, clear_ms, make_multisig, offer_ms_import,
|
||||
press_select, cap_story, goto_home,
|
||||
pick_menu_item, cap_menu, is_q1):
|
||||
# import wallet, tweak xpub only, check that change detected
|
||||
clear_ms()
|
||||
|
||||
@ -963,7 +982,7 @@ def test_import_dup_diff_xpub(N, clear_ms, make_multisig, offer_ms_import, need_
|
||||
assert 'Create new multisig wallet' in story
|
||||
assert 'xxx-orig' in story
|
||||
assert 'P2SH' in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# change one key.
|
||||
title, story = offer_ms_import(make_named('xxx-new', tweaked=True))
|
||||
@ -977,7 +996,8 @@ def test_import_dup_diff_xpub(N, clear_ms, make_multisig, offer_ms_import, need_
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('m_of_n', [(2,2), (2,3), (15,15)])
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ])
|
||||
def test_import_dup_xfp_fails(m_of_n, use_regtest, addr_fmt, clear_ms, make_multisig, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
def test_import_dup_xfp_fails(m_of_n, use_regtest, addr_fmt, clear_ms,
|
||||
make_multisig, import_ms_wallet, test_ms_show_addr):
|
||||
|
||||
M, N = m_of_n
|
||||
|
||||
@ -1047,11 +1067,12 @@ def test_ms_cli(dev, addr_fmt, clear_ms, import_ms_wallet, addr_vs_path, M=1, N=
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_myself_wallet(dev, set_bip39_pw, offer_ms_import, need_keypress, clear_ms,
|
||||
reset_seed_words):
|
||||
def make_myself_wallet(dev, set_bip39_pw, offer_ms_import, press_select, clear_ms,
|
||||
reset_seed_words, is_q1):
|
||||
|
||||
# construct a wallet (M of 4) using different bip39 passwords, and default sim
|
||||
def doit(M, addr_fmt=None, do_import=True):
|
||||
|
||||
passwords = ['Me', 'Myself', 'And I', '']
|
||||
|
||||
if 0:
|
||||
@ -1095,7 +1116,7 @@ def make_myself_wallet(dev, set_bip39_pw, offer_ms_import, need_keypress, clear_
|
||||
|
||||
# dont care if update or create; accept it.
|
||||
time.sleep(.1)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
def select_wallet(idx):
|
||||
# select to specific pw
|
||||
@ -1103,7 +1124,7 @@ def make_myself_wallet(dev, set_bip39_pw, offer_ms_import, need_keypress, clear_
|
||||
if do_import:
|
||||
offer_ms_import(config)
|
||||
time.sleep(.1)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
assert xfp == keys[idx][0]
|
||||
|
||||
return (keys, select_wallet)
|
||||
@ -1332,9 +1353,12 @@ def test_ms_sign_myself(M, use_regtest, make_myself_wallet, segwit, num_ins, dev
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2wsh', 'p2sh-p2wsh'])
|
||||
@pytest.mark.parametrize('acct_num', [ 0, 99, 4321])
|
||||
@pytest.mark.parametrize('N', [ 3, 14])
|
||||
def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_item, need_keypress,
|
||||
microsd_path, set_bip39_pw, clear_ms, get_settings, load_export):
|
||||
def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_item,
|
||||
need_keypress, microsd_path, set_bip39_pw, clear_ms,
|
||||
get_settings, load_export, is_q1, press_select, press_cancel):
|
||||
# test UX and math for bip45 export
|
||||
|
||||
|
||||
|
||||
# cleanup
|
||||
from glob import glob
|
||||
@ -1355,13 +1379,13 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
pick_menu_item('Multisig Wallets')
|
||||
pick_menu_item('Export XPUB')
|
||||
time.sleep(.05)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# enter account number every time
|
||||
time.sleep(.05)
|
||||
for n in str(acct_num):
|
||||
need_keypress(n)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
need_keypress('1')
|
||||
|
||||
@ -1378,7 +1402,7 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
assert 'XPUB' in story
|
||||
|
||||
if addr_fmt == 'p2wsh':
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
elif addr_fmt == 'p2sh-p2wsh':
|
||||
need_keypress('1')
|
||||
elif addr_fmt == 'p2sh':
|
||||
@ -1407,13 +1431,13 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
else:
|
||||
assert 0, N
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
|
||||
assert "Create new multisig" in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
impf, fname = load_export("sd", label="Coldcard multisig setup", is_json=False, sig_check=False,
|
||||
tail_check="Import that file onto the other Coldcards involved with this multisig wallet",
|
||||
@ -1428,8 +1452,8 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
el_fname = microsd_path(fname)
|
||||
assert f'{M}of{N}' in wal['wallet_type']
|
||||
|
||||
need_keypress('y')
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
press_select()
|
||||
|
||||
if N == 4 and acct_num == 0:
|
||||
|
||||
@ -1454,7 +1478,7 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
if "Press (1) to import multisig wallet file from SD Card" in story:
|
||||
need_keypress("1")
|
||||
time.sleep(.05)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.05)
|
||||
pick_menu_item(cc_fname.rsplit('/', 1)[1])
|
||||
|
||||
@ -1471,8 +1495,8 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
# test code ehre
|
||||
|
||||
# abort import, good enough
|
||||
need_keypress('x')
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
press_cancel()
|
||||
|
||||
|
||||
@pytest.mark.unfinalized
|
||||
@ -1480,7 +1504,7 @@ def test_make_airgapped(addr_fmt, acct_num, N, goto_home, cap_story, pick_menu_i
|
||||
@pytest.mark.parametrize('addr_style', ["legacy", "p2sh-segwit", "bech32"])
|
||||
@pytest.mark.parametrize('cc_sign_first', [True, False])
|
||||
def test_bitcoind_cosigning(cc_sign_first, dev, bitcoind, import_ms_wallet, clear_ms, try_sign,
|
||||
need_keypress, addr_style, use_regtest):
|
||||
press_cancel, addr_style, use_regtest, is_q1):
|
||||
# Make a P2SH wallet with local bitcoind as a co-signer (and simulator)
|
||||
# - send an receive various
|
||||
# - following text of <https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md>
|
||||
@ -1551,7 +1575,7 @@ def test_bitcoind_cosigning(cc_sign_first, dev, bitcoind, import_ms_wallet, clea
|
||||
M, xfp_paths, scr, addr_fmt=addr_fmt), timeout=None)
|
||||
assert got_addr == ms_addr
|
||||
time.sleep(.1)
|
||||
need_keypress('x') # clear screen / start over
|
||||
press_cancel() # clear screen / start over
|
||||
|
||||
print(f"Will be signing an input from {ms_addr}")
|
||||
|
||||
@ -1766,7 +1790,7 @@ def test_iss6743(repeat, set_seed_words, sim_execfile, try_sign):
|
||||
|
||||
@pytest.mark.parametrize('N', [ 3, 15])
|
||||
@pytest.mark.parametrize('xderiv', [ None, 'any', 'unknown', '*', '', 'none'])
|
||||
def test_ms_import_nopath(N, xderiv, make_multisig, clear_ms, offer_ms_import, need_keypress):
|
||||
def test_ms_import_nopath(N, xderiv, make_multisig, clear_ms, offer_ms_import):
|
||||
# try various synonyms for unknown/any derivation styles
|
||||
|
||||
keys = make_multisig(N, N, deriv="m/48'/0'/0'/1'/0", unique=1)
|
||||
@ -1785,11 +1809,12 @@ def test_ms_import_nopath(N, xderiv, make_multisig, clear_ms, offer_ms_import, n
|
||||
@pytest.mark.parametrize('N', [ 15])
|
||||
@pytest.mark.parametrize('M', [ 1, 15])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
def test_ms_import_many_derivs(M, N, way, make_multisig, clear_ms, offer_ms_import, need_keypress,
|
||||
def test_ms_import_many_derivs(M, N, way, make_multisig, clear_ms, offer_ms_import, press_select,
|
||||
pick_menu_item, cap_story, microsd_path, virtdisk_path, nfc_read_text,
|
||||
goto_home, load_export):
|
||||
goto_home, load_export, is_q1):
|
||||
# try config file with different derivation paths given, including None
|
||||
# - also check we can convert those into Electrum wallets
|
||||
|
||||
actual = "m/48'/0'/0'/1'/0"
|
||||
derivs = [ actual, 'm', "m/45'/0'/99'", "m/45'/34/34'/34"]
|
||||
|
||||
@ -1814,7 +1839,7 @@ def test_ms_import_many_derivs(M, N, way, make_multisig, clear_ms, offer_ms_impo
|
||||
assert f'P2SH-P2WSH' in story
|
||||
assert 'Derivation:\n Varies' in story
|
||||
assert f' Varies ({len(set(derivs))})\n' in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
goto_home()
|
||||
pick_menu_item('Settings')
|
||||
@ -1834,7 +1859,7 @@ def test_ms_import_many_derivs(M, N, way, make_multisig, clear_ms, offer_ms_impo
|
||||
time.sleep(.25)
|
||||
title, story = cap_story()
|
||||
assert 'This saves a skeleton Electrum wallet file' in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
el = load_export(way, label="Electrum multisig wallet", sig_check=False, is_json=True)
|
||||
|
||||
@ -1913,15 +1938,15 @@ def test_ms_addr_explorer(descriptor, change, M, N, addr_fmt, make_multisig, cle
|
||||
|
||||
time.sleep(.5)
|
||||
title, story = cap_story()
|
||||
assert "Press (6)" in story
|
||||
assert "(0)" in story
|
||||
assert "change addresses." in story
|
||||
if change:
|
||||
need_keypress("6")
|
||||
need_keypress("0")
|
||||
time.sleep(0.2)
|
||||
title, story = cap_story()
|
||||
# once change is selected - do not offer this option again
|
||||
assert "change addresses." not in story
|
||||
assert "Press (6)" not in story
|
||||
assert "(0)" not in story
|
||||
# unwrap text a bit
|
||||
if change:
|
||||
story = story.replace("=>\n", "=> ").replace('1/0]\n =>', "1/0 =>")
|
||||
@ -1954,8 +1979,11 @@ def test_ms_addr_explorer(descriptor, change, M, N, addr_fmt, make_multisig, cle
|
||||
assert expect.endswith(end)
|
||||
|
||||
|
||||
def test_dup_ms_wallet_bug(goto_home, pick_menu_item, need_keypress, import_ms_wallet, clear_ms, M=2, N=3):
|
||||
|
||||
def test_dup_ms_wallet_bug(goto_home, pick_menu_item, press_select, import_ms_wallet,
|
||||
clear_ms, is_q1):
|
||||
M = 2
|
||||
N = 3
|
||||
|
||||
deriv = ["m/48'/1'/0'/69'/1"]*N
|
||||
fmts = [ 'p2wsh', 'p2sh-p2wsh']
|
||||
|
||||
@ -1972,13 +2000,13 @@ def test_dup_ms_wallet_bug(goto_home, pick_menu_item, need_keypress, import_ms_w
|
||||
time.sleep(.1)
|
||||
pick_menu_item('2/3: name-1')
|
||||
pick_menu_item('Delete')
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
# BUG: pre v4.0.3, would be showing a "Yikes" referencing multisig:419 at this point
|
||||
|
||||
pick_menu_item('2/3: name-0')
|
||||
pick_menu_item('Delete')
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
clear_ms()
|
||||
|
||||
@ -1987,8 +2015,8 @@ def test_dup_ms_wallet_bug(goto_home, pick_menu_item, need_keypress, import_ms_w
|
||||
@pytest.mark.parametrize('int_ext_desc', [True, False])
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
def test_import_desciptor(M_N, addr_fmt, int_ext_desc, way, import_ms_wallet, goto_home, pick_menu_item,
|
||||
need_keypress, clear_ms, cap_story, microsd_path, virtdisk_path,
|
||||
nfc_read_text, load_export):
|
||||
press_select, clear_ms, cap_story, microsd_path, virtdisk_path,
|
||||
nfc_read_text, load_export, is_q1):
|
||||
clear_ms()
|
||||
M, N = M_N
|
||||
import_ms_wallet(M, N, addr_fmt=addr_fmt, accept=1, descriptor=True, int_ext_desc=int_ext_desc)
|
||||
@ -1996,7 +2024,7 @@ def test_import_desciptor(M_N, addr_fmt, int_ext_desc, way, import_ms_wallet, go
|
||||
goto_home()
|
||||
pick_menu_item('Settings')
|
||||
pick_menu_item('Multisig Wallets')
|
||||
need_keypress('y') # only one enrolled multisig - choose it
|
||||
press_select() # only one enrolled multisig - choose it
|
||||
pick_menu_item('Descriptors')
|
||||
pick_menu_item('Export')
|
||||
contents = load_export(way, label="Descriptor multisig setup", is_json=False, sig_check=False)
|
||||
@ -2023,7 +2051,8 @@ def test_import_desciptor(M_N, addr_fmt, int_ext_desc, way, import_ms_wallet, go
|
||||
@pytest.mark.parametrize('way', ["sd", "vdisk", "nfc"])
|
||||
def test_bitcoind_ms_address(change, descriptor, M_N, addr_fmt, clear_ms, goto_home, need_keypress,
|
||||
pick_menu_item, cap_menu, cap_story, make_multisig, import_ms_wallet,
|
||||
microsd_path, bitcoind_d_wallet_w_sk, use_regtest, load_export, way):
|
||||
microsd_path, bitcoind_d_wallet_w_sk, use_regtest, load_export, way,
|
||||
is_q1, press_select):
|
||||
use_regtest()
|
||||
clear_ms()
|
||||
bitcoind = bitcoind_d_wallet_w_sk
|
||||
@ -2057,22 +2086,22 @@ def test_bitcoind_ms_address(change, descriptor, M_N, addr_fmt, clear_ms, goto_h
|
||||
|
||||
time.sleep(0.2)
|
||||
title, story = cap_story()
|
||||
assert "Press (6)" in story
|
||||
assert "(0)" in story
|
||||
assert "change addresses." in story
|
||||
if change:
|
||||
need_keypress("6")
|
||||
need_keypress("0")
|
||||
time.sleep(0.2)
|
||||
title, story = cap_story()
|
||||
# once change is selected - do not offer this option again
|
||||
assert "change addresses." not in story
|
||||
assert "Press (6)" not in story
|
||||
assert "(0)" not in story
|
||||
|
||||
contents = load_export(way, label="Address summary", is_json=False, sig_check=False, vdisk_key="4")
|
||||
addr_cont = contents.strip()
|
||||
goto_home()
|
||||
pick_menu_item('Settings')
|
||||
pick_menu_item('Multisig Wallets')
|
||||
need_keypress('y') # only one enrolled multisig - choose it
|
||||
press_select() # only one enrolled multisig - choose it
|
||||
pick_menu_item('Descriptors')
|
||||
pick_menu_item("Bitcoin Core")
|
||||
contents = load_export(way, label="Bitcoin Core multisig setup", is_json=False, sig_check=False)
|
||||
@ -2111,7 +2140,9 @@ def test_bitcoind_ms_address(change, descriptor, M_N, addr_fmt, clear_ms, goto_h
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, microsd_wipe, goto_home, need_keypress,
|
||||
pick_menu_item, cap_story, load_export, microsd_path, cap_menu, try_sign):
|
||||
pick_menu_item, cap_story, load_export, microsd_path, cap_menu, try_sign,
|
||||
is_q1, press_select):
|
||||
|
||||
use_regtest()
|
||||
clear_ms()
|
||||
microsd_wipe()
|
||||
@ -2129,9 +2160,9 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m
|
||||
time.sleep(0.5)
|
||||
title, story = cap_story()
|
||||
assert "extended public keys (XPUB) you would need to join a multisig wallet" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
need_keypress("0") # account
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
xpub_obj = load_export("sd", label="Multisig XPUB", is_json=True, sig_check=False)
|
||||
template = xpub_obj["p2sh_desc"]
|
||||
# get key from bitcoind cosigner
|
||||
@ -2159,7 +2190,7 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m
|
||||
# in case Vdisk is enabled
|
||||
need_keypress("1")
|
||||
time.sleep(0.5)
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
pick_menu_item(name)
|
||||
_, story = cap_story()
|
||||
assert "Create new multisig wallet?" in story
|
||||
@ -2168,7 +2199,7 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m
|
||||
assert f"All {N} co-signers must approve spends" in story
|
||||
assert "P2SH" in story
|
||||
assert "Derivation:\n Varies (2)" in story
|
||||
need_keypress("y") # approve multisig import
|
||||
press_select() # approve multisig import
|
||||
goto_home()
|
||||
pick_menu_item('Settings')
|
||||
pick_menu_item('Multisig Wallets')
|
||||
@ -2221,8 +2252,10 @@ def test_legacy_multisig_witness_utxo_in_psbt(bitcoind, use_regtest, clear_ms, m
|
||||
@pytest.mark.parametrize("psbt_v2", [True, False])
|
||||
def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypress, pick_menu_item,
|
||||
sighash, cap_menu, cap_story, microsd_path, use_regtest, bitcoind,
|
||||
microsd_wipe, load_export, settings_set, psbt_v2, finalize_v2_v0_convert):
|
||||
microsd_wipe, load_export, settings_set, psbt_v2, is_q1,
|
||||
finalize_v2_v0_convert, press_select):
|
||||
# 2of2 case here is described in docs with tutorial
|
||||
|
||||
M, N = m_n
|
||||
settings_set("sighshchk", 1) # disable checks
|
||||
use_regtest()
|
||||
@ -2251,9 +2284,9 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
time.sleep(0.5)
|
||||
title, story = cap_story()
|
||||
assert "extended public keys (XPUB) you would need to join a multisig wallet" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
need_keypress("0") # account
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
xpub_obj = load_export("sd", label="Multisig XPUB", is_json=True, sig_check=False)
|
||||
template = xpub_obj[desc_type]
|
||||
# get keys from bitcoind signers
|
||||
@ -2289,7 +2322,7 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
# in case Vdisk is enabled
|
||||
need_keypress("1")
|
||||
time.sleep(0.5)
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
pick_menu_item(name)
|
||||
_, story = cap_story()
|
||||
assert "Create new multisig wallet?" in story
|
||||
@ -2306,7 +2339,7 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
else:
|
||||
assert "P2SH-P2WSH" in story
|
||||
assert "Derivation:\n Varies (2)" in story
|
||||
need_keypress("y") # approve multisig import
|
||||
press_select() # approve multisig import
|
||||
goto_home()
|
||||
pick_menu_item('Settings')
|
||||
pick_menu_item('Multisig Wallets')
|
||||
@ -2377,7 +2410,7 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
pick_menu_item(name)
|
||||
except:
|
||||
time.sleep(0.5)
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
pick_menu_item(name)
|
||||
time.sleep(0.5)
|
||||
title, story = cap_story()
|
||||
@ -2391,12 +2424,12 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
else:
|
||||
assert "Caution" in story
|
||||
assert "Some inputs have unusual SIGHASH values not used in typical cases." in story
|
||||
need_keypress("y") # confirm signing
|
||||
press_select() # confirm signing
|
||||
time.sleep(0.5)
|
||||
title, story = cap_story()
|
||||
assert "PSBT Signed" == title
|
||||
assert "Updated PSBT is:" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
os.remove(microsd_path(name))
|
||||
|
||||
fname = story.split("\n\n")[-1]
|
||||
@ -2442,12 +2475,12 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
pick_menu_item(name)
|
||||
except:
|
||||
time.sleep(0.5)
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
pick_menu_item(name)
|
||||
time.sleep(0.5)
|
||||
title, story = cap_story()
|
||||
assert title == "OK TO SEND?"
|
||||
need_keypress("y") # confirm signing
|
||||
press_select() # confirm signing
|
||||
time.sleep(0.5)
|
||||
title, story = cap_story()
|
||||
if "SINGLE" in sighash:
|
||||
@ -2460,7 +2493,7 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
|
||||
assert "PSBT Signed" == title
|
||||
assert "Updated PSBT is:" in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
fname = story.split("\n\n")[-1]
|
||||
with open(microsd_path(fname), "r") as f:
|
||||
cc_signed_psbt = f.read().strip()
|
||||
@ -2499,7 +2532,7 @@ def test_bitcoind_MofN_tutorial(m_n, desc_type, clear_ms, goto_home, need_keypre
|
||||
("M must be <= N", "wsh(sortedmulti(3,[0f056943/48'/1'/0'/2']tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP/0/*,[c463f778/44'/0'/0']tpubDD8pw7eZ9bUzYUR1LK5wpkA69iy3BpuLxPzsE6FFNdtTnJDySduc1VJdFEhEJQDKjYktznKdJgHwaQDRfQDQJpceDxH22c1ZKUMjrarVs7M/0/*))#uueddtsy"),
|
||||
])
|
||||
def test_exotic_descriptors(desc, clear_ms, goto_home, need_keypress, pick_menu_item, cap_menu, cap_story, make_multisig,
|
||||
import_ms_wallet, microsd_path, bitcoind_d_wallet_w_sk, use_regtest):
|
||||
import_ms_wallet, microsd_path, bitcoind_d_wallet_w_sk, use_regtest, is_q1, press_select):
|
||||
use_regtest()
|
||||
clear_ms()
|
||||
msg, desc = desc
|
||||
@ -2518,7 +2551,7 @@ def test_exotic_descriptors(desc, clear_ms, goto_home, need_keypress, pick_menu_
|
||||
assert "Press (1) to import multisig wallet file from SD Card" in story
|
||||
need_keypress("1")
|
||||
time.sleep(0.5)
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
pick_menu_item(name)
|
||||
_, story = cap_story()
|
||||
assert "Failed to import" in story
|
||||
|
||||
@ -11,7 +11,8 @@ from struct import pack, unpack
|
||||
import ndef
|
||||
from hashlib import sha256
|
||||
from txn import *
|
||||
from charcodes import KEY_NFC, KEY_ENTER, KEY_CANCEL
|
||||
from charcodes import KEY_NFC
|
||||
|
||||
|
||||
@pytest.mark.parametrize('case', range(6))
|
||||
def test_ndef(case, load_shared_mod):
|
||||
@ -147,17 +148,15 @@ def test_ndef_ccfile(ccfile, load_shared_mod):
|
||||
|
||||
@pytest.fixture
|
||||
def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress,
|
||||
sim_exec, nfc_read, nfc_write, nfc_block4rf, is_q1):
|
||||
sim_exec, nfc_read, nfc_write, nfc_block4rf, press_select,
|
||||
press_cancel, press_nfc):
|
||||
|
||||
# like "try_sign" but use NFC to send/receive PSBT/results
|
||||
confirm = KEY_ENTER if is_q1 else "y"
|
||||
cancel = KEY_CANCEL if is_q1 else "x"
|
||||
k_nfc = KEY_NFC if is_q1 else "3"
|
||||
|
||||
sim_exec('from pyb import SDCard; SDCard.ejected = True; import nfc; nfc.NFCHandler.startup()')
|
||||
|
||||
def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False, complete=False, encoding='binary', over_nfc=True):
|
||||
|
||||
def doit(f_or_data, accept=True, expect_finalize=False, accept_ms_import=False,
|
||||
complete=False, encoding='binary', over_nfc=True):
|
||||
|
||||
if f_or_data[0:5] == b'psbt\xff':
|
||||
ip = f_or_data
|
||||
@ -203,7 +202,7 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress,
|
||||
_, story = cap_story()
|
||||
assert 'NFC' in story
|
||||
|
||||
need_keypress(k_nfc)
|
||||
press_nfc()
|
||||
time.sleep(.1)
|
||||
nfc_write(ccfile)
|
||||
|
||||
@ -211,14 +210,17 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress,
|
||||
|
||||
if accept_ms_import:
|
||||
# would be better to do cap_story here
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
time.sleep(0.050)
|
||||
|
||||
title, story = cap_story()
|
||||
assert title == 'OK TO SEND?'
|
||||
|
||||
if accept != None:
|
||||
need_keypress(confirm if accept else cancel)
|
||||
if accept:
|
||||
press_select()
|
||||
else:
|
||||
press_cancel()
|
||||
|
||||
if accept == False:
|
||||
time.sleep(0.050)
|
||||
@ -240,14 +242,14 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress,
|
||||
if 'Final TXID:' in lines:
|
||||
txid = lines[-1]
|
||||
|
||||
need_keypress(k_nfc)
|
||||
press_nfc()
|
||||
time.sleep(.1)
|
||||
contents = nfc_read()
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
else:
|
||||
nfc_block4rf()
|
||||
contents = nfc_read()
|
||||
need_keypress(confirm)
|
||||
press_select()
|
||||
txid = None
|
||||
|
||||
got_txid = None
|
||||
@ -326,10 +328,7 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress,
|
||||
|
||||
@pytest.mark.parametrize('num_outs', [ 1, 20, 250])
|
||||
def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress,
|
||||
cap_story, is_q1):
|
||||
k_nfc = KEY_NFC if is_q1 else "3"
|
||||
# import pdb;pdb.set_trace()
|
||||
|
||||
cap_story, is_q1, press_nfc, press_cancel):
|
||||
# Read signing result (transaction) over NFC, decode it.
|
||||
psbt = fake_txn(1, num_outs)
|
||||
orig, result = try_sign(psbt, accept=True, finalize=True)
|
||||
@ -344,7 +343,7 @@ def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress,
|
||||
assert 'TXID' in title, story
|
||||
txid = a2b_hex(story.split()[0])
|
||||
assert f'Press {KEY_NFC if is_q1 else "(3)"}' in story
|
||||
need_keypress(k_nfc)
|
||||
press_nfc()
|
||||
|
||||
if too_big:
|
||||
title, story = cap_story()
|
||||
@ -352,7 +351,7 @@ def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress,
|
||||
return
|
||||
|
||||
contents = nfc_read()
|
||||
need_keypress(KEY_CANCEL if is_q1 else 'x')
|
||||
press_cancel()
|
||||
|
||||
#print("contents = " + B2A(contents))
|
||||
for got in ndef.message_decoder(contents):
|
||||
|
||||
@ -241,7 +241,8 @@ def test_ux_trick_menus(goto_trick_menu, pick_menu_item, cap_menu, need_keypress
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def new_trick_pin(goto_trick_menu, pick_menu_item, cap_menu, need_keypress, cap_story, enter_pin, se2_gate, is_simulator):
|
||||
def new_trick_pin(goto_trick_menu, pick_menu_item, cap_menu, press_select,
|
||||
cap_story, enter_pin, se2_gate, is_simulator):
|
||||
# using menus and UX, setup a new trick PIN
|
||||
def doit(new_pin, op_mode, expect=None):
|
||||
goto_trick_menu()
|
||||
@ -258,7 +259,7 @@ def new_trick_pin(goto_trick_menu, pick_menu_item, cap_menu, need_keypress, cap_
|
||||
if 'on this duress wallet' in story:
|
||||
# extra confirm step, seen only for trick pins which lead to duress wallet
|
||||
time.sleep(.1)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
_,story = cap_story()
|
||||
@ -266,7 +267,7 @@ def new_trick_pin(goto_trick_menu, pick_menu_item, cap_menu, need_keypress, cap_
|
||||
|
||||
assert new_pin in story
|
||||
time.sleep(.1)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
@ -295,7 +296,7 @@ def new_trick_pin(goto_trick_menu, pick_menu_item, cap_menu, need_keypress, cap_
|
||||
_, story = cap_story()
|
||||
if expect:
|
||||
assert expect in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
return doit
|
||||
|
||||
@ -480,13 +481,13 @@ def test_ux_countdown_choices(subchoice, expect, xflags, new_trick_pin, new_pin_
|
||||
def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs,
|
||||
reset_seed_words, repl, clear_all_tricks, import_ms_wallet, get_setting, clear_ms,
|
||||
new_trick_pin, new_pin_confirmed, cap_menu, pick_menu_item, cap_story, need_keypress,
|
||||
stop_after_activated=False,
|
||||
press_select, press_cancel, stop_after_activated=False,
|
||||
):
|
||||
|
||||
# import multisig
|
||||
clear_ms()
|
||||
import_ms_wallet(2, 2)
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
assert len(get_setting('multisig')) == 1
|
||||
|
||||
@ -500,7 +501,7 @@ def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs,
|
||||
pick_menu_item('Wipe -> Wallet')
|
||||
_, story = cap_story()
|
||||
assert 'Seed is silently wiped, and' in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
else:
|
||||
new_trick_pin(new_pin, 'Duress Wallet', 'Goes directly to a specific duress wallet')
|
||||
xflags &= ~TC_WIPE
|
||||
@ -508,7 +509,7 @@ def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs,
|
||||
pick_menu_item(subchoice)
|
||||
_, story = cap_story()
|
||||
assert expect in story
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
|
||||
op_mode = subchoice
|
||||
if with_wipe:
|
||||
@ -546,14 +547,14 @@ def test_ux_duress_choices(with_wipe, subchoice, expect, xflags, xargs,
|
||||
seed = Mnemonic.to_seed(' '.join(words), passphrase='')
|
||||
wallet = BIP32Node.from_master_secret(seed, netcode='XTN') # dev might be BTC
|
||||
|
||||
need_keypress('x')
|
||||
press_cancel()
|
||||
time.sleep(.1)
|
||||
pick_menu_item('Activate Wallet')
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert 'This will temporarily load' in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
if stop_after_activated: return
|
||||
_, story = cap_story()
|
||||
@ -818,12 +819,13 @@ def build_duress_wallets(request, seed_vault=False):
|
||||
# fixtures I need directly
|
||||
cap_story = request.getfixturevalue('cap_story')
|
||||
need_keypress = request.getfixturevalue('need_keypress')
|
||||
press_select = request.getfixturevalue('press_select')
|
||||
restore_main_seed = request.getfixturevalue('restore_main_seed')
|
||||
|
||||
# fixtures I need in test_ux_duress_choices
|
||||
args = {f: request.getfixturevalue(f)
|
||||
for f in ['reset_seed_words', 'repl', 'clear_all_tricks', 'new_trick_pin', 'clear_ms',
|
||||
'import_ms_wallet', 'get_setting',
|
||||
'import_ms_wallet', 'get_setting', 'press_select', 'press_cancel',
|
||||
'new_pin_confirmed', 'cap_menu', 'pick_menu_item', 'cap_story', 'need_keypress']}
|
||||
|
||||
for (subchoice, expect, xflags, xargs) in [
|
||||
@ -842,11 +844,11 @@ def build_duress_wallets(request, seed_vault=False):
|
||||
_, story = cap_story()
|
||||
assert 'Saved to Seed Vault' in story
|
||||
|
||||
need_keypress('y')
|
||||
press_select()
|
||||
time.sleep(0.1)
|
||||
_, story = cap_story()
|
||||
assert 'temporary master key is in effect now' in story
|
||||
need_keypress("y")
|
||||
press_select()
|
||||
|
||||
# re-login to reset to normal seed
|
||||
# .. because cant get into trick menu when non-master seed is set (says Unavailable)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user