From 05e2909603471258f45145686225450c717b04cb Mon Sep 17 00:00:00 2001 From: kdmukai Date: Fri, 18 Aug 2023 07:23:57 -0500 Subject: [PATCH] Bugfix & improvements when message signing is disabled --- src/seedsigner/views/scan_views.py | 19 ++++++------- src/seedsigner/views/seed_views.py | 6 +++- src/seedsigner/views/view.py | 26 +++++++++++++---- tests/screenshot_generator/generator.py | 13 +++++++-- tests/test_flows_seed.py | 38 ++++++++++++++++++++++++- 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/seedsigner/views/scan_views.py b/src/seedsigner/views/scan_views.py index 2b83d03b..9c4e5990 100644 --- a/src/seedsigner/views/scan_views.py +++ b/src/seedsigner/views/scan_views.py @@ -136,19 +136,16 @@ class ScanView(View): ) elif self.decoder.is_sign_message: - if self.settings.get_value(SettingsConstants.SETTING__MESSAGE_SIGNING) == SettingsConstants.OPTION__ENABLED: - from seedsigner.views.seed_views import SeedSignMessageStartView - qr_data = self.decoder.get_qr_data() + from seedsigner.views.seed_views import SeedSignMessageStartView + qr_data = self.decoder.get_qr_data() - return Destination( - SeedSignMessageStartView, - view_args=dict( - derivation_path=qr_data["derivation_path"], - message=qr_data["message"], - ) + return Destination( + SeedSignMessageStartView, + view_args=dict( + derivation_path=qr_data["derivation_path"], + message=qr_data["message"], ) - else: - return Destination(OptionDisabledView, view_args=dict(error_msg="Message signing is currently disabled in Settings")) + ) else: return Destination(NotYetImplementedView) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 16733ca8..a58b1f7e 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -23,7 +23,7 @@ from seedsigner.models.seed import InvalidSeedException, Seed from seedsigner.models.settings import Settings, SettingsConstants from seedsigner.models.settings_definition import SettingsDefinition from seedsigner.models.threads import BaseThread, ThreadsafeCounter -from seedsigner.views.view import NotYetImplementedView, View, Destination, BackStackView, MainMenuView +from seedsigner.views.view import NotYetImplementedView, OptionDisabledView, View, Destination, BackStackView, MainMenuView @@ -1921,6 +1921,10 @@ class SeedSignMessageStartView(View): self.derivation_path = derivation_path self.message = message + if self.settings.get_value(SettingsConstants.SETTING__MESSAGE_SIGNING) == SettingsConstants.OPTION__DISABLED: + self.set_redirect(Destination(OptionDisabledView, view_args=dict(settings_attr=SettingsConstants.SETTING__MESSAGE_SIGNING))) + return + # calculate the actual receive address addr_format = embit_utils.parse_derivation_path(derivation_path) if not addr_format["clean_match"]: diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index d3e3c73c..3f507a73 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -5,6 +5,7 @@ from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconCo 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, SettingsConstants +from seedsigner.models.settings_definition import SettingsDefinition from seedsigner.models.threads import BaseThread @@ -331,7 +332,7 @@ class ErrorView(View): class NetworkMismatchErrorView(ErrorView): title: str = "Network Mismatch" show_back_button: bool = False - button_text: str = "Change Settings" + button_text: str = "Change Setting" next_destination: Destination = None @@ -368,18 +369,33 @@ class UnhandledExceptionView(View): @dataclass class OptionDisabledView(View): - error_msg: str + UPDATE_SETTING = "Update Setting" + DONE = "Done" + settings_attr: str + + def __post_init__(self): + super().__post_init__() + self.settings_entry = SettingsDefinition.get_settings_entry(self.settings_attr) + self.error_msg = f"\"{self.settings_entry.display_name}\" is currently disabled in Settings." def run(self): - WarningScreen( + button_data = [self.UPDATE_SETTING, self.DONE] + selected_menu_num = self.run_screen( + WarningScreen, title="Option Disabled", status_headline=None, text=self.error_msg, - button_data=["OK"], + button_data=button_data, show_back_button=False, allow_text_overflow=True, # Fit what we can, let the rest go off the edges - ).display() + ) + + if button_data[selected_menu_num] == self.UPDATE_SETTING: + from seedsigner.views.settings_views import SettingsEntryUpdateSelectionView + return Destination(SettingsEntryUpdateSelectionView, view_args=dict(attr_name=self.settings_attr), clear_history=True) + else: + return Destination(MainMenuView, clear_history=True) diff --git a/tests/screenshot_generator/generator.py b/tests/screenshot_generator/generator.py index 3e9494a4..9afac77d 100644 --- a/tests/screenshot_generator/generator.py +++ b/tests/screenshot_generator/generator.py @@ -3,6 +3,9 @@ import os import sys import time from mock import Mock, patch, MagicMock +from seedsigner.helpers import embit_utils + +from seedsigner.models.settings import Settings # Prevent importing modules w/Raspi hardware dependencies. @@ -28,7 +31,7 @@ from seedsigner.models.seed import Seed from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition from seedsigner.views import (MainMenuView, PowerOptionsView, RestartView, NotYetImplementedView, UnhandledExceptionView, psbt_views, seed_views, settings_views, tools_views) -from seedsigner.views.view import NetworkMismatchErrorView, PowerOffView, View +from seedsigner.views.view import NetworkMismatchErrorView, OptionDisabledView, PowerOffView, View from .utils import ScreenshotComplete, ScreenshotRenderer @@ -80,10 +83,12 @@ def test_generate_screenshots(target_locale): controller.multisig_wallet_descriptor = embit.descriptor.Descriptor.from_string(MULTISIG_WALLET_DESCRIPTOR) # Message signing data + derivation_path = "m/84h/0h/0h/0/0" controller.sign_message_data = { "seed_num": 0, - "derivation_path": "m/84h/0h/0h/0/0", + "derivation_path": derivation_path, "message": "I attest that I control this bitcoin address blah blah blah", + "addr_format": embit_utils.parse_derivation_path(derivation_path) } # Automatically populate all Settings options Views @@ -116,6 +121,7 @@ def test_generate_screenshots(target_locale): (UnhandledExceptionView, dict(error=UnhandledExceptionViewFood)), (settings_views.SettingsIngestSettingsQRView, dict(data="settings::v1 name=factory_reset")), NetworkMismatchErrorView, + (OptionDisabledView, dict(settings_attr=SettingsConstants.SETTING__MESSAGE_SIGNING)), ], @@ -231,7 +237,8 @@ def test_generate_screenshots(target_locale): print(f"Completed {view_name}") except Exception as e: # Something else went wrong - print(repr(e)) + from traceback import print_exc + print_exc() raise e finally: if toast_thread: diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py index d2ebc7d2..521d77bc 100644 --- a/tests/test_flows_seed.py +++ b/tests/test_flows_seed.py @@ -8,7 +8,7 @@ from base import FlowTestRunScreenNotExecutedException, FlowTestInvalidButtonDat from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON from seedsigner.models.settings import Settings, SettingsConstants from seedsigner.models.seed import Seed -from seedsigner.views.view import MainMenuView, RemoveMicroSDWarningView, View, NetworkMismatchErrorView +from seedsigner.views.view import MainMenuView, OptionDisabledView, RemoveMicroSDWarningView, View, NetworkMismatchErrorView from seedsigner.views import seed_views, scan_views, settings_views, tools_views @@ -441,3 +441,39 @@ class TestMessageSigningFlows(FlowTest): # REGTEST settings vs MAINNET derivation path with the message self.settings.set_value(SettingsConstants.SETTING__NETWORK, SettingsConstants.REGTEST) expect_network_mismatch_error(self.load_short_message_into_decoder) + + + + + def test_sign_message_option_disabled(self): + """ + Should redirect to OptionDisabledView if a `signmessage` QR is scanned with + message signing disabled. + + Should offer the option to route directly to enable that settings or return to + MainMenuView. + """ + # Ensure message signing is disabled + self.settings.set_value(SettingsConstants.SETTING__MESSAGE_SIGNING, SettingsConstants.OPTION__DISABLED) + + sequence = [ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), + FlowStep(scan_views.ScanView, before_run=self.load_short_message_into_decoder), # simulate read message QR; ret val is ignored + FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), + ] + + # First test routing to update the setting + self.run_sequence( + sequence + [ + FlowStep(OptionDisabledView, button_data_selection=OptionDisabledView.UPDATE_SETTING, is_redirect=True), + FlowStep(settings_views.SettingsEntryUpdateSelectionView), + ] + ) + + # Now test exiting to Main Menu + self.run_sequence( + sequence + [ + FlowStep(OptionDisabledView, button_data_selection=OptionDisabledView.DONE, is_redirect=True), + FlowStep(MainMenuView), + ] + )