mk4 changes
This commit is contained in:
parent
28cffb543a
commit
5b0cd6fc5f
@ -20,7 +20,7 @@ def pytest_addoption(parser):
|
||||
parser.addoption("--manual", action="store_true",
|
||||
default=False, help="operator must press keys on real CC")
|
||||
|
||||
parser.addoption("--mk", default=3, help="Assume mark N hardware")
|
||||
parser.addoption("--mk", default=4, help="Assume mark N hardware")
|
||||
|
||||
parser.addoption("--duress", action="store_true",
|
||||
default=False, help="assume logged-in with duress PIN")
|
||||
@ -64,8 +64,9 @@ def simulator(request):
|
||||
def sim_exec(dev):
|
||||
# run code in the simulator's interpretor
|
||||
|
||||
def doit(cmd):
|
||||
def doit(cmd, binary=False):
|
||||
s = dev.send_recv(b'EXEC' + cmd.encode('utf-8'))
|
||||
if binary: return s
|
||||
return s.decode('utf-8') if not isinstance(s, str) else s
|
||||
|
||||
return doit
|
||||
@ -574,7 +575,7 @@ def reset_seed_words(sim_exec, sim_execfile, simulator):
|
||||
def settings_set(sim_exec):
|
||||
|
||||
def doit(key, val):
|
||||
x = sim_exec("nvstore.settings.set('%s', %r)" % (key, val))
|
||||
x = sim_exec("settings.set('%s', %r)" % (key, val))
|
||||
assert x == ''
|
||||
|
||||
return doit
|
||||
@ -583,7 +584,7 @@ def settings_set(sim_exec):
|
||||
def settings_get(sim_exec):
|
||||
|
||||
def doit(key):
|
||||
cmd = f"RV.write(repr(nvstore.settings.get('{key}')))"
|
||||
cmd = f"RV.write(repr(settings.get('{key}')))"
|
||||
resp = sim_exec(cmd)
|
||||
assert 'Traceback' not in resp, resp
|
||||
return eval(resp)
|
||||
@ -594,7 +595,7 @@ def settings_get(sim_exec):
|
||||
def settings_remove(sim_exec):
|
||||
|
||||
def doit(key):
|
||||
x = sim_exec("nvstore.settings.remove_key('%s')" % key)
|
||||
x = sim_exec("settings.remove_key('%s')" % key)
|
||||
assert x == ''
|
||||
|
||||
return doit
|
||||
@ -1000,8 +1001,34 @@ def is_mark2(request):
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def is_mark3(request):
|
||||
# better: ask it
|
||||
return int(request.config.getoption('--mk')) == 3
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def is_mark4(request):
|
||||
# better: ask it
|
||||
return int(request.config.getoption('--mk')) == 4
|
||||
|
||||
@pytest.fixture()
|
||||
def nfc_read(sim_exec):
|
||||
def doit():
|
||||
rv = sim_exec('RV.write(NFC.dump_ndef())', binary=True)
|
||||
if b'Traceback' in rv: raise pytest.fail(rv.decode('utf-8'))
|
||||
return rv
|
||||
return doit
|
||||
|
||||
@pytest.fixture
|
||||
def load_shared_mod():
|
||||
# load indicated file.py as a module
|
||||
# from <https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path>
|
||||
def doit(name, path):
|
||||
import importlib.util
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
return doit
|
||||
|
||||
# useful fixtures related to multisig
|
||||
from test_multisig import (import_ms_wallet, make_multisig, offer_ms_import, fake_ms_txn,
|
||||
make_ms_address, clear_ms, make_myself_wallet)
|
||||
|
||||
@ -10,7 +10,7 @@ from ubinascii import hexlify as b2a_hex
|
||||
from ubinascii import unhexlify as a2b_hex
|
||||
|
||||
import ngu, ustruct
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
|
||||
if 1:
|
||||
# test file contents: completeness, syntax
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
# quickly main wipe seed; don't install anything new
|
||||
from glob import numpad, dis
|
||||
from pincodes import pa
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from pincodes import AE_SECRET_LEN, PA_IS_BLANK
|
||||
|
||||
if not pa.is_secret_blank():
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
import main
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from ujson import dumps
|
||||
RV.write(dumps(settings.get(main.SKEY)))
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from ujson import dumps
|
||||
RV.write(dumps(settings.current))
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ from ubinascii import unhexlify as a2b_hex
|
||||
|
||||
import ustruct
|
||||
from sflash import SF
|
||||
from nvstore import SLOTS, settings
|
||||
from glob import SLOTS, settings
|
||||
|
||||
# reset whatever's there
|
||||
SF.chip_erase()
|
||||
|
||||
@ -5,7 +5,7 @@ from sim_settings import sim_defaults
|
||||
import stash, chains
|
||||
from h import b2a_hex
|
||||
from pincodes import pa
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from stash import SecretStash, SensitiveValues
|
||||
from utils import xfp2str
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ from sim_settings import sim_defaults
|
||||
import stash, chains
|
||||
from h import b2a_hex
|
||||
from pincodes import pa
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from stash import SecretStash, SensitiveValues
|
||||
from utils import xfp2str
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ from sim_settings import sim_defaults
|
||||
import stash, chains
|
||||
from h import b2a_hex
|
||||
from pincodes import pa
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
import stash
|
||||
from seed import set_seed_value
|
||||
from utils import xfp2str
|
||||
|
||||
@ -6,7 +6,7 @@ from sim_settings import sim_defaults
|
||||
import stash, chains
|
||||
from h import b2a_hex
|
||||
from pincodes import pa
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from stash import SecretStash, SensitiveValues
|
||||
from utils import xfp2str, swab32
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ from ubinascii import hexlify as b2a_hex
|
||||
from ubinascii import unhexlify as a2b_hex
|
||||
|
||||
import ustruct
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from public_constants import MAX_TXN_LEN
|
||||
|
||||
# load PSBT into simulated SPI Flash
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# quickly clear all multisig wallets installed
|
||||
from nvstore import settings
|
||||
from glob import settings
|
||||
from ux import restore_menu
|
||||
|
||||
if settings.get('multisig'):
|
||||
|
||||
@ -5,3 +5,8 @@ markers =
|
||||
bitcoind: indicates local bitcoind (testnet) will be needed
|
||||
onetime: test cant be combined with any others, likely needs board reset
|
||||
veryslow: test takes more than 30 minutes realtime
|
||||
|
||||
# this isn't working FIXME
|
||||
filterwarnings =
|
||||
ignore:.*DeprecationWarning.*
|
||||
|
||||
|
||||
@ -10,3 +10,6 @@ onetimepass==1.0.1
|
||||
|
||||
# for QR scanning (pulls in numpy)
|
||||
zbar-py==1.0.4
|
||||
|
||||
nfcpy==1.0.3
|
||||
ndef==0.3.3
|
||||
|
||||
@ -155,10 +155,27 @@ def test_speed_test(request, fake_txn, is_mark3, start_sign, end_sign, dev, need
|
||||
print(" Tx time: %.1f" % tx_time)
|
||||
print("Sign time: %.1f" % ready_time)
|
||||
|
||||
if 0:
|
||||
# TODO: attempt to re-create the mega transaction: 5,569 inputs, one out
|
||||
# see <https://bitcoin.stackexchange.com/questions/11542>
|
||||
# - how big woudl PSBT be?
|
||||
# - not a great test case because so slow.
|
||||
def test_mega_txn(fake_txn, is_mark4, start_sign, end_sign, dev):
|
||||
if not is_mark4:
|
||||
raise pytest.xfail('no way')
|
||||
|
||||
psbt = fake_txn(5569, 1, dev.master_xpub)
|
||||
|
||||
open('debug/mega.psbt', 'wb').write(psbt)
|
||||
|
||||
_, txn = try_sign(psbt, accept=True, finalize=True)
|
||||
|
||||
open('debug/mega.txn', 'wb').write(txn)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('segwit', [True, False])
|
||||
@pytest.mark.parametrize('out_style', ADDR_STYLES)
|
||||
def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3,
|
||||
def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3, is_mark4,
|
||||
start_sign, end_sign, dev, segwit, out_style, accept = True):
|
||||
|
||||
# try a bunch of different bigger sized txns
|
||||
@ -170,14 +187,15 @@ def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3,
|
||||
# - only mk3 can do full amounts
|
||||
# - time on mk3, v4.0.0 firmware: 13 minutes
|
||||
|
||||
if not is_mark3:
|
||||
num_in = 10
|
||||
num_out = 10
|
||||
else:
|
||||
num_in = 10
|
||||
num_out = 10
|
||||
|
||||
if is_mark3:
|
||||
num_in = 20
|
||||
num_out = 250
|
||||
|
||||
|
||||
elif is_mark4:
|
||||
num_in = 250
|
||||
num_out = 2000
|
||||
|
||||
psbt = fake_txn(num_in, num_out, dev.master_xpub, segwit_in=segwit, outstyles=[out_style])
|
||||
|
||||
@ -196,6 +214,8 @@ def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3,
|
||||
|
||||
signed = end_sign(accept, finalize=True)
|
||||
|
||||
open('debug/signed.txn', 'wb').write(signed)
|
||||
|
||||
decoded = decode_with_bitcoind(signed)
|
||||
|
||||
#print("Bitcoin code says:", end=''); pprint(decoded)
|
||||
@ -1170,7 +1190,7 @@ def test_txid_calc(num_ins, fake_txn, try_sign, dev, segwit, decode_with_bitcoin
|
||||
title, story = cap_story()
|
||||
assert '0' in story
|
||||
assert 'TXID' in title, story
|
||||
txid = story.strip()
|
||||
txid = story.split()[0]
|
||||
|
||||
if 1:
|
||||
# compare to PyCoin
|
||||
@ -1395,4 +1415,48 @@ def test_value_render(units, fake_txn, start_sign, cap_story, settings_set, sett
|
||||
|
||||
settings_remove('rz')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('num_outs', [ 1, 20, 250])
|
||||
def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story):
|
||||
# Read signing result over NFC, decode it.
|
||||
import ndef
|
||||
from hashlib import sha256
|
||||
psbt = fake_txn(1, num_outs)
|
||||
orig, result = try_sign(psbt, accept=True, finalize=True)
|
||||
|
||||
too_big = len(result) > 8000
|
||||
|
||||
if too_big: assert num_outs > 100
|
||||
if num_outs > 100: assert too_big
|
||||
|
||||
time.sleep(.1)
|
||||
title, story = cap_story()
|
||||
assert 'TXID' in title, story
|
||||
txid = a2b_hex(story.split()[0])
|
||||
assert 'Press 3' in story
|
||||
need_keypress('3')
|
||||
|
||||
if too_big:
|
||||
title, story = cap_story()
|
||||
assert 'is too large' in story
|
||||
return
|
||||
|
||||
contents = nfc_read()
|
||||
#need_keypress('x')
|
||||
|
||||
#print("contents = " + B2A(contents))
|
||||
for got in ndef.message_decoder(contents):
|
||||
if got.type == 'urn:nfc:wkt:T':
|
||||
assert 'Transaction' in got.text
|
||||
assert b2a_hex(txid).decode() in got.text
|
||||
elif got.type == 'urn:nfc:ext:bitcoin.org:txid':
|
||||
assert got.data == txid
|
||||
elif got.type == 'urn:nfc:ext:bitcoin.org:txn':
|
||||
assert got.data == result
|
||||
elif got.type == 'urn:nfc:ext:bitcoin.org:sha256':
|
||||
assert got.data == sha256(result).digest()
|
||||
else:
|
||||
raise ValueError(got.type)
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
@ -239,5 +239,86 @@ def test_match_deriv_path(patterns, paths, answers, sim_exec):
|
||||
rv = sim_exec(cmd)
|
||||
assert rv == str(bool(ans))
|
||||
|
||||
@pytest.mark.parametrize('case', range(6))
|
||||
def test_ndef(case, load_shared_mod):
|
||||
# NDEF unit tests
|
||||
import ndef
|
||||
from struct import pack, unpack
|
||||
from binascii import b2a_hex
|
||||
|
||||
def get_body(efile):
|
||||
# unwrap CC_FILE and cruft
|
||||
assert efile[-1] == 0xfe
|
||||
assert efile[0] == 0xE2
|
||||
st = len(cc_ndef.CC_FILE)
|
||||
if efile[st] == 0xff:
|
||||
xl = unpack('>H', efile[st+1:st+3])[0]
|
||||
st += 3
|
||||
else:
|
||||
xl = efile[st]
|
||||
st += 1
|
||||
body = efile[st:-1]
|
||||
assert len(body) == xl
|
||||
return body
|
||||
|
||||
def decode(msg):
|
||||
return list(ndef.message_decoder(get_body(msg)))
|
||||
|
||||
cc_ndef = load_shared_mod('cc_ndef', '../shared/ndef.py')
|
||||
n = cc_ndef.ndefMaker()
|
||||
|
||||
if case == 0:
|
||||
n.add_text("Hello world")
|
||||
|
||||
got, = decode(n.bytes())
|
||||
assert got.type == 'urn:nfc:wkt:T'
|
||||
assert got.text == 'Hello world'
|
||||
assert got.language == 'en'
|
||||
assert got.encoding == 'UTF-8'
|
||||
|
||||
elif case == 1:
|
||||
n.add_text("Hello world")
|
||||
n.add_url("store.coinkite.com/store/coldcard")
|
||||
|
||||
txt,url = decode(n.bytes())
|
||||
assert txt.text == 'Hello world'
|
||||
|
||||
assert url.type == 'urn:nfc:wkt:U'
|
||||
assert url.uri == 'https://store.coinkite.com/store/coldcard' == url.iri
|
||||
|
||||
elif case == 2:
|
||||
hx = b2a_hex(bytes(range(32)))
|
||||
n.add_text("Title")
|
||||
n.add_custom('bitcoin.org:sha256', hx)
|
||||
|
||||
txt,sha = decode(n.bytes())
|
||||
assert txt.text == 'Title'
|
||||
assert sha.data == hx
|
||||
|
||||
elif case == 3:
|
||||
psbt = b'psbt\xff' + bytes(5000)
|
||||
n.add_text("Title")
|
||||
n.add_custom('bitcoin.org:psbt', psbt)
|
||||
n.add_text("Footer")
|
||||
|
||||
txt,p,ft = decode(n.bytes())
|
||||
assert txt.text == 'Title'
|
||||
assert ft.text == 'Footer'
|
||||
assert p.data == psbt
|
||||
assert p.type == 'urn:nfc:ext:bitcoin.org:psbt'
|
||||
|
||||
elif case == 4:
|
||||
hx = b2a_hex(bytes(range(32)))
|
||||
n.add_custom('bitcoin.org:txid', hx)
|
||||
got, = decode(n.bytes())
|
||||
assert got.type == 'urn:nfc:ext:bitcoin.org:txid'
|
||||
assert got.data == hx
|
||||
|
||||
elif case == 5:
|
||||
hx = bytes(2000)
|
||||
n.add_custom('bitcoin.org:txn', hx)
|
||||
got, = decode(n.bytes())
|
||||
assert got.type == 'urn:nfc:ext:bitcoin.org:txn'
|
||||
assert got.data == hx
|
||||
|
||||
# EOF
|
||||
|
||||
Loading…
Reference in New Issue
Block a user