mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
Routing bugfixes, additional tests
This commit is contained in:
@@ -19,6 +19,7 @@ from seedsigner.models.settings_definition import SettingsConstants
|
||||
# TODO: PR these directly into `embit`? Or replace with new/existing methods already in `embit`?
|
||||
|
||||
|
||||
# TODO: Refactor `wallet_type` to conform to our `sig_type` naming convention
|
||||
def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG, script_type: str = SettingsConstants.NATIVE_SEGWIT) -> str:
|
||||
if network == SettingsConstants.MAINNET:
|
||||
network_path = "0'"
|
||||
|
||||
@@ -109,15 +109,15 @@ class Seed:
|
||||
|
||||
|
||||
@property
|
||||
def script_override(self) -> list:
|
||||
def script_override(self) -> str:
|
||||
return None
|
||||
|
||||
|
||||
def derivation_override(self, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
def derivation_override(self, sig_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
return None
|
||||
|
||||
|
||||
def detect_version(self, derivation_path: str, network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
def detect_version(self, derivation_path: str, network: str = SettingsConstants.MAINNET, sig_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(network)]
|
||||
return bip32.detect_version(derivation_path, default="xpub", network=embit_network)
|
||||
|
||||
@@ -205,17 +205,17 @@ class ElectrumSeed(Seed):
|
||||
|
||||
|
||||
@property
|
||||
def script_override(self) -> list:
|
||||
return [SettingsConstants.NATIVE_SEGWIT]
|
||||
def script_override(self) -> str:
|
||||
return SettingsConstants.NATIVE_SEGWIT
|
||||
|
||||
|
||||
def derivation_override(self, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
return "m/0h" if SettingsConstants.SINGLE_SIG == wallet_type else "m/1h"
|
||||
def derivation_override(self, sig_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
return "m/0h" if sig_type == SettingsConstants.SINGLE_SIG else "m/1h"
|
||||
|
||||
|
||||
def detect_version(self, derivation_path: str, network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
def detect_version(self, derivation_path: str, network: str = SettingsConstants.MAINNET, sig_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(network)]
|
||||
return embit_network["zpub"] if SettingsConstants.SINGLE_SIG == wallet_type else embit_network["Zpub"]
|
||||
return embit_network["zpub"] if sig_type == SettingsConstants.SINGLE_SIG else embit_network["Zpub"]
|
||||
|
||||
|
||||
@property
|
||||
|
||||
@@ -79,9 +79,8 @@ class PSBTSelectSeedView(View):
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
elif button_data[selected_menu_num] == self.TYPE_ELECTRUM:
|
||||
from seedsigner.views.seed_views import SeedMnemonicEntryView
|
||||
self.controller.storage.init_pending_mnemonic(num_words=12, is_electrum=True)
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
from seedsigner.views.seed_views import SeedElectrumMnemonicStartView
|
||||
return Destination(SeedElectrumMnemonicStartView)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -688,8 +688,16 @@ class SeedExportXpubScriptTypeView(View):
|
||||
def run(self):
|
||||
from .tools_views import ToolsAddressExplorerAddressTypeView
|
||||
args = {"seed_num": self.seed_num, "sig_type": self.sig_type}
|
||||
|
||||
script_types = self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES)
|
||||
|
||||
seed = self.controller.storage.seeds[self.seed_num]
|
||||
script_types = seed.script_override if seed.script_override else self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES)
|
||||
if seed.script_override:
|
||||
# This seed only allows one script type
|
||||
# TODO: Does it matter if the Settings don't have the override script type
|
||||
# enabled?
|
||||
script_types = [seed.script_override]
|
||||
|
||||
if len(script_types) == 1:
|
||||
# Nothing to select; skip this screen
|
||||
args["script_type"] = script_types[0]
|
||||
@@ -1729,7 +1737,7 @@ class SeedAddressVerificationView(View):
|
||||
raise Exception("Can't validate a single sig addr without specifying a seed")
|
||||
self.seed_num = seed_num
|
||||
self.seed = self.controller.get_seed(seed_num)
|
||||
self.seed_derivation_override = self.seed.derivation_override(wallet_type=SettingsConstants.SINGLE_SIG)
|
||||
self.seed_derivation_override = self.seed.derivation_override(sig_type=SettingsConstants.SINGLE_SIG)
|
||||
else:
|
||||
self.seed = None
|
||||
self.address = self.controller.unverified_address["address"]
|
||||
|
||||
@@ -496,8 +496,8 @@ class ToolsAddressExplorerSelectSourceView(View):
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
elif button_data[selected_menu_num] == self.TYPE_ELECTRUM:
|
||||
from seedsigner.views.seed_views import SeedElectrumWarningView
|
||||
return Destination(SeedElectrumWarningView)
|
||||
from seedsigner.views.seed_views import SeedElectrumMnemonicStartView
|
||||
return Destination(SeedElectrumMnemonicStartView)
|
||||
|
||||
|
||||
|
||||
@@ -532,7 +532,7 @@ class ToolsAddressExplorerAddressTypeView(View):
|
||||
if self.seed_num is not None:
|
||||
self.seed = self.controller.storage.seeds[seed_num]
|
||||
data["seed_num"] = self.seed
|
||||
seed_derivation_override = self.seed.derivation_override(wallet_type=SettingsConstants.SINGLE_SIG)
|
||||
seed_derivation_override = self.seed.derivation_override(sig_type=SettingsConstants.SINGLE_SIG)
|
||||
|
||||
if self.script_type == SettingsConstants.CUSTOM_DERIVATION:
|
||||
derivation_path = self.custom_derivation
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from base import FlowTest, FlowStep
|
||||
|
||||
from seedsigner.controller import Controller
|
||||
from seedsigner.views.view import MainMenuView
|
||||
from seedsigner.views import scan_views, seed_views, psbt_views
|
||||
from seedsigner.models.settings import SettingsConstants
|
||||
@@ -57,7 +58,49 @@ class TestPSBTFlows(FlowTest):
|
||||
FlowStep(MainMenuView)
|
||||
])
|
||||
|
||||
|
||||
|
||||
def test_scan_psbt_first_then_load_electrum_seed(self):
|
||||
"""
|
||||
Should be able to load an Electrum mnemonic after first loading in a psbt.
|
||||
"""
|
||||
def load_psbt_into_decoder(view: scan_views.ScanView):
|
||||
# Same psbt as above, but we don't care about the details here
|
||||
view.decoder.add_data("cHNidP8BANgCAAAAAsTXZs3fz/dmGb6M80+jjvJZdYya+cw5bT/dGuhZFdSlAAAAAAD9////qo6xg/UZAvUkcbse1F+C9zbP/FeZNjThx7SCIn6eMCgBAAAAAP3///8EQOIBAAAAAAAWABSkZPM7kLcTRE2En1t33/0RCHgMjQXYnnYAAAAAFgAUKMaPRKXdY4m8iKrE9j+rycskJU1A4gEAAAAAABYAFPYc9wiHRrYKAZYLLztREAwpPBIwipVcAwAAAAAWABSiFuiJIa4NrxLUBVQNS0NIun6DDtoRAABPAQQ1h88DBcQGZIAAAAA+0J+jlNL3dpWwlnBi8Dx+Ipg4e6uvB3HdjzFPX7r9CAOOlAIxgII+/xCcj+XoEenKH7wj5s5wlu7Q7CCZWFLGLhA5Su0UVAAAgAEAAIAAAACAAAEA7QIAAAAEE6njX/fnvn7hbkKIRcxzNYFOSfbCdNeWnd7Fe/1UcQ0BAAAAAP3///8TqeNf9+e+fuFuQohFzHM1gU5J9sJ015ad3sV7/VRxDQMAAAAA/f///xOp41/3575+4W5CiEXMczWBTkn2wnTXlp3exXv9VHENBAAAAAD9////E6njX/fnvn7hbkKIRcxzNYFOSfbCdNeWnd7Fe/1UcQ0GAAAAAP3///8CUnheAwAAAAAWABRCfygPJ+Fjsx4BknYvvm3A3qKn2xJ/XQcAAAAAF6kU1I4TAst5nAj15ey7vwe5cM3OFq+HlhEAAAEBH1J4XgMAAAAAFgAUQn8oDyfhY7MeAZJ2L75twN6ip9sBAwQBAAAAIgYCo7sfm78RQY3B5n0ac/QF8VtMAzFnci+h5D1MtpgRY7oYOUrtFFQAAIABAACAAAAAgAEAAAAGAAAAAAEAcQIAAAABxY7wh0nsfJQfzWrD/9rN9BYsM+iOmPaO6I0ANFgO/PcAAAAAAP3///8CptiUAAAAAAAWABRIm4HhQY/TzOjeWSPRrbuJo9MlW826oHYAAAAAFgAU0z+0L2QSLGtyQTn8FhbCpcI7jbliAQAAAQEfzbqgdgAAAAAWABTTP7QvZBIsa3JBOfwWFsKlwjuNuQEDBAEAAAAiBgITHmebEANk81CraV4xZIpqkNjjw0tIvezl1Ism1NRH3Rg5Su0UVAAAgAEAAIAAAACAAQAAAAAAAAAAIgICuTT7WnuiUTpObjWnZFHzIeEvW9PTB+1LLVFNQJVFeIIYOUrtFFQAAIABAACAAAAAgAEAAAAHAAAAACICAk8f3hpc5C35chgSg+Pe2zZ9IhHREd4aKW2+yAMRIFeqGDlK7RRUAACAAQAAgAAAAIABAAAACQAAAAAAIgIDjt1CjvrnMMnjbmTNKUAYoKEDRbmKjNjbq+6Ppqj3bqQYOUrtFFQAAIABAACAAAAAgAEAAAAIAAAAAA==")
|
||||
|
||||
self.settings.set_value(SettingsConstants.SETTING__ELECTRUM_SEEDS, SettingsConstants.OPTION__ENABLED)
|
||||
|
||||
sequence = [
|
||||
FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN),
|
||||
FlowStep(scan_views.ScanView, before_run=load_psbt_into_decoder), # simulate read PSBT; ret val is ignored
|
||||
FlowStep(psbt_views.PSBTSelectSeedView, button_data_selection=psbt_views.PSBTSelectSeedView.TYPE_ELECTRUM),
|
||||
FlowStep(seed_views.SeedElectrumMnemonicStartView),
|
||||
]
|
||||
|
||||
# Load an Electrum mnemonic during the flow (same one used in test_seed.py)
|
||||
# This seed can't actually sign the psbt.
|
||||
for word in "regular reject rare profit once math fringe chase until ketchup century escape".split():
|
||||
sequence += [
|
||||
FlowStep(seed_views.SeedMnemonicEntryView, screen_return_value=word),
|
||||
]
|
||||
|
||||
sequence += [
|
||||
FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE),
|
||||
|
||||
# TODO: Change the PSBT to one that this Electrum seed can actually sign in
|
||||
# order to test the full flow:
|
||||
# FlowStep(seed_views.SeedOptionsView, is_redirect=True),
|
||||
# FlowStep(psbt_views.PSBTOverviewView),
|
||||
|
||||
# Until then, the View won't actually auto-route us back into the PSBT flow
|
||||
FlowStep(seed_views.SeedOptionsView),
|
||||
]
|
||||
|
||||
self.run_sequence(sequence)
|
||||
|
||||
# But we can at least verify that we're still in the PSBT flow
|
||||
assert self.controller.resume_main_flow == Controller.FLOW__PSBT
|
||||
|
||||
|
||||
def test_scan_multisig_psbt_seed_already_signed_flow(self):
|
||||
|
||||
def load_psbt_into_decoder(view: scan_views.ScanView):
|
||||
|
||||
+42
-11
@@ -3,11 +3,11 @@ import pytest
|
||||
|
||||
# Must import test base before the Controller
|
||||
from base import BaseTest, FlowTest, FlowStep
|
||||
from base import FlowTestRunScreenNotExecutedException, FlowTestInvalidButtonDataSelectionException
|
||||
from base import FlowTestInvalidButtonDataSelectionException
|
||||
|
||||
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.models.seed import ElectrumSeed, Seed
|
||||
from seedsigner.views.view import ErrorView, MainMenuView, OptionDisabledView, RemoveMicroSDWarningView, View, NetworkMismatchErrorView, NotYetImplementedView
|
||||
from seedsigner.views import seed_views, scan_views, settings_views, tools_views
|
||||
|
||||
@@ -243,15 +243,15 @@ class TestSeedFlows(FlowTest):
|
||||
self.settings.set_value(SettingsConstants.SETTING__SCRIPT_TYPES, [x for x,y in script_types if x!=disabled_script])
|
||||
self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x,y in coordinators if x!=disabled_coord])
|
||||
|
||||
# test that multisig is not an option via exception raised when redirected to next step instead of having a choice
|
||||
with pytest.raises(FlowTestRunScreenNotExecutedException) as e:
|
||||
self.run_sequence(
|
||||
initial_destination_view_args=dict(seed_num=0),
|
||||
sequence=[
|
||||
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
|
||||
FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=disabled_sig),
|
||||
]
|
||||
)
|
||||
# If multisig isn't an option, then the sig type selection is skipped altogether
|
||||
self.run_sequence(
|
||||
initial_destination_view_args=dict(seed_num=0),
|
||||
sequence=[
|
||||
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
|
||||
FlowStep(seed_views.SeedExportXpubSigTypeView, is_redirect=True),
|
||||
FlowStep(seed_views.SeedExportXpubScriptTypeView),
|
||||
]
|
||||
)
|
||||
|
||||
# test that taproot is not an option via exception raised when choice is taproot
|
||||
with pytest.raises(FlowTestInvalidButtonDataSelectionException) as e:
|
||||
@@ -345,6 +345,37 @@ class TestSeedFlows(FlowTest):
|
||||
)
|
||||
|
||||
|
||||
def test_export_xpub_electrum_seed_flow(self):
|
||||
"""
|
||||
Electrum seeds should skip script type selection
|
||||
"""
|
||||
# Load a finalized Seed into the Controller
|
||||
self.controller.storage.init_pending_mnemonic(num_words=12, is_electrum=True)
|
||||
self.controller.storage.set_pending_seed(ElectrumSeed("regular reject rare profit once math fringe chase until ketchup century escape".split()))
|
||||
self.controller.storage.finalize_pending_seed()
|
||||
|
||||
# Make sure all options are enabled
|
||||
self.settings.set_value(SettingsConstants.SETTING__SIG_TYPES, [x for x,y in SettingsConstants.ALL_SIG_TYPES])
|
||||
self.settings.set_value(SettingsConstants.SETTING__SCRIPT_TYPES, [x for x,y in SettingsConstants.ALL_SCRIPT_TYPES])
|
||||
self.settings.set_value(SettingsConstants.SETTING__COORDINATORS, [x for x,y in SettingsConstants.ALL_COORDINATORS])
|
||||
|
||||
self.run_sequence(
|
||||
initial_destination_view_args=dict(seed_num=0),
|
||||
sequence=[
|
||||
FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.EXPORT_XPUB),
|
||||
FlowStep(seed_views.SeedExportXpubSigTypeView, button_data_selection=seed_views.SeedExportXpubSigTypeView.SINGLE_SIG),
|
||||
|
||||
# Skips past the script type options via redirect
|
||||
FlowStep(seed_views.SeedExportXpubScriptTypeView, is_redirect=True),
|
||||
FlowStep(seed_views.SeedExportXpubCoordinatorView, button_data_selection=self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS)[0]),
|
||||
FlowStep(seed_views.SeedExportXpubWarningView, screen_return_value=0),
|
||||
FlowStep(seed_views.SeedExportXpubDetailsView, screen_return_value=0),
|
||||
FlowStep(seed_views.SeedExportXpubQRDisplayView, screen_return_value=0),
|
||||
FlowStep(MainMenuView),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_discard_seed_flow(self):
|
||||
"""
|
||||
Selecting "Discard Seed" from the SeedOptionsView should enter the Discard Seed flow and
|
||||
|
||||
@@ -39,8 +39,6 @@ class TestToolsFlows(FlowTest):
|
||||
Finalizing a seed during the Address Explorer flow should return to the next
|
||||
Address Explorer step upon completion.
|
||||
"""
|
||||
controller = Controller.get_instance()
|
||||
|
||||
def load_seed_into_decoder(view: scan_views.ScanView):
|
||||
view.decoder.add_data("0000" * 11 + "0003")
|
||||
|
||||
@@ -55,11 +53,11 @@ class TestToolsFlows(FlowTest):
|
||||
FlowStep(seed_views.SeedExportXpubScriptTypeView),
|
||||
])
|
||||
|
||||
assert controller.resume_main_flow == Controller.FLOW__ADDRESS_EXPLORER
|
||||
assert self.controller.resume_main_flow == Controller.FLOW__ADDRESS_EXPLORER
|
||||
|
||||
# Reset
|
||||
controller.storage.seeds.clear()
|
||||
controller.storage.set_pending_seed(Seed(mnemonic=["abandon "* 11 + "about"]))
|
||||
self.controller.storage.seeds.clear()
|
||||
self.controller.storage.set_pending_seed(Seed(mnemonic=["abandon "* 11 + "about"]))
|
||||
|
||||
# Finalize the new seed w/passphrase
|
||||
self.run_sequence(
|
||||
@@ -73,6 +71,39 @@ class TestToolsFlows(FlowTest):
|
||||
)
|
||||
|
||||
|
||||
def test__address_explorer__load_electrum_seed__sideflow(self):
|
||||
"""
|
||||
Loading an Electrum seed during the Address Explorer flow should return to
|
||||
the Address Explorer flow upon completion, skip the script type selection,
|
||||
and successfully generate receive or change addresses.
|
||||
"""
|
||||
self.settings.set_value(SettingsConstants.SETTING__ELECTRUM_SEEDS, SettingsConstants.OPTION__ENABLED)
|
||||
|
||||
sequence = [
|
||||
FlowStep(MainMenuView, button_data_selection=MainMenuView.TOOLS),
|
||||
FlowStep(tools_views.ToolsMenuView, button_data_selection=tools_views.ToolsMenuView.ADDRESS_EXPLORER),
|
||||
FlowStep(tools_views.ToolsAddressExplorerSelectSourceView, button_data_selection=tools_views.ToolsAddressExplorerSelectSourceView.TYPE_ELECTRUM),
|
||||
FlowStep(seed_views.SeedElectrumMnemonicStartView),
|
||||
]
|
||||
|
||||
# Load an Electrum mnemonic during the flow (same one used in test_seed.py)
|
||||
for word in "regular reject rare profit once math fringe chase until ketchup century escape".split():
|
||||
sequence += [
|
||||
FlowStep(seed_views.SeedMnemonicEntryView, screen_return_value=word),
|
||||
]
|
||||
|
||||
sequence += [
|
||||
FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE),
|
||||
FlowStep(seed_views.SeedOptionsView, is_redirect=True),
|
||||
FlowStep(seed_views.SeedExportXpubScriptTypeView, is_redirect=True),
|
||||
FlowStep(tools_views.ToolsAddressExplorerAddressTypeView, button_data_selection=tools_views.ToolsAddressExplorerAddressTypeView.RECEIVE),
|
||||
FlowStep(tools_views.ToolsAddressExplorerAddressListView),
|
||||
]
|
||||
|
||||
self.run_sequence(sequence)
|
||||
|
||||
|
||||
|
||||
def test__address_explorer__scan_wrong_qrtype__flow(self):
|
||||
"""
|
||||
Scanning the wrong type of QR code when a SeedQR is expected should route to ErrorView
|
||||
|
||||
Reference in New Issue
Block a user