# (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