# SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. # SPDX-License-Identifier: GPL-3.0-or-later # # SPDX-FileCopyrightText: 2018 Coinkite, Inc. # SPDX-License-Identifier: GPL-3.0-only # # (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard # and is covered by GPLv3 license found in COPYING. # # utils.py # import gc, sys, ustruct from ubinascii import unhexlify as a2b_hex from ubinascii import hexlify as b2a_hex from ubinascii import a2b_base64, b2a_base64 class imported: # Context manager that temporarily imports # a list of modules. # LATER: doubtful this saves any memory when all the code is frozen. def __init__(self, *modules): self.modules = modules def __enter__(self): # import everything required rv = tuple(__import__(n) for n in self.modules) return rv[0] if len(self.modules) == 1 else rv def __exit__(self, exc_type, exc_value, traceback): for n in self.modules: if n in sys.modules: del sys.modules[n] # recovery that tasty memory. gc.collect() def pretty_delay(n): # decode # of seconds into various ranges, need not be precise. if n < 120: return '%d seconds' % n n /= 60 if n < 60: return '%d minutes' % n n /= 60 if n < 48: return '%.1f hours' % n n /= 24 return 'about %d days' % n def pretty_short_delay(sec): # precise, shorter on screen display if sec >= 3600: return '%2dh %2dm %2ds' % (sec //3600, (sec//60) % 60, sec % 60) else: return '%2dm %2ds' % ((sec//60) % 60, sec % 60) def pop_count(i): # 32-bit population count for integers # from i = i - ((i >> 1) & 0x55555555) i = (i & 0x33333333) + ((i >> 2) & 0x33333333) return (((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) & 0xffffffff) >> 24 def get_filesize(fn): # like os.path.getsize() import uos return uos.stat(fn)[6] class HexWriter: # Emulate a file/stream but convert binary to hex as they write def __init__(self, fd): self.fd = fd def __enter__(self): self.fd.__enter__() return self def __exit__(self, *a, **k): self.fd.write(b'\r\n') return self.fd.__exit__(*a, **k) def write(self, b): self.fd.write(b2a_hex(b)) class Base64Writer: # Emulate a file/stream but convert binary to Base64 as they write def __init__(self, fd): self.fd = fd self.runt = b'' def __enter__(self): self.fd.__enter__() return self def __exit__(self, *a, **k): if self.runt: self.fd.write(b2a_base64(self.runt)) self.fd.write(b'\r\n') return self.fd.__exit__(*a, **k) def write(self, buf): if self.runt: buf = self.runt + buf rl = len(buf) % 3 self.runt = buf[-rl:] if rl else b'' if rl < len(buf): tmp = b2a_base64(buf[:(-rl if rl else None)]) # library puts in newlines!? assert tmp[-1:] == b'\n', tmp assert tmp[-2:-1] != b'=', tmp self.fd.write(tmp[:-1]) def swab32(n): # endian swap: 32 bits return ustruct.unpack('>I', ustruct.pack('= FW_HEADER_SIZE magic_value, timestamp, version_string, pk, fw_size, install_flags, hw_compat = \ unpack_from(FWH_PY_FORMAT, hdr)[0:7] if bad_magic_ok and magic_value != FW_HEADER_MAGIC: # it's just not a firmware file, and that's ok return None assert magic_value == FW_HEADER_MAGIC, 'bad magic' if binary_size is not None: assert fw_size == binary_size, 'truncated' # TODO: maybe show the version string? Warn them that downgrade doesn't work? except Exception as exc: return "That does not look like a firmware " \ "file we would want to use: %s" % exc if hw_compat != 0: # check this hardware is compatible ok = False if hw_label == 'mk1': ok = (hw_compat & MK_1_OK) elif hw_label == 'mk2': ok = (hw_compat & MK_2_OK) elif hw_label == 'mk3': ok = (hw_compat & MK_3_OK) if not ok: return "New firmware doesn't support this version of Coldcard hardware (%s)."%hw_label return None def clean_shutdown(style=0): # wipe SPI flash and shutdown (wiping main memory) import callgate try: from common import sf sf.wipe_most() except: pass callgate.show_logout(style) class UXStateMachine: def __init__(self, initial_state, machine_name=None): print('UXStateMachine init: initial_state={}'.format(initial_state)) self.state = initial_state self.prev_states = [] def goto(self, new_state): print('Go from {} to {}'.format(self.state, new_state)) self.prev_states.append(self.state) self.state = new_state # Transition back to previous state def goto_prev(self): if len(self.prev_state) > 0: prev_state = self.prev_state.pop() if self.machine_name != None: print('{}: Go from {} to PREVIOUS state {}'.format(self.machine_name, self.state, prev_state)) else: print('Go from {} to PREVIOUS state {}'.format(self.state, prev_state)) self.state = prev_state async def show(self): pass # EOF