Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolves issue 621 - Verify Address for exported Xpub Option #629

Open
wants to merge 26 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/seedsigner/gui/screens/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,14 @@ class WarningScreen(WarningEdgesMixin, LargeIconStatusScreen):



@dataclass
class AdviceScreen(WarningEdgesMixin, LargeIconStatusScreen):
title: str = _mft("Advice")
status_icon_name: str = SeedSignerIconConstants.WARNING
status_color: str = "yellow"



@dataclass
class DireWarningScreen(WarningScreen):
status_headline: str = _mft("Classified Info!") # The colored text under the alert icon
Expand Down
8 changes: 8 additions & 0 deletions src/seedsigner/views/scan_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ def run(self):
from seedsigner.views.seed_views import AddressVerificationStartView
address = self.decoder.get_address()
(script_type, network) = self.decoder.get_address_type()
seed_num = self.seed_num
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot always be assumed that object has .seed_num, sometimes user will scan an address first, afterwards should be prompted to load a seed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed on 1e63527 commit.



return Destination(
AddressVerificationStartView,
Expand All @@ -141,6 +143,7 @@ def run(self):
"address": address,
"script_type": script_type,
"network": network,
"seed_num": seed_num
}
)

Expand Down Expand Up @@ -209,6 +212,11 @@ class ScanAddressView(ScanView):
instructions_text = _mft("Scan address QR")
invalid_qr_type_message = _mft("Expected an address QR")

def __init__(self, seed_num: int=None):
super().__init__()
self.seed_num = seed_num


@property
def is_valid_qr_type(self):
return self.decoder.is_address
79 changes: 63 additions & 16 deletions src/seedsigner/views/seed_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants
from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen,
WarningScreen, DireWarningScreen, seed_screens)
WarningScreen, DireWarningScreen, seed_screens, AdviceScreen)
from seedsigner.gui.screens.screen import ButtonOption
from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder
from seedsigner.models.qr_type import QRType
Expand All @@ -18,6 +18,7 @@
from seedsigner.models.settings_definition import SettingsDefinition
from seedsigner.models.threads import BaseThread, ThreadsafeCounter
from seedsigner.views.view import NotYetImplementedView, OptionDisabledView, View, Destination, BackStackView, MainMenuView
from seedsigner.views.scan_views import ScanAddressView

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -77,9 +78,10 @@ class SeedSelectSeedView(View):
TYPE_ELECTRUM = ButtonOption("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)


def __init__(self, flow: str):
def __init__(self, flow: str, seed_num: int = None):
super().__init__()
self.flow = flow
self.seed_num=seed_num


def run(self):
Expand Down Expand Up @@ -115,13 +117,16 @@ def run(self):
if self.settings.get_value(SettingsConstants.SETTING__ELECTRUM_SEEDS) == SettingsConstants.OPTION__ENABLED:
button_data.append(self.TYPE_ELECTRUM)

selected_menu_num = self.run_screen(
seed_screens.SeedSelectSeedScreen,
title=title,
text=text,
is_button_text_centered=False,
button_data=button_data,
)
if self.seed_num==None:
selected_menu_num = self.run_screen(
seed_screens.SeedSelectSeedScreen,
title=title,
text=text,
is_button_text_centered=False,
button_data=button_data,
)
else:
selected_menu_num = self.seed_num

if selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
Expand Down Expand Up @@ -980,6 +985,7 @@ class SeedExportXpubQRDisplayView(View):
def __init__(self, seed_num: int, coordinator: str, derivation_path: str, sig_type: str = SettingsConstants.SINGLE_SIG):
super().__init__()
self.seed = self.controller.get_seed(seed_num)
self.seed_num = seed_num

encoder_args = dict(
seed=self.seed,
Expand Down Expand Up @@ -1007,7 +1013,42 @@ def run(self):
qr_encoder=self.qr_encoder
)

return Destination(MainMenuView)
return Destination(SeedExportXpubQRAskVerifyAddView, view_args={"seed_num": self.seed_num})



class SeedExportXpubQRAskVerifyAddView(View):
"""
Ask to the user if he wants to verify an address from wallet with exported Xpub.
"""
VERIFY = ButtonOption("Verify an Address", SeedSignerIconConstants.QRCODE)
DONE = ButtonOption("Done")

def __init__(self, seed_num: int):
super().__init__()
self.seed_num = seed_num


def run(self):
button_data = [self.VERIFY, self.DONE]

# Because we have an explicit "DONE" button, we disable "BACK" to keep the
# routing options sane.
selected_menu_num = self.run_screen(
AdviceScreen,
title="Advice",
status_headline = _("Check new wallet"),
text=_("Scan wallet address for verify it with xpub?"),
is_button_text_centered=True,
show_back_button=False,
button_data=button_data
)

if button_data[selected_menu_num] == self.VERIFY:
return Destination(ScanAddressView, view_args={"seed_num": self.seed_num}, clear_history=True)

elif button_data[selected_menu_num] == self.DONE:
return Destination(MainMenuView)



Expand Down Expand Up @@ -1684,13 +1725,14 @@ def run(self):
Address verification
****************************************************************************"""
class AddressVerificationStartView(View):
def __init__(self, address: str, script_type: str, network: str):
def __init__(self, address: str, script_type: str, network: str, seed_num: int=None):
super().__init__()
self.controller.unverified_address = dict(
address=address,
script_type=script_type,
network=network
)
self.seed_num=seed_num


def run(self):
Expand All @@ -1700,26 +1742,26 @@ def run(self):
if self.controller.unverified_address["script_type"] == SettingsConstants.LEGACY_P2PKH:
# Legacy P2PKH addresses are always singlesig
sig_type = SettingsConstants.SINGLE_SIG
destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), skip_current_view=True)
destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR, seed_num=self.seed_num), skip_current_view=True)

if self.controller.unverified_address["script_type"] == SettingsConstants.NESTED_SEGWIT:
# No way to differentiate single sig from multisig
return Destination(AddressVerificationSigTypeView, skip_current_view=True)
return Destination(AddressVerificationSigTypeView, skip_current_view=True, view_args={"seed_num": self.seed_num})

if self.controller.unverified_address["script_type"] == SettingsConstants.NATIVE_SEGWIT:
if len(self.controller.unverified_address["address"]) >= 62:
# Mainnet/testnet are 62, regtest is 64
sig_type = SettingsConstants.MULTISIG
if self.controller.multisig_wallet_descriptor:
# Can jump straight to the brute-force verification View
destination = Destination(SeedAddressVerificationView, skip_current_view=True)
destination = Destination(SeedAddressVerificationView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), skip_current_view=True)
else:
self.controller.resume_main_flow = Controller.FLOW__VERIFY_MULTISIG_ADDR
destination = Destination(LoadMultisigWalletDescriptorView, skip_current_view=True)

else:
sig_type = SettingsConstants.SINGLE_SIG
destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), skip_current_view=True)
destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR, seed_num=self.seed_num), skip_current_view=True)

elif self.controller.unverified_address["script_type"] == SettingsConstants.TAPROOT:
# TODO: add Taproot support
Expand All @@ -1742,6 +1784,11 @@ class AddressVerificationSigTypeView(View):
SINGLE_SIG = ButtonOption("Single Sig")
MULTISIG = ButtonOption("Multisig")

def __init__(self, seed_num: int=None):
super().__init__()
self.seed_num = seed_num


def run(self):
from seedsigner.helpers import embit_utils
from seedsigner.controller import Controller
Expand All @@ -1760,7 +1807,7 @@ def run(self):

elif button_data[selected_menu_num] == self.SINGLE_SIG:
sig_type = SettingsConstants.SINGLE_SIG
destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR))
destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR, seed_num=self.seed_num))

elif button_data[selected_menu_num] == self.MULTISIG:
sig_type = SettingsConstants.MULTISIG
Expand Down
3 changes: 2 additions & 1 deletion tests/screenshot_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ def PSBTOpReturnView_raw_hex_data_cb_before():
ScreenshotConfig(seed_views.SeedExportXpubCoordinatorView, dict(seed_num=0, sig_type="ss", script_type="nat")),
ScreenshotConfig(seed_views.SeedExportXpubWarningView, dict(seed_num=0, sig_type="msig", script_type="nes", coordinator="spd", custom_derivation="")),
ScreenshotConfig(seed_views.SeedExportXpubDetailsView, dict(seed_num=0, sig_type="ss", script_type="nat", coordinator="bw", custom_derivation="")),
#ScreenshotConfig(SeedExportXpubQRDisplayView),
ScreenshotConfig(seed_views.SeedExportXpubQRDisplayView, dict(seed_num=0, coordinator="bw", derivation_path="m/84'/1'/0'", sig_type="ss")),
ScreenshotConfig(seed_views.SeedExportXpubQRAskVerifyAddView, dict(seed_num=0)),
ScreenshotConfig(seed_views.SeedWordsWarningView, dict(seed_num=0)),
ScreenshotConfig(seed_views.SeedWordsView, dict(seed_num=0)),
ScreenshotConfig(seed_views.SeedWordsView, dict(seed_num=0, page_index=2), screenshot_name="SeedWordsView_2"),
Expand Down
9 changes: 8 additions & 1 deletion tests/test_flows_seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ def flowtest_standard_xpub(sig_tuple, script_tuple, coord_tuple):
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(seed_views.SeedExportXpubQRAskVerifyAddView, screen_return_value=0),
FlowStep(scan_views.ScanAddressView, screen_return_value=0),
FlowStep(MainMenuView),
]
)
Expand Down Expand Up @@ -330,6 +332,8 @@ def test_export_xpub_custom_derivation_flow(self):
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(seed_views.SeedExportXpubQRAskVerifyAddView, screen_return_value=0),
FlowStep(scan_views.ScanAddressView, screen_return_value=0),
FlowStep(MainMenuView),
]
)
Expand Down Expand Up @@ -361,6 +365,8 @@ def test_export_xpub_skip_non_option_flow(self):
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(seed_views.SeedExportXpubQRAskVerifyAddView, screen_return_value=0),
FlowStep(scan_views.ScanAddressView, screen_return_value=0),
FlowStep(MainMenuView),
]
)
Expand Down Expand Up @@ -392,6 +398,8 @@ def test_export_xpub_electrum_seed_flow(self):
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(seed_views.SeedExportXpubQRAskVerifyAddView, screen_return_value=0),
FlowStep(scan_views.ScanAddressView, screen_return_value=0),
FlowStep(MainMenuView),
]
)
Expand Down Expand Up @@ -674,4 +682,3 @@ def expect_unsupported_derivation(load_message: Callable):

self.settings.set_value(SettingsConstants.SETTING__NETWORK, SettingsConstants.MAINNET)
expect_unsupported_derivation(self.load_custom_derivation_into_decoder)

Loading