109 lines
3.5 KiB
Python
109 lines
3.5 KiB
Python
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
|
#
|
|
# calc.py - Simple TOY calculator, before login. Not meant to be useful, just fun!
|
|
#
|
|
# Test with: ./simulator.py --q1 --eff -g --set calc=1
|
|
#
|
|
import utime, ngu, re
|
|
from utils import B2A, word_wrap
|
|
from ux_q1 import ux_input_text
|
|
|
|
async def login_repl():
|
|
from glob import dis
|
|
from pincodes import pa
|
|
|
|
NUM_LINES = 7 # 10 - title - 2 for prompt
|
|
|
|
# recognise 12-12 / 12- but also accept underscore, or even space in pin: "12 12"
|
|
re_prefix = re.compile(r'^(\d\d+)[-_]$')
|
|
re_pin = re.compile(r'^(\d\d+)[-_ ](\d\d+)$')
|
|
|
|
# in decreasing order of hazard...
|
|
# - find these with: import builtins; help(builtins)
|
|
blacklist = ['import', '__', 'exec', 'locals', 'globals', 'eval', 'input',
|
|
'getattr', 'setattr', 'delattr', 'open', 'execfile', 'compile' ]
|
|
|
|
lines = '''\
|
|
|
|
Example Commands:
|
|
>> 23 + 55 / 22
|
|
>> 1.020 * 45.88
|
|
>> sha256('some message')
|
|
>> cls # clear screen
|
|
>> help\
|
|
'''.split('\n')
|
|
|
|
state = dict()
|
|
state['sha256'] = lambda x: B2A(ngu.hash.sha256s(x))
|
|
state['sha512'] = lambda x: B2A(ngu.hash.sha512(x).digest())
|
|
state['ripemd'] = lambda x: B2A(ngu.hash.ripemd160(x))
|
|
state['rand'] = lambda x=32: B2A(ngu.random.bytes(x))
|
|
state['cls'] = lambda: lines.clear()
|
|
state['help'] = lambda: 'Commands: ' + (', '.join(state))
|
|
|
|
while 1:
|
|
dis.clear()
|
|
dis.text(0, 0, ' ECC Calculator ', invert=True)
|
|
for i,ln in enumerate(lines):
|
|
dis.text(0, i+1, ln, dark=ln.startswith('>> '))
|
|
|
|
dis.text(0, -2, '━'*34, dark=True)
|
|
dis.text(0, -1, '>> ')
|
|
|
|
# prompt always a bottom of screen
|
|
ln = await ux_input_text('', max_len=34-3, force_xy=(3, 9),
|
|
prompt='', min_len=1, scan_ok=True, placeholder=None)
|
|
|
|
lines.append('>> ' + (ln or ''))
|
|
ans = None
|
|
try:
|
|
dis.busy_bar(1)
|
|
|
|
if ln is None :
|
|
# Cancel key - do nothing
|
|
ans = None
|
|
elif ln in ('help', 'cls', 'rand'):
|
|
# no need for () for these commands
|
|
ans = state[ln]()
|
|
elif pa.attempts_left and re_pin.match(ln) and (len(ln) <= 13):
|
|
# try login
|
|
m = re_pin.match(ln)
|
|
ln = m.group(1)+ '-' + m.group(2)
|
|
|
|
try:
|
|
pa.setup(ln)
|
|
ok = pa.login()
|
|
if ok: return
|
|
except RuntimeError as exc:
|
|
# I'm a brick and other stuff can happen here
|
|
# - especially AUTH_FAIL when pin is just wrong.
|
|
if exc.args[0] == 'AUTH_FAIL':
|
|
pa.attempts_left -= 1
|
|
ans = '%-7d # %d tries remain' % (eval(ln), pa.attempts_left)
|
|
else:
|
|
ans = 'Error: ' + repr(exc.args)
|
|
|
|
elif re_prefix.match(ln) and (len(ln) <= 7):
|
|
# show words
|
|
ans = pa.prefix_words(ln[:-1].encode())
|
|
else:
|
|
if any((b in ln) for b in blacklist):
|
|
ans = None
|
|
else:
|
|
ans = eval(ln, state.copy())
|
|
|
|
except Exception as exc:
|
|
lines.extend(word_wrap(str(exc), 34))
|
|
finally:
|
|
dis.busy_bar(0)
|
|
|
|
if ans is not None:
|
|
here = repr(ans) if not isinstance(ans, str) else ans
|
|
lines.extend(word_wrap(here, 34))
|
|
|
|
# trim lines to fit (scroll)
|
|
lines = lines[-NUM_LINES:]
|
|
|
|
|
|
# EOF
|