(some) policy test for sssp
This commit is contained in:
parent
d053398a9a
commit
6ab63b9dcf
@ -1029,7 +1029,6 @@ def sssp_spending_policy(key, default=False, change=None):
|
||||
|
||||
raise KeyError(key)
|
||||
|
||||
return default
|
||||
|
||||
async def sssp_feature_menu(*a):
|
||||
# Show the top menu for SSSP feature, or enable access first time.
|
||||
|
||||
@ -2632,6 +2632,7 @@ def build_test_seed_vault():
|
||||
from test_backup import backup_system
|
||||
from test_bbqr import readback_bbqr, render_bbqr, readback_bbqr_ll, try_sign_bbqr, split_scan_bbqr
|
||||
from test_bip39pw import set_bip39_pw
|
||||
from test_ccc import get_last_violation
|
||||
from test_drv_entro import derive_bip85_secret, activate_bip85_ephemeral
|
||||
from test_ephemeral import generate_ephemeral_words, import_ephemeral_xprv, goto_eph_seed_menu
|
||||
from test_ephemeral import ephemeral_seed_disabled_ui, restore_main_seed, confirm_tmp_seed
|
||||
|
||||
548
testing/test_sssp.py
Normal file
548
testing/test_sssp.py
Normal file
@ -0,0 +1,548 @@
|
||||
# (c) Copyright 2025 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# tests related to Single Signer Spending Policy feature (SSSP)
|
||||
#
|
||||
# run simulator without --eff
|
||||
#
|
||||
#
|
||||
import pytest, time, base64, os
|
||||
from psbt import BasicPSBT
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def goto_sssp_menu(goto_home, pick_menu_item, is_mark4):
|
||||
def doit():
|
||||
goto_home()
|
||||
pick_menu_item("Advanced/Tools")
|
||||
pick_menu_item("Spending Policy")
|
||||
pick_menu_item("Single-Signer")
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def setup_sssp(goto_sssp_menu, pick_menu_item, cap_story, press_select, pass_word_quiz, is_q1,
|
||||
seed_story_to_words, cap_menu, OK, word_menu_entry, press_cancel, press_delete,
|
||||
enter_number, scan_a_qr, cap_screen, settings_get, need_keypress, microsd_path,
|
||||
master_settings_get, enter_pin):
|
||||
|
||||
def doit(pin=None, mag=None, vel=None, whitelist=None, w2fa=None, has_violation=None,
|
||||
word_check=None, notes_and_pws=None, rel_keys=None):
|
||||
|
||||
goto_sssp_menu()
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
|
||||
# it is possible that PIN was set beforehand
|
||||
if title == "Spending Policy":
|
||||
assert "stops you from signing transactions unless conditions are met" in story
|
||||
assert "locked into a special mode" in story
|
||||
assert "First step is to define a new PIN" in story
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
scr = cap_screen()
|
||||
if "Spending Policy" in scr:
|
||||
what = "Enter first part of PIN" if is_q1 else "Enter PIN Prefix"
|
||||
assert what in scr
|
||||
|
||||
enter_pin(pin)
|
||||
time.sleep(.1)
|
||||
scr = cap_screen()
|
||||
what = "Confirm PIN value"if is_q1 else "CONFIRM PIN VALUE"
|
||||
assert what in scr
|
||||
enter_pin(pin)
|
||||
time.sleep(.1)
|
||||
|
||||
m = cap_menu()
|
||||
|
||||
assert "Edit Policy..." in m
|
||||
if has_violation is not None:
|
||||
if has_violation:
|
||||
assert "Last Violation" in m
|
||||
else:
|
||||
assert "last Violation" not in m
|
||||
|
||||
assert "Word Check" in m
|
||||
assert "Allow Notes" in m
|
||||
assert "Related Keys" in m
|
||||
assert "Remove Policy" in m
|
||||
assert "Test Drive" in m
|
||||
assert "ACTIVATE" in m
|
||||
|
||||
pick_menu_item("Edit Policy...")
|
||||
|
||||
whitelist_mi = "Whitelist Addresses" if is_q1 else "Whitelist"
|
||||
mag_mi = "Max Magnitude"
|
||||
vel_mi = "Limit Velocity"
|
||||
mi_2fa = "Web 2FA"
|
||||
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert mag_mi in m
|
||||
assert vel_mi in m
|
||||
assert whitelist_mi in m
|
||||
assert mi_2fa in m
|
||||
|
||||
# setting above values here
|
||||
if mag:
|
||||
pick_menu_item(mag_mi)
|
||||
enter_number(mag)
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert f"{mag} {'BTC' if int(mag) < 1000 else 'SATS'}" in story
|
||||
press_select()
|
||||
|
||||
time.sleep(.1)
|
||||
assert settings_get("sssp")["pol"]["mag"] == mag
|
||||
|
||||
if vel:
|
||||
if not settings_get("sssp")["pol"].get("mag", None):
|
||||
pick_menu_item(vel_mi)
|
||||
title, story = cap_story()
|
||||
assert 'Velocity limit requires' in story
|
||||
assert 'starting value' in story
|
||||
press_select()
|
||||
else:
|
||||
pick_menu_item(vel_mi)
|
||||
|
||||
if vel == "Unlimited":
|
||||
target = 0
|
||||
else:
|
||||
target = int(vel.split()[0])
|
||||
|
||||
pick_menu_item(vel) # actually a full menu item
|
||||
time.sleep(.3)
|
||||
assert settings_get("sssp")["pol"]["vel"] == target
|
||||
|
||||
if whitelist:
|
||||
pick_menu_item(whitelist_mi)
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "(none yet)" in m
|
||||
assert "Import from File" in m
|
||||
if is_q1:
|
||||
assert "Scan QR" in m
|
||||
pick_menu_item("Scan QR")
|
||||
for i, addr in enumerate(whitelist, start=1):
|
||||
scan_a_qr(addr)
|
||||
|
||||
for _ in range(10):
|
||||
scr = cap_screen()
|
||||
if (f"Got {i} so far" in scr) and ("ENTER to apply" in scr):
|
||||
break
|
||||
time.sleep(.2)
|
||||
else:
|
||||
assert False, "updating whitelist failed"
|
||||
|
||||
press_select()
|
||||
else:
|
||||
assert "Scan QR" not in m
|
||||
fname = "ccc_addrs.txt"
|
||||
with open(microsd_path(fname), "w") as f:
|
||||
for a in whitelist:
|
||||
f.write(f"{a}\n")
|
||||
|
||||
pick_menu_item("Import from File")
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
if "Press (1)" in story:
|
||||
need_keypress("1")
|
||||
pick_menu_item(fname)
|
||||
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
if len(whitelist) == 1:
|
||||
assert "Added new address to whitelist" in story
|
||||
else:
|
||||
assert f"Added {len(whitelist)} new addresses to whitelist" in story
|
||||
|
||||
for addr in whitelist:
|
||||
assert addr in story
|
||||
|
||||
# check menu correct
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
mi_addrs = [a for a in m if '⋯' in a]
|
||||
for mia, addr in zip(mi_addrs, reversed(whitelist)):
|
||||
_start, _end = mia.split('⋯')
|
||||
assert addr.startswith(_start)
|
||||
assert addr.endswith(_end)
|
||||
|
||||
press_cancel()
|
||||
|
||||
assert settings_get("sssp")["pol"]["addrs"] == whitelist
|
||||
|
||||
if w2fa:
|
||||
pick_menu_item(mi_2fa)
|
||||
|
||||
press_cancel() # leave Edit Policy... (shared settings with CCC)
|
||||
|
||||
# now rest of sssp specific settings
|
||||
if word_check is not None:
|
||||
pick_menu_item("Word Check")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert "addition to special PIN" in story
|
||||
assert "provide the first and last seed words" in story
|
||||
if word_check:
|
||||
assert "Enable?" in story
|
||||
press_select() # confirm action
|
||||
assert settings_get("sssp")["words"]
|
||||
else:
|
||||
assert "Disable?" in story
|
||||
pol = settings_get("sssp")
|
||||
if "words" in pol:
|
||||
assert not pol["words"]
|
||||
|
||||
if notes_and_pws is not None:
|
||||
pick_menu_item("Allow Notes")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert "Allow (read-only) access to secure notes and passwords?" in story
|
||||
if notes_and_pws:
|
||||
assert "Enable?" in story
|
||||
press_select() # confirm action
|
||||
assert settings_get("sssp")["notes"]
|
||||
else:
|
||||
assert "Disable?" in story
|
||||
pol = settings_get("sssp")
|
||||
if "notes" in pol:
|
||||
assert not pol["notes"]
|
||||
|
||||
if rel_keys is not None:
|
||||
pick_menu_item("Related Keys")
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert "Allow access to BIP-39 passphrase wallets" in story
|
||||
assert "or Seed Vault (if any)" in story
|
||||
if rel_keys:
|
||||
assert "Enable?" in story
|
||||
press_select() # confirm action
|
||||
assert settings_get("sssp")["okeys"]
|
||||
else:
|
||||
assert "Disable?" in story
|
||||
pol = settings_get("sssp")
|
||||
if "okeys" in pol:
|
||||
assert not pol["okeys"]
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def policy_sign(start_sign, end_sign, cap_story, get_last_violation):
|
||||
def doit(wallet, psbt, violation=None):
|
||||
start_sign(base64.b64decode(psbt))
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
|
||||
if violation:
|
||||
# assume SSSP cases
|
||||
assert title == "Failure"
|
||||
assert 'warning' not in story
|
||||
assert "Spending Policy violation." in story
|
||||
assert violation in get_last_violation()
|
||||
return
|
||||
|
||||
assert 'OK TO SEND?' == title
|
||||
assert "warning" not in story
|
||||
|
||||
signed = end_sign(accept=True)
|
||||
po = BasicPSBT().parse(signed)
|
||||
|
||||
tx_hex = None
|
||||
if violation is None:
|
||||
assert not get_last_violation()
|
||||
assert len(po.inputs[0].part_sigs) or po.inputs[0].taproot_key_sig
|
||||
res = wallet.finalizepsbt(base64.b64encode(signed).decode())
|
||||
assert res["complete"]
|
||||
tx_hex = res["hex"]
|
||||
res = wallet.testmempoolaccept([tx_hex])
|
||||
assert res[0]["allowed"]
|
||||
res = wallet.sendrawtransaction(tx_hex)
|
||||
assert len(res) == 64 # tx id
|
||||
|
||||
return signed, tx_hex
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def remove_settings_slots(settings_slots):
|
||||
for s in settings_slots():
|
||||
try:
|
||||
os.remove(s)
|
||||
except: pass
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize("mag_ok", [True, False])
|
||||
@pytest.mark.parametrize("mag", [1000000, 2])
|
||||
def test_magnitude(mag_ok, mag, setup_sssp, bitcoind, settings_set, pick_menu_item,
|
||||
bitcoind_d_sim_watch, policy_sign, press_select, sim_exec, settings_remove,
|
||||
reset_seed_words, settings_path, remove_settings_slots):
|
||||
|
||||
wo = bitcoind_d_sim_watch
|
||||
|
||||
settings_set("chain", "XRT")
|
||||
|
||||
if mag_ok:
|
||||
# always try limit/border value
|
||||
if mag is None:
|
||||
to_send = 1
|
||||
else:
|
||||
to_send = mag / 100000000 if mag > 1000 else mag
|
||||
else:
|
||||
if mag is None:
|
||||
to_send = 1.1
|
||||
else:
|
||||
to_send = ((mag / 100000000)+1) if mag > 1000 else (mag+0.001)
|
||||
|
||||
setup_sssp("11-11", mag=mag)
|
||||
|
||||
pick_menu_item("ACTIVATE")
|
||||
press_select()
|
||||
|
||||
addr = wo.getnewaddress()
|
||||
bitcoind.supply_wallet.sendtoaddress(address=addr, amount=5.0)
|
||||
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
|
||||
# create funded PSBT
|
||||
psbt_resp = wo.walletcreatefundedpsbt(
|
||||
[], [{bitcoind.supply_wallet.getnewaddress(): to_send}], 0, {"fee_rate": 2}
|
||||
)
|
||||
psbt = psbt_resp.get("psbt")
|
||||
|
||||
policy_sign(wo, psbt, violation=None if mag_ok else "magnitude")
|
||||
|
||||
# I could use test drive here
|
||||
settings_remove("sssp")
|
||||
sim_exec('from pincodes import pa;pa.hobbled_mode = False; from actions import goto_top_menu; goto_top_menu()')
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize("whitelist_ok", [True, False])
|
||||
def test_whitelist(whitelist_ok, setup_sssp, bitcoind, settings_set, policy_sign,
|
||||
bitcoind_d_sim_watch, settings_remove, sim_exec):
|
||||
|
||||
wo = bitcoind_d_sim_watch
|
||||
|
||||
settings_set("chain", "XRT")
|
||||
|
||||
whitelist = [
|
||||
"bcrt1qqca9eefwz8tzn7rk6aumhwhapyf5vsrtrddxxp",
|
||||
"bcrt1q7nck280nje50gzjja3gyguhp2ds6astu5ndhkj",
|
||||
"bcrt1qhexpvdhwuerqq0h24j06g8y5eumjjdr28ng4vv",
|
||||
"bcrt1q3ylr55pk7rl0rc06d8th7h25zmcuvvg8wt0yl3",
|
||||
]
|
||||
|
||||
if whitelist_ok:
|
||||
send_to = whitelist[0]
|
||||
else:
|
||||
send_to = bitcoind.supply_wallet.getnewaddress()
|
||||
|
||||
setup_sssp("11-11", whitelist=whitelist)
|
||||
|
||||
multi_addr = wo.getnewaddress()
|
||||
bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=5.0)
|
||||
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
|
||||
# create funded PSBT
|
||||
psbt_resp = wo.walletcreatefundedpsbt(
|
||||
[], [{send_to: 1}], 0, {"fee_rate": 2}
|
||||
)
|
||||
psbt = psbt_resp.get("psbt")
|
||||
policy_sign(wo, psbt, violation=None if whitelist_ok else "whitelist")
|
||||
|
||||
# I could use test drive here
|
||||
settings_remove("sssp")
|
||||
sim_exec('from pincodes import pa;pa.hobbled_mode = False; from actions import goto_top_menu; goto_top_menu()')
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize("velocity_mi", ['6 blocks (hour)', '48 blocks (8h)'])
|
||||
def test_velocity(velocity_mi, setup_sssp, bitcoind, settings_set, settings_remove,
|
||||
policy_sign, settings_get, bitcoind_d_sim_watch, sim_exec):
|
||||
|
||||
wo = bitcoind_d_sim_watch
|
||||
wo.keypoolrefill(20)
|
||||
settings_set("chain", "XRT")
|
||||
|
||||
blocks = int(velocity_mi.split()[0])
|
||||
|
||||
setup_sssp("11-11", vel=velocity_mi)
|
||||
|
||||
assert "block_h" not in settings_get("sssp")["pol"]
|
||||
|
||||
multi_addr = wo.getnewaddress()
|
||||
bitcoind.supply_wallet.sendtoaddress(address=multi_addr, amount=49)
|
||||
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
|
||||
# create funded PSBT, first tx
|
||||
init_block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] # block height
|
||||
psbt_resp = wo.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1}],
|
||||
init_block_height) # nLockTime set to current block height
|
||||
psbt = psbt_resp.get("psbt")
|
||||
po = BasicPSBT().parse(base64.b64decode(psbt))
|
||||
assert po.parsed_txn.nLockTime == init_block_height
|
||||
policy_sign(wo, psbt) # success as this is first tx that sets block height from 0
|
||||
|
||||
assert settings_get("sssp")["pol"]["block_h"] == init_block_height
|
||||
|
||||
# mine some, BUT not enough to satisfy velocity policy
|
||||
# - check velocity is exactly right to block number vs. required gap
|
||||
bitcoind.supply_wallet.generatetoaddress(blocks - 1, bitcoind.supply_wallet.getnewaddress())
|
||||
block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"]
|
||||
psbt_resp = wo.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1}],
|
||||
block_height)
|
||||
psbt = psbt_resp.get("psbt")
|
||||
po = BasicPSBT().parse(base64.b64decode(psbt))
|
||||
assert po.parsed_txn.nLockTime == block_height
|
||||
policy_sign(wo, psbt, violation="velocity")
|
||||
|
||||
assert settings_get("sssp")["pol"]["block_h"] == init_block_height # still initial block height as above failed
|
||||
|
||||
# mine the remaining one block to satisfy velocity policy
|
||||
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
|
||||
block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"]
|
||||
psbt_resp = wo.walletcreatefundedpsbt([], [{bitcoind.supply_wallet.getnewaddress(): 1}],
|
||||
block_height)
|
||||
psbt = psbt_resp.get("psbt")
|
||||
po = BasicPSBT().parse(base64.b64decode(psbt))
|
||||
assert po.parsed_txn.nLockTime == block_height
|
||||
policy_sign(wo, psbt) # success
|
||||
|
||||
assert settings_get("sssp")["pol"]["block_h"] == block_height # updated block height
|
||||
|
||||
# check txn re-sign fails (if velocity in effect)
|
||||
policy_sign(wo, psbt, violation="rewound")
|
||||
# check decreasing nLockTime
|
||||
policy_sign(
|
||||
wo,
|
||||
wo.walletcreatefundedpsbt(
|
||||
[], [{bitcoind.supply_wallet.getnewaddress(): 1}], block_height - 1
|
||||
)["psbt"],
|
||||
violation="rewound"
|
||||
)
|
||||
# check nLockTime disabled when velocity enabled - fail
|
||||
policy_sign(
|
||||
wo,
|
||||
wo.walletcreatefundedpsbt(
|
||||
[], [{bitcoind.supply_wallet.getnewaddress(): 1}], 0
|
||||
)["psbt"],
|
||||
violation="no nLockTime"
|
||||
)
|
||||
# unix timestamp
|
||||
policy_sign(
|
||||
wo,
|
||||
wo.walletcreatefundedpsbt(
|
||||
[], [{bitcoind.supply_wallet.getnewaddress(): 1}], 500000000
|
||||
)["psbt"],
|
||||
violation="nLockTime not height"
|
||||
)
|
||||
|
||||
# I could use test drive here
|
||||
settings_remove("sssp")
|
||||
sim_exec('from pincodes import pa;pa.hobbled_mode = False; from actions import goto_top_menu; goto_top_menu()')
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
def test_warnings(setup_sssp, bitcoind, settings_set, policy_sign, sim_exec,
|
||||
bitcoind_d_sim_watch, settings_get, settings_remove):
|
||||
|
||||
wo = bitcoind_d_sim_watch
|
||||
wo.keypoolrefill(20)
|
||||
|
||||
settings_set("chain", "XRT")
|
||||
|
||||
whitelist = ["bcrt1qlk39jrclgnawa42tvhu2n7se987qm96qg8v76e",
|
||||
"2Mxp1Dy2MyR4w36J2VaZhrFugNNFgh6LC1j",
|
||||
"mjR14oKxYzRg9RAZdpu3hrw8zXfFgGzLKm"]
|
||||
|
||||
setup_sssp("11-11", mag=10000000, vel='6 blocks (hour)', whitelist=whitelist)
|
||||
|
||||
bitcoind.supply_wallet.sendtoaddress(address=wo.getnewaddress(), amount=2)
|
||||
bitcoind.supply_wallet.generatetoaddress(1, bitcoind.supply_wallet.getnewaddress())
|
||||
# create funded PSBT, first tx
|
||||
# whitelist OK, velocity OK, & magnitude OK - but fee high
|
||||
init_block_height = bitcoind.supply_wallet.getblockchaininfo()["blocks"] # block height
|
||||
psbt_resp = wo.walletcreatefundedpsbt([], [{whitelist[0]: 0.06},{whitelist[1]: 0.01},{whitelist[2]: 0.03}],
|
||||
init_block_height, {"fee_rate":48000})
|
||||
psbt = psbt_resp.get("psbt")
|
||||
po = BasicPSBT().parse(base64.b64decode(psbt))
|
||||
assert po.parsed_txn.nLockTime == init_block_height
|
||||
policy_sign(wo, psbt, violation="has warnings")
|
||||
|
||||
# invalidate nLockTime with use of nSequence max values
|
||||
utxos = wo.listunspent()
|
||||
ins = []
|
||||
for i, utxo in enumerate(utxos):
|
||||
# block height based RTL
|
||||
inp = {
|
||||
"txid": utxo["txid"],
|
||||
"vout": utxo["vout"],
|
||||
"sequence": 0xffffffff,
|
||||
}
|
||||
ins.append(inp)
|
||||
|
||||
psbt_resp = wo.walletcreatefundedpsbt(ins, [{whitelist[0]: 0.06},{whitelist[1]: 0.01},{whitelist[2]: 0.03}],
|
||||
0, {"fee_rate":2, "replaceable": False}) # locktime needs to be zero, otherwise exception from core (contradicting parameters)
|
||||
po = BasicPSBT().parse(base64.b64decode(psbt_resp.get("psbt")))
|
||||
assert po.parsed_txn.nLockTime == 0
|
||||
po.parsed_txn.nLockTime = init_block_height # add locktime
|
||||
po.txn = po.parsed_txn.serialize_with_witness()
|
||||
# num_warn=2, warn_list=["Bad Locktime"]
|
||||
policy_sign(wo, po.as_b64_str(), violation="has warnings")
|
||||
|
||||
# exotic sighash warning
|
||||
settings_set("sighshchk", 1) # needed to only get warning instead of failure
|
||||
psbt_resp = wo.walletcreatefundedpsbt([], [{whitelist[0]: 0.06},{whitelist[1]: 0.01},{whitelist[2]: 0.03}],
|
||||
init_block_height, {"fee_rate":2, "replaceable": True})
|
||||
po = BasicPSBT().parse(base64.b64decode(psbt_resp.get("psbt")))
|
||||
for idx, i in enumerate(po.inputs):
|
||||
i.sighash = 2 # NONE
|
||||
|
||||
# num_warn=2, warn_list=["sighash NONE"]
|
||||
policy_sign(wo, po.as_b64_str(), violation="has warnings")
|
||||
|
||||
# I could use test drive here
|
||||
settings_remove("sssp")
|
||||
sim_exec('from pincodes import pa;pa.hobbled_mode = False; from actions import goto_top_menu; goto_top_menu()')
|
||||
|
||||
|
||||
def test_remove_sssp(setup_sssp, pick_menu_item, press_select, cap_story, cap_menu, settings_get):
|
||||
setup_sssp("11-11", mag=10000000, vel='6 blocks (hour)')
|
||||
# check test drive
|
||||
pick_menu_item("Test Drive")
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "COLDCARD operation will look like with Spending Policy" in story
|
||||
press_select()
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "EXIT TEST DRIVE" in m
|
||||
assert "Settings" not in m
|
||||
pick_menu_item("EXIT TEST DRIVE")
|
||||
time.sleep(.1)
|
||||
m = cap_menu()
|
||||
assert "Edit Policy..." in m # back in policy settings
|
||||
pick_menu_item("Remove Policy")
|
||||
time.sleep(.1)
|
||||
_, story = cap_story()
|
||||
assert "Bypass PIN will be removed" in story
|
||||
assert "spending policy settings forgotten" in story
|
||||
press_select()
|
||||
assert not settings_get("sssp")
|
||||
tps = settings_get("tp")
|
||||
if tps:
|
||||
assert "11-11" not in tps
|
||||
|
||||
|
||||
def test_use_main_pin_as_unlock(setup_sssp, cap_story):
|
||||
# not allowed
|
||||
# simulator PIN
|
||||
with pytest.raises(Exception):
|
||||
setup_sssp("12-12")
|
||||
|
||||
_, story = cap_story()
|
||||
assert "already in use" in story
|
||||
assert "PIN codes must be unique" in story
|
||||
|
||||
# EOF
|
||||
Loading…
Reference in New Issue
Block a user