Interim commit

This commit is contained in:
kdmukai
2023-08-05 10:00:03 -05:00
parent 886391bbf0
commit 8650841333
30 changed files with 178 additions and 103 deletions
+58 -2
View File
@@ -1,4 +1,60 @@
from seedsigner.controller import Controller
from time import time
master_start = time()
start = time()
from seedsigner.controller import Controller
print("main.py time elapsed to import Controller:", time() - start)
# Get the one and only Controller instance and start our main loop
Controller.get_instance().start()
controller = Controller.get_instance()
print("main.py time elapsed to import and initialize Controller:", time() - start)
start = time()
from seedsigner.models.threads import BaseThread
class BackgroundImportThread(BaseThread):
def run(self):
start = time()
from importlib import import_module
# import seedsigner.hardware.buttons # slowly imports GPIO along the way
def time_import(module_name):
last = time()
import_module(module_name)
print(time() - last, module_name)
time_import('embit')
time_import('seedsigner.helpers.embit_utils')
# Do costly initializations
time_import('seedsigner.models.seed_storage')
from seedsigner.models.seed_storage import SeedStorage
Controller.get_instance().storage = SeedStorage()
# Get MainMenuView ready to respond quickly
time_import('seedsigner.views.scan_views')
time_import('seedsigner.views.seed_views')
time_import('seedsigner.views.tools_views')
time_import('seedsigner.views.settings_views')
# Lowest priority costly initializations
# time_import('picamera')
# time_import('picamera.array')
# time_import('seedsigner.hardware.pivideostream')
print("Total BackgroundImportThread import time:", time() - start)
exit()
background_import_thread = BackgroundImportThread()
background_import_thread.start()
print("main.py time elapsed through BackgroundImportThread.start():", time() - start)
print("*" * 80)
print("\nmain.py OVERALL time elapsed until Controller.start():", time() - master_start)
print("\n" + "*" * 80)
controller.start()
+56 -33
View File
@@ -1,26 +1,29 @@
print("controller.py")
from time import time
start = time()
print("Starting Controller import...")
import logging
import traceback
import os
from embit.descriptor import Descriptor
from embit.psbt import PSBT
# from embit.descriptor import Descriptor
# from embit.psbt import PSBT
from PIL.Image import Image
from typing import List
from seedsigner.gui.renderer import Renderer
from seedsigner.hardware.buttons import HardwareButtons
from seedsigner.hardware.microsd import MicroSD
from seedsigner.views.screensaver import ScreensaverScreen
from seedsigner.views.view import Destination, NotYetImplementedView, UnhandledExceptionView
from seedsigner.views.view import Destination
from .models import Seed, SeedStorage, Settings, Singleton, PSBTParser
from .models.settings import Settings
from .models.singleton import Singleton
logger = logging.getLogger(__name__)
print("Total Controller import time:", time() - start)
class BackStack(List[Destination]):
class BackStack(list[Destination]):
def __repr__(self):
if len(self) == 0:
return "[]"
@@ -40,6 +43,7 @@ class StopFlowBasedTest(Exception):
pass
class FlowBasedTestException(Exception):
"""
This is a special exception that is only raised by the test suite.
@@ -71,22 +75,20 @@ class Controller(Singleton):
# Declare class member vars with type hints to enable richer IDE support throughout
# the code.
buttons: HardwareButtons = None
storage: SeedStorage = None
storage: 'SeedStorage' = None # TODO: Rename "storage" to something more indicative of its temp, in-memory state
settings: Settings = None
renderer: Renderer = None
# TODO: Refactor these flow-related attrs that survive across multiple Screens.
# TODO: Should all in-memory flow-related attrs get wiped on MainMenuView?
psbt: PSBT = None
psbt_seed: Seed = None
psbt_parser: PSBTParser = None
psbt: 'embit.psbt.PSBT' = None
psbt_seed: 'Seed' = None
psbt_parser: 'PSBTParser' = None
unverified_address = None
multisig_wallet_descriptor: Descriptor = None
multisig_wallet_descriptor: 'embit.descriptor.Descriptor' = None
image_entropy_preview_frames: List[Image] = None
image_entropy_preview_frames: list[Image] = None
image_entropy_final_image: Image = None
address_explorer_data: dict = None
@@ -102,7 +104,7 @@ class Controller(Singleton):
resume_main_flow: str = None
back_stack: BackStack = None
screensaver: ScreensaverScreen = None
screensaver: 'ScreensaverScreen' = None
@classmethod
@@ -127,6 +129,10 @@ class Controller(Singleton):
each time you try to re-initialize a Controller.
"""
start = time()
from seedsigner.gui.renderer import Renderer
from seedsigner.hardware.microsd import MicroSD
# Must be called before the first get_instance() call
if cls._instance:
raise Exception("Instance already configured")
@@ -135,15 +141,7 @@ class Controller(Singleton):
controller = cls.__new__(cls)
cls._instance = controller
# Input Buttons
if disable_hardware:
controller.buttons = None
else:
controller.buttons = HardwareButtons.get_instance()
# models
# TODO: Rename "storage" to something more indicative of its temp, in-memory state
controller.storage = SeedStorage()
controller.settings = Settings.get_instance()
controller.microsd = MicroSD.get_instance()
@@ -156,13 +154,19 @@ class Controller(Singleton):
# Configure the Renderer
Renderer.configure_instance()
controller.screensaver = ScreensaverScreen(controller.buttons)
controller.back_stack = BackStack()
# Other behavior constants
controller.screensaver_activation_ms = 120 * 1000
print(f"Controller configured in {time() - start:.3f}s")
start = time()
background_import_thread = BackgroundImportThread()
background_import_thread.start()
print("Time elapsed through BackgroundImportThread.start():", time() - start)
return cls._instance
@@ -172,7 +176,7 @@ class Controller(Singleton):
return Camera.get_instance()
def get_seed(self, seed_num: int) -> Seed:
def get_seed(self, seed_num: int) -> 'Seed':
if seed_num < len(self.storage.seeds):
return self.storage.seeds[seed_num]
else:
@@ -187,7 +191,6 @@ class Controller(Singleton):
def pop_prev_from_back_stack(self):
from .views import Destination
if len(self.back_stack) > 0:
# Pop the top View (which is the current View_cls)
self.back_stack.pop()
@@ -212,9 +215,16 @@ class Controller(Singleton):
from .views import MainMenuView, BackStackView
from .views.screensaver import OpeningSplashScreen
start = time()
opening_splash = OpeningSplashScreen()
print(f"Instantiating opening splash screen took {time() - start:.3f}s")
start = time()
opening_splash.start()
print(f"Opening splash screen took {time() - start:.3f}s")
""" Class references can be stored as variables in python!
This loop receives a View class to execute and stores it in the `View_cls`
@@ -288,6 +298,7 @@ class Controller(Singleton):
if not next_destination:
# Should only happen during dev when you hit an unimplemented option
from seedsigner.views.view import NotYetImplementedView
next_destination = Destination(NotYetImplementedView)
if next_destination.skip_current_view:
@@ -320,7 +331,8 @@ class Controller(Singleton):
print("-" * 30)
finally:
if self.screensaver.is_running:
from seedsigner.gui.renderer import Renderer
if self.is_screensaver_running:
self.screensaver.stop()
# Clear the screen when exiting
@@ -328,7 +340,17 @@ class Controller(Singleton):
Renderer.get_instance().display_blank_screen()
@property
def is_screensaver_running(self):
return self.screensaver is not None and self.screensaver.is_running
def start_screensaver(self):
if not self.screensaver:
# Do a lazy/late import and instantiation to reduce Controller initial startup time
from seedsigner.views.screensaver import ScreensaverScreen
from seedsigner.hardware.buttons import HardwareButtons
self.screensaver = ScreensaverScreen(HardwareButtons.get_instance())
self.screensaver.start()
@@ -342,6 +364,7 @@ class Controller(Singleton):
* python file, line num, method name
* Exception message
"""
from seedsigner.views.view import UnhandledExceptionView
logger.exception(e)
# The final exception output line is:
+8 -4
View File
@@ -7,9 +7,9 @@ from decimal import Decimal
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from typing import List, Tuple
from seedsigner.models import Singleton
from seedsigner.models.settings import Settings
from seedsigner.models.settings_definition import SettingsConstants
from seedsigner.models.singleton import Singleton
# TODO: Remove all pixel hard coding
@@ -610,13 +610,15 @@ class ToastOverlay(BaseComponent):
def render(self):
import time
from seedsigner.controller import Controller
from seedsigner.hardware.buttons import HardwareButtons
self.controller: Controller = Controller.get_instance()
self.current_screen = self.renderer.canvas.copy()
buttons = HardwareButtons.get_instance()
# Special case when screensaver is running
if self.controller.screensaver._is_running:
self.controller.buttons.override_ind = True
if self.controller.is_screensaver_running:
buttons.override_ind = True
self.image_draw.rounded_rectangle(
( GUIConstants.EDGE_PADDING + 2, self.canvas_height - 60, self.canvas_width - GUIConstants.EDGE_PADDING - 2, self.canvas_width - GUIConstants.EDGE_PADDING - 2),
@@ -633,11 +635,13 @@ class ToastOverlay(BaseComponent):
t_end = time.time() + 3
while time.time() < t_end:
if self.controller.buttons.has_any_input():
if buttons.has_any_input():
break
self.renderer.show_image(self.current_screen)
@dataclass
class FormattedAddress(BaseComponent):
"""
+2 -4
View File
@@ -1,9 +1,9 @@
from PIL import Image, ImageDraw, ImageFont
from PIL import Image, ImageDraw
from threading import Lock
from seedsigner.gui.components import Fonts, GUIConstants
from seedsigner.hardware.ST7789 import ST7789
from seedsigner.models import ConfigurableSingleton
from seedsigner.models.singleton import ConfigurableSingleton
@@ -19,8 +19,6 @@ class Renderer(ConfigurableSingleton):
@classmethod
def configure_instance(cls):
from seedsigner.models.settings import Settings
# Instantiate the one and only Renderer instance
renderer = cls.__new__(cls)
cls._instance = renderer
+1 -1
View File
@@ -6,7 +6,7 @@ from PIL import Image, ImageDraw
from seedsigner.gui import renderer
from seedsigner.hardware.buttons import HardwareButtonsConstants
from seedsigner.hardware.camera import Camera
from seedsigner.models import DecodeQR, DecodeQRStatus
from seedsigner.models.decode_qr import DecodeQR, DecodeQRStatus
from seedsigner.models.threads import BaseThread
from .screen import BaseScreen, ButtonListScreen
+3 -5
View File
@@ -7,12 +7,10 @@ from seedsigner.gui.keyboard import Keyboard, TextEntryDisplay
from seedsigner.gui.renderer import Renderer
from seedsigner.models.threads import BaseThread, ThreadsafeCounter
from seedsigner.models.encode_qr import EncodeQR
from seedsigner.models.settings import SettingsConstants
from ..components import (FontAwesomeIconConstants, GUIConstants, BaseComponent, Button, Icon, IconButton,
LargeIconButton, SeedSignerCustomIconConstants, TopNav, TextArea, load_image, ToastOverlay,
Fonts)
LargeIconButton, SeedSignerCustomIconConstants, TopNav, TextArea, load_image, ToastOverlay)
from seedsigner.hardware.buttons import HardwareButtonsConstants, HardwareButtons
@@ -660,10 +658,10 @@ class LargeButtonScreen(BaseTopNavScreen):
@dataclass
class QRDisplayScreen(BaseScreen):
qr_encoder: EncodeQR = None
qr_encoder: 'EncodeQR' = None
class QRDisplayThread(BaseThread):
def __init__(self, qr_encoder: EncodeQR, qr_brightness: ThreadsafeCounter, renderer: Renderer,
def __init__(self, qr_encoder: 'EncodeQR', qr_brightness: ThreadsafeCounter, renderer: Renderer,
tips_start_time: ThreadsafeCounter):
super().__init__()
self.qr_encoder = qr_encoder
+2 -7
View File
@@ -2,21 +2,16 @@ import math
import time
from dataclasses import dataclass
from typing import List, Tuple
from typing import List
from PIL import Image, ImageDraw, ImageFilter
from seedsigner.gui.renderer import Renderer
from seedsigner.helpers.qr import QR
from seedsigner.models.qr_type import QRType
from seedsigner.models.threads import BaseThread, ThreadsafeCounter
from seedsigner.models.seed import Seed
from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition
from .screen import RET_CODE__BACK_BUTTON, BaseScreen, BaseTopNavScreen, ButtonListScreen, KeyboardScreen, WarningEdgesMixin
from ..components import (Button, FontAwesomeIconConstants, Fonts, FormattedAddress, IconButton,
IconTextLine, SeedSignerCustomIconConstants, TextArea, GUIConstants,
calc_text_centering)
IconTextLine, SeedSignerCustomIconConstants, TextArea, GUIConstants)
from seedsigner.gui.keyboard import Keyboard, TextEntryDisplay
from seedsigner.hardware.buttons import HardwareButtons, HardwareButtonsConstants
+1 -1
View File
@@ -72,7 +72,7 @@ class HardwareButtons(Singleton):
while True:
cur_time = int(time.time() * 1000)
if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.screensaver.is_running:
if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.is_screensaver_running:
# Start the screensaver. Will block execution until input detected.
controller.start_screensaver()
+2 -3
View File
@@ -2,9 +2,9 @@ import io
from picamera import PiCamera
from PIL import Image
from seedsigner.models import Singleton
from seedsigner.hardware.pivideostream import PiVideoStream
from seedsigner.models.settings import SettingsConstants
from seedsigner.models.settings import Settings, SettingsConstants
from seedsigner.models.singleton import Singleton
@@ -16,7 +16,6 @@ class Camera(Singleton):
@classmethod
def get_instance(cls):
# This is the only way to access the one and only Controller
from seedsigner.models import Settings
if cls._instance is None:
cls._instance = cls.__new__(cls)
cls._instance._camera_rotation = int(Settings.get_instance().get_value(SettingsConstants.SETTING__CAMERA_ROTATION))
+1
View File
@@ -1,3 +1,4 @@
print("embit_utils.py")
import embit
from embit import bip32
from embit.bip32 import HDKey
@@ -5,7 +5,6 @@ from embit import bip39
from embit.bip39 import mnemonic_to_bytes, mnemonic_from_bytes
from typing import List
from seedsigner.models.seed import Seed
def calculate_checksum(mnemonic: list, wordlist_language_code: str) -> List[str]:
@@ -15,6 +14,7 @@ def calculate_checksum(mnemonic: list, wordlist_language_code: str) -> List[str]
If 11- or 23-words are provided, append word `0000` to end of list as temp final
word.
"""
from seedsigner.models.seed import Seed
if len(mnemonic) in [11, 23]:
mnemonic.append(Seed.get_wordlist(wordlist_language_code)[0])
-10
View File
@@ -1,10 +0,0 @@
# Must import crucial base class first!
from .singleton import Singleton, ConfigurableSingleton
from .seed import *
from .qr_type import *
from .decode_qr import *
from .encode_qr import *
from .psbt_parser import *
from .seed_storage import *
from .settings import *
+4 -1
View File
@@ -1,3 +1,5 @@
print("decode_qr.py")
import base64
import json
import logging
@@ -13,7 +15,8 @@ from urtypes.crypto import Account, Output
from urtypes.bytes import Bytes
from seedsigner.helpers.ur2.ur_decoder import URDecoder
from seedsigner.models import QRType, Seed
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
from seedsigner.models.settings import SettingsConstants
+5 -2
View File
@@ -1,3 +1,5 @@
print("encode_qr.py")
import math
from embit import bip32
@@ -11,12 +13,13 @@ from embit.psbt import PSBT
from seedsigner.helpers.ur2.ur_encoder import UREncoder
from seedsigner.helpers.ur2.ur import UR
from seedsigner.helpers.qr import QR
from seedsigner.models import Seed, QRType
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
from seedsigner.models.settings import SettingsConstants
from urtypes.crypto import PSBT as UR_PSBT
from urtypes.crypto import Account, HDKey, Output, Keypath, PathComponent, SCRIPT_EXPRESSION_TAG_MAP
from seedsigner.models.settings import SettingsConstants
+4 -2
View File
@@ -1,12 +1,14 @@
print("psbt_parser.py")
from binascii import hexlify
from embit import psbt, script, ec, bip32, bip39
from embit import psbt, script, ec, bip32
from embit.descriptor import Descriptor
from embit.networks import NETWORKS
from embit.psbt import PSBT
from io import BytesIO
from typing import List
from seedsigner.models import Seed
from seedsigner.models.seed import Seed
from seedsigner.models.settings import SettingsConstants
+3 -1
View File
@@ -1,3 +1,4 @@
print("seed.py")
import unicodedata
from binascii import hexlify
@@ -6,7 +7,6 @@ from embit.networks import NETWORKS
from typing import List
from seedsigner.models.settings import SettingsConstants
from seedsigner.helpers import embit_utils
class InvalidSeedException(Exception):
@@ -108,6 +108,8 @@ class Seed:
def get_xpub(self, wallet_path: str = '/', network: str = SettingsConstants.MAINNET):
# Import here to avoid slow startup times; takes 1.35s to import the first time
from seedsigner.helpers import embit_utils
return embit_utils.get_xpub(seed_bytes=self.seed_bytes, derivation_path=wallet_path, embit_network=SettingsConstants.map_network_to_embit(network))
+3 -2
View File
@@ -1,6 +1,7 @@
print("seed_storage.py")
from typing import List
from seedsigner.models import Seed
from seedsigner.models.seed import InvalidSeedException
from seedsigner.models.seed import Seed, InvalidSeedException
from seedsigner.models.settings_definition import SettingsConstants
+1 -2
View File
@@ -5,8 +5,7 @@ import platform
from typing import List
from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition
from .singleton import Singleton
from seedsigner.models.singleton import Singleton
class InvalidSettingsQRData(Exception):
+2 -1
View File
@@ -1,5 +1,6 @@
print("psbt_views.py")
import logging
from typing import List
from embit.psbt import PSBT
from embit import script
+2 -1
View File
@@ -3,7 +3,8 @@ import re
from embit.descriptor import Descriptor
from seedsigner.gui.screens import scan_screens
from seedsigner.models import DecodeQR, Seed
from seedsigner.models.decode_qr import DecodeQR
from seedsigner.models.seed import Seed
from seedsigner.models.settings import SettingsConstants
from seedsigner.views.settings_views import SettingsIngestSettingsQRView
from seedsigner.views.view import MainMenuView, NotYetImplementedView, View, Destination
+2
View File
@@ -1,3 +1,5 @@
print("seed_views.py")
import embit
import random
import time
+2
View File
@@ -1,3 +1,5 @@
print("tools_views.py")
import hashlib
import os
+3 -2
View File
@@ -1,11 +1,13 @@
print("view.py")
from dataclasses import dataclass
from typing import Type
from seedsigner.gui.components import FontAwesomeIconConstants
from seedsigner.gui.screens import RET_CODE__POWER_BUTTON, RET_CODE__BACK_BUTTON
from seedsigner.gui.screens.screen import BaseScreen, DireWarningScreen, LargeButtonScreen, PowerOffScreen, PowerOffNotRequiredScreen, ResetScreen, WarningScreen
from seedsigner.models.settings import Settings
from seedsigner.models.threads import BaseThread
from seedsigner.models import Settings
class BackStackView:
@@ -46,7 +48,6 @@ class View:
# Import here to avoid circular imports
from seedsigner.controller import Controller
from seedsigner.gui import Renderer
from seedsigner.models import Settings
self.controller: Controller = Controller.get_instance()
self.settings = Settings.get_instance()
+1 -1
View File
@@ -14,7 +14,7 @@ sys.modules['seedsigner.hardware.microsd'] = MagicMock()
from seedsigner.controller import Controller, FlowBasedTestException, StopFlowBasedTest
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON, RET_CODE__POWER_BUTTON
from seedsigner.models import Settings
from seedsigner.models.settings import Settings
from seedsigner.views.view import Destination, MainMenuView, View
+1 -1
View File
@@ -1,6 +1,6 @@
import pytest
from mock import MagicMock
from seedsigner.models import Seed
from seedsigner.models.seed import Seed
from embit import bip39
from seedsigner.models.settings import SettingsConstants
+4 -4
View File
@@ -1,7 +1,7 @@
import pytest
from mock import MagicMock
from seedsigner.models import Seed, DecodeQR, DecodeQRStatus, QRType, PSBTParser
from embit import psbt, bip39
from seedsigner.models.decode_qr import DecodeQR, DecodeQRStatus
from seedsigner.models.psbt_parser import PSBTParser
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
from seedsigner.models.settings_definition import SettingsConstants
+2 -3
View File
@@ -1,6 +1,5 @@
import pytest
from mock import MagicMock
from seedsigner.models import EncodeQR, QRType
from seedsigner.models.encode_qr import EncodeQR
from seedsigner.models.qr_type import QRType
from embit import psbt
from binascii import a2b_base64
+1 -2
View File
@@ -3,9 +3,8 @@ import os
# Must import test base before the Controller
from base import FlowTest, FlowStep
from seedsigner.models import SettingsDefinition
from seedsigner.models.settings import Settings
from seedsigner.models.settings_definition import SettingsConstants
from seedsigner.models.settings_definition import SettingsDefinition, SettingsConstants
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON
from seedsigner.views.view import MainMenuView
from seedsigner.views import settings_views
+2 -3
View File
@@ -1,6 +1,5 @@
import pytest
from mock import MagicMock
from seedsigner.models import PSBTParser, Seed
from seedsigner.models.psbt_parser import PSBTParser
from seedsigner.models.seed import Seed
from embit import psbt
from binascii import a2b_base64
from seedsigner.models.settings_definition import SettingsConstants
+1 -4
View File
@@ -1,7 +1,4 @@
import pytest
from mock import MagicMock
from seedsigner.models import Seed
from embit import bip39
from seedsigner.models.seed import Seed
from seedsigner.models.settings import SettingsConstants