mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
Test suite fixes; DonateView
* Updated custom SeedSigner icon font pack * Removing debugging print statements * Default button handling for BaseTopNavScreen
This commit is contained in:
@@ -49,7 +49,7 @@ class Controller(Singleton):
|
||||
rather than at the top in order avoid circular imports.
|
||||
"""
|
||||
|
||||
VERSION = "0.5.0 Pre-Release 3"
|
||||
VERSION = "0.5.0"
|
||||
|
||||
# Declare class member vars with type hints to enable richer IDE support throughout
|
||||
# the code.
|
||||
@@ -185,15 +185,6 @@ class Controller(Singleton):
|
||||
opening_splash = OpeningSplashView()
|
||||
opening_splash.start()
|
||||
|
||||
# TODO: Remove for v0.5.0 production release
|
||||
WarningScreen(
|
||||
title="Warning",
|
||||
status_headline="Pre-Release Code",
|
||||
text="Do not use this with real funds or to create new secure keys!",
|
||||
show_back_button=False,
|
||||
).display()
|
||||
|
||||
|
||||
""" Class references can be stored as variables in python!
|
||||
|
||||
This loop receives a View class to execute and stores it in the `View_cls`
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import math
|
||||
from operator import is_
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
@@ -33,7 +32,7 @@ class GUIConstants:
|
||||
ICON_FONT_SIZE = 22
|
||||
ICON_INLINE_FONT_SIZE = 24
|
||||
ICON_LARGE_BUTTON_SIZE = 36
|
||||
ICON_PRIMARY_SCREEN_SIZE = 44
|
||||
ICON_PRIMARY_SCREEN_SIZE = 60
|
||||
|
||||
TOP_NAV_TITLE_FONT_NAME = "OpenSans-SemiBold"
|
||||
TOP_NAV_TITLE_FONT_SIZE = 20
|
||||
@@ -109,18 +108,19 @@ class FontAwesomeIconConstants:
|
||||
class SeedSignerCustomIconConstants:
|
||||
LARGE_CHEVRON_LEFT = "\ue900"
|
||||
SMALL_CHEVRON_RIGHT = "\ue901"
|
||||
PAGE_DOWN = "\ue902"
|
||||
PAGE_UP = "\ue903"
|
||||
CIRCLE_X = "\ue904"
|
||||
CIRCLE_EXCLAMATION = "\ue905"
|
||||
CIRCLE_CHECK = "\ue906"
|
||||
FINGERPRINT = "\ue907"
|
||||
PATH = "\ue908"
|
||||
BITCOIN_LOGO = "\ue909"
|
||||
BITCOIN_LOGO_2 = "\ue90a"
|
||||
PAGE_DOWN = "\ue902"
|
||||
PLUS = "\ue904"
|
||||
CIRCLE_CHECK = "\ue907"
|
||||
CIRCLE_EXCLAMATION = "\ue908"
|
||||
CIRCLE_X = "\ue909"
|
||||
FINGERPRINT = "\ue90a"
|
||||
PATH = "\ue90b"
|
||||
BITCOIN_LOGO_STRAIGHT = "\ue90c"
|
||||
BITCOIN_LOGO_TILTED = "\ue90d"
|
||||
|
||||
MIN_VALUE = LARGE_CHEVRON_LEFT
|
||||
MAX_VALUE = BITCOIN_LOGO_2
|
||||
MAX_VALUE = BITCOIN_LOGO_TILTED
|
||||
|
||||
|
||||
|
||||
@@ -419,7 +419,7 @@ class TextArea(BaseComponent):
|
||||
class Icon(BaseComponent):
|
||||
screen_x: int = 0
|
||||
screen_y: int = 0
|
||||
icon_name: str = SeedSignerCustomIconConstants.BITCOIN_LOGO
|
||||
icon_name: str = SeedSignerCustomIconConstants.BITCOIN_LOGO_TILTED
|
||||
icon_size: int = GUIConstants.ICON_FONT_SIZE
|
||||
icon_color: str = GUIConstants.BODY_FONT_COLOR
|
||||
|
||||
@@ -438,11 +438,11 @@ class Icon(BaseComponent):
|
||||
|
||||
def render(self):
|
||||
self.image_draw.text(
|
||||
(self.screen_x, self.screen_y),
|
||||
(self.screen_x, self.screen_y + self.height),
|
||||
text=self.icon_name,
|
||||
font=self.icon_font,
|
||||
fill=self.icon_color,
|
||||
anchor="lt", # left, top anchor to avoid "ascender" gap space
|
||||
anchor="ls",
|
||||
)
|
||||
|
||||
|
||||
@@ -539,7 +539,8 @@ class IconTextLine(BaseComponent):
|
||||
if self.icon_name:
|
||||
icon_y = self.screen_y + int((self.height - self.icon.height)/2)
|
||||
self.icon.screen_y = icon_y
|
||||
self.height = self.icon.height
|
||||
|
||||
self.height = max(self.icon.height, self.height)
|
||||
|
||||
if self.is_text_centered:
|
||||
if self.icon_name:
|
||||
@@ -762,8 +763,6 @@ class BtcAmount(BaseComponent):
|
||||
denomination = Settings.get_instance().get_value(SettingsConstants.SETTING__BTC_DENOMINATION)
|
||||
network = Settings.get_instance().get_value(SettingsConstants.SETTING__NETWORK)
|
||||
|
||||
print(self.total_sats)
|
||||
|
||||
btc_unit = "tBtc"
|
||||
sats_unit = "tSats"
|
||||
if network == SettingsConstants.MAINNET:
|
||||
@@ -789,7 +788,7 @@ class BtcAmount(BaseComponent):
|
||||
btc_icon = Icon(
|
||||
image_draw=draw,
|
||||
canvas=self.paste_image,
|
||||
icon_name=SeedSignerCustomIconConstants.BITCOIN_LOGO,
|
||||
icon_name=SeedSignerCustomIconConstants.BITCOIN_LOGO_TILTED,
|
||||
icon_color=btc_color,
|
||||
icon_size=self.icon_size,
|
||||
screen_x=0,
|
||||
@@ -877,7 +876,7 @@ class BtcAmount(BaseComponent):
|
||||
btc_icon = Icon(
|
||||
image_draw=draw,
|
||||
canvas=self.paste_image,
|
||||
icon_name=SeedSignerCustomIconConstants.BITCOIN_LOGO,
|
||||
icon_name=SeedSignerCustomIconConstants.BITCOIN_LOGO_TILTED,
|
||||
icon_color=btc_color,
|
||||
icon_size=self.icon_size,
|
||||
screen_x=0,
|
||||
@@ -1047,7 +1046,6 @@ class Button(BaseComponent):
|
||||
self.icon_selected = Icon(icon_name=self.icon_name, icon_size=self.icon_size, icon_color=self.selected_icon_color)
|
||||
|
||||
if self.is_icon_inline:
|
||||
# TODO: Only apply screen_* at render
|
||||
if self.is_text_centered:
|
||||
# Shift the text's centering
|
||||
if self.text:
|
||||
@@ -1077,6 +1075,7 @@ class Button(BaseComponent):
|
||||
|
||||
self.right_icon_y = math.ceil((self.height - self.right_icon.height)/2)
|
||||
|
||||
|
||||
def render(self):
|
||||
if self.is_selected:
|
||||
background_color = self.selected_color
|
||||
|
||||
@@ -18,17 +18,13 @@ class Renderer(ConfigurableSingleton):
|
||||
|
||||
|
||||
@classmethod
|
||||
def configure_instance(cls, config={}):
|
||||
def configure_instance(cls):
|
||||
from seedsigner.models.settings import Settings
|
||||
super().configure_instance(config)
|
||||
|
||||
# Instantiate the one and only Renderer instance
|
||||
renderer = cls.__new__(cls)
|
||||
cls._instance = renderer
|
||||
|
||||
# TODO: Use Settings values to wire up diff hardware params
|
||||
settings = Settings.get_instance()
|
||||
|
||||
# Eventually we'll be able to plug in other display controllers
|
||||
renderer.disp = ST7789()
|
||||
renderer.canvas_width = renderer.disp.width
|
||||
|
||||
@@ -38,7 +38,6 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
# icon_text_lines_y = self.components[-1].screen_y + self.components[-1].height
|
||||
icon_text_lines_y = self.top_nav.height + GUIConstants.COMPONENT_PADDING
|
||||
|
||||
|
||||
if not self.destination_addresses:
|
||||
# This is a self-transfer
|
||||
spend_amount = self.change_amount
|
||||
@@ -52,7 +51,7 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
|
||||
# Prep the transaction flow chart
|
||||
self.chart_x = 0
|
||||
self.chart_y = self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
self.chart_y = self.components[-1].screen_y + self.components[-1].height + int(GUIConstants.COMPONENT_PADDING/2)
|
||||
chart_height = self.buttons[0].screen_y - self.chart_y - GUIConstants.COMPONENT_PADDING
|
||||
|
||||
# We need to supersample the whole panel so that small/thin elements render
|
||||
@@ -70,7 +69,8 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
font_size = GUIConstants.BODY_FONT_MIN_SIZE * ssf
|
||||
font = Fonts.get_font(GUIConstants.BODY_FONT_NAME, font_size)
|
||||
|
||||
tw, chart_text_height = font.getsize("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") # All possible chars for max range
|
||||
# Measure from anchor "top" to include font pixels below baseline (e.g. the "g" in "change")
|
||||
(left, top, right, chart_text_height) = font.getbbox(text="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", anchor="lt")
|
||||
vertical_center = int(image.height/2)
|
||||
# Supersampling renders thin elements poorly if they land on an even line before scaling down
|
||||
if vertical_center % 2 == 1:
|
||||
|
||||
@@ -56,8 +56,6 @@ class BaseScreen(BaseComponent):
|
||||
|
||||
return self._run()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("------")
|
||||
repr(e)
|
||||
raise e
|
||||
finally:
|
||||
@@ -214,7 +212,42 @@ class BaseTopNavScreen(BaseScreen):
|
||||
|
||||
|
||||
def _run(self):
|
||||
raise Exception("Must implement in a child class")
|
||||
while True:
|
||||
if not self.top_nav.show_back_button and not self.top_nav.show_power_button:
|
||||
# There's no navigation away from this screen; nothing to do here
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
user_input = self.hw_inputs.wait_for(
|
||||
HardwareButtonsConstants.ALL_KEYS,
|
||||
check_release=True,
|
||||
release_keys=HardwareButtonsConstants.KEYS__ANYCLICK
|
||||
)
|
||||
|
||||
with self.renderer.lock:
|
||||
if not self.top_nav.is_selected and user_input in [
|
||||
HardwareButtonsConstants.KEY_LEFT,
|
||||
HardwareButtonsConstants.KEY_UP
|
||||
]:
|
||||
self.top_nav.is_selected = True
|
||||
self.top_nav.render_buttons()
|
||||
|
||||
elif self.top_nav.is_selected and user_input in [
|
||||
HardwareButtonsConstants.KEY_DOWN,
|
||||
HardwareButtonsConstants.KEY_RIGHT
|
||||
]:
|
||||
self.top_nav.is_selected = False
|
||||
self.top_nav.render_buttons()
|
||||
|
||||
elif self.top_nav.is_selected and user_input in HardwareButtonsConstants.KEYS__ANYCLICK:
|
||||
return self.top_nav.selected_button
|
||||
|
||||
else:
|
||||
# Nothing to do with this input
|
||||
continue
|
||||
|
||||
# Write the screen updates
|
||||
self.renderer.show_image()
|
||||
|
||||
|
||||
|
||||
@@ -810,6 +843,7 @@ class DireWarningScreen(WarningScreen):
|
||||
status_color: str = GUIConstants.DIRE_WARNING_COLOR
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class ResetScreen(BaseTopNavScreen):
|
||||
def __post_init__(self):
|
||||
@@ -822,10 +856,6 @@ class ResetScreen(BaseTopNavScreen):
|
||||
screen_y=self.top_nav.height,
|
||||
height=self.canvas_height - self.top_nav.height,
|
||||
))
|
||||
|
||||
def _run(self):
|
||||
while True:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -841,7 +871,3 @@ class PowerOffScreen(BaseTopNavScreen):
|
||||
screen_y=self.top_nav.height,
|
||||
height=self.canvas_height - self.top_nav.height,
|
||||
))
|
||||
|
||||
def _run(self):
|
||||
while True:
|
||||
pass
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import List
|
||||
from seedsigner.gui.components import Button, CheckboxButton, CheckedSelectionButton, FontAwesomeIconConstants, GUIConstants, IconButton, TextArea
|
||||
from seedsigner.gui.screens.scan_screens import ScanScreen
|
||||
|
||||
from seedsigner.gui.screens.screen import ButtonListScreen
|
||||
from seedsigner.gui.screens.screen import BaseTopNavScreen, ButtonListScreen
|
||||
from seedsigner.hardware.buttons import HardwareButtonsConstants
|
||||
from seedsigner.models.settings import SettingsConstants
|
||||
|
||||
@@ -190,3 +190,24 @@ class IOTestScreen(ScanScreen):
|
||||
cur_selected_button.is_selected = True
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class DonateScreen(BaseTopNavScreen):
|
||||
def __post_init__(self):
|
||||
self.title = "Donate"
|
||||
super().__post_init__()
|
||||
|
||||
self.components.append(TextArea(
|
||||
text="SeedSigner is 100% free & open source, funded solely by the Bitcoin community.\n\nDonate onchain or LN at:",
|
||||
screen_y=self.top_nav.height + 3*GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
|
||||
self.components.append(TextArea(
|
||||
text="seedsigner.com",
|
||||
font_size=GUIConstants.TOP_NAV_TITLE_FONT_SIZE + 8,
|
||||
font_color=GUIConstants.ACCENT_COLOR,
|
||||
supersampling_factor=1,
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
))
|
||||
|
||||
@@ -60,8 +60,6 @@ class DecodeQR:
|
||||
|
||||
qr_type = DecodeQR.detect_segment_type(data, wordlist_language_code=self.wordlist_language_code)
|
||||
|
||||
print(qr_type)
|
||||
|
||||
if self.qr_type == None:
|
||||
self.qr_type = qr_type
|
||||
|
||||
@@ -97,6 +95,10 @@ class DecodeQR:
|
||||
|
||||
elif self.qr_type != qr_type:
|
||||
raise Exception('QR Fragment Unexpected Type Change')
|
||||
|
||||
if not self.decoder:
|
||||
# Did not find any recognizable format
|
||||
return DecodeQRStatus.INVALID
|
||||
|
||||
# Process the binary formats first
|
||||
if self.qr_type == QRType.SEED__COMPACTSEEDQR:
|
||||
|
||||
@@ -58,7 +58,6 @@ class PSBTParser():
|
||||
|
||||
def _set_root(self):
|
||||
self.root = bip32.HDKey.from_seed(self.seed.seed_bytes, version=NETWORKS[SettingsConstants.map_network_to_embit(self.network)]["xprv"])
|
||||
print(f"root: {self.root}")
|
||||
|
||||
|
||||
def parse(self):
|
||||
@@ -85,7 +84,6 @@ class PSBTParser():
|
||||
|
||||
def _parse_inputs(self):
|
||||
self.input_amount = 0
|
||||
print(f"psbt.inputs: {self.psbt.inputs}")
|
||||
self.num_inputs = len(self.psbt.inputs)
|
||||
for inp in self.psbt.inputs:
|
||||
if inp.witness_utxo:
|
||||
@@ -343,5 +341,5 @@ class PSBTParser():
|
||||
i = change_data["output_index"]
|
||||
output = self.psbt.outputs[i]
|
||||
is_owner = descriptor.owns(output)
|
||||
print(f"{self.psbt.tx.vout[i].script_pubkey.address()} | {output.value} | {is_owner}")
|
||||
# print(f"{self.psbt.tx.vout[i].script_pubkey.address()} | {output.value} | {is_owner}")
|
||||
return is_owner
|
||||
|
||||
@@ -513,8 +513,6 @@ class SettingsDefinition:
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
import os
|
||||
print("Exporting SettingsDefinition to json")
|
||||
|
||||
output_file = "settings_definition.json"
|
||||
with open(output_file, 'w') as json_file:
|
||||
|
||||
Binary file not shown.
@@ -109,7 +109,7 @@ class PSBTOverviewView(View):
|
||||
num_change_outputs = 0
|
||||
num_self_transfer_outputs = 0
|
||||
for change_output in change_data:
|
||||
print(f"""{change_output["derivation_path"][0]}""")
|
||||
# print(f"""{change_output["derivation_path"][0]}""")
|
||||
if change_output["derivation_path"][0].split("/")[-2] == "1":
|
||||
num_change_outputs += 1
|
||||
else:
|
||||
@@ -277,8 +277,6 @@ class PSBTChangeDetailsView(View):
|
||||
# Single-sig verification is easy. We expect to find a single fingerprint
|
||||
# and derivation path.
|
||||
seed_fingerprint = self.controller.psbt_seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
|
||||
print(f"seed fingerprint: {seed_fingerprint}")
|
||||
print(change_data)
|
||||
|
||||
if seed_fingerprint not in change_data.get("fingerprint"):
|
||||
# TODO: Something is wrong with this psbt(?). Reroute to warning?
|
||||
|
||||
@@ -256,7 +256,6 @@ class SeedReviewPassphraseView(View):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.seed = self.controller.storage.get_pending_seed()
|
||||
print(f"SeedReviewPassphraseView self.seed: {self.seed}")
|
||||
|
||||
|
||||
def run(self):
|
||||
@@ -348,7 +347,7 @@ class SeedOptionsView(View):
|
||||
REVIEW_PSBT = "Review PSBT"
|
||||
VERIFY_ADDRESS = "Verify Addr"
|
||||
EXPORT_XPUB = "Export Xpub"
|
||||
BACKUP = ("Backup Seed", None, None, None, FontAwesomeIconConstants.CIRCLE_CHEVRON_RIGHT)
|
||||
BACKUP = ("Backup Seed", None, None, None, SeedSignerCustomIconConstants.SMALL_CHEVRON_RIGHT)
|
||||
DISCARD = ("Discard Seed", None, None, "red")
|
||||
|
||||
button_data = []
|
||||
@@ -366,7 +365,7 @@ class SeedOptionsView(View):
|
||||
if self.controller.psbt:
|
||||
if PSBTParser.has_matching_input_fingerprint(self.controller.psbt, self.seed, network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)):
|
||||
if self.controller.resume_main_flow and self.controller.resume_main_flow == Controller.FLOW__PSBT:
|
||||
# Re-route us directly back to the start of the PSBT flow
|
||||
# Re-route us directly back to the start of the PSBT flow
|
||||
self.controller.resume_main_flow = None
|
||||
self.controller.psbt_seed = self.seed
|
||||
return Destination(PSBTOverviewView, skip_current_view=True)
|
||||
@@ -1122,9 +1121,6 @@ class AddressVerificationStartView(View):
|
||||
self.controller.unverified_address["sig_type"] = sig_type
|
||||
self.controller.unverified_address["derivation_path"] = derivation_path
|
||||
|
||||
import json
|
||||
print(json.dumps(self.controller.unverified_address, indent=4))
|
||||
|
||||
return destination
|
||||
|
||||
|
||||
@@ -1332,8 +1328,6 @@ class SeedAddressVerificationView(View):
|
||||
# Successfully verified the addr; update the data
|
||||
self.controller.unverified_address["verified_index"] = self.verified_index.cur_count
|
||||
self.controller.unverified_address["verified_index_is_change"] = self.verified_index_is_change.cur_count == 1
|
||||
import json
|
||||
print(json.dumps(self.controller.unverified_address, indent=4))
|
||||
return Destination(AddressVerificationSuccessView, view_args=dict(seed_num=self.seed_num))
|
||||
|
||||
else:
|
||||
@@ -1379,14 +1373,12 @@ class SeedAddressVerificationView(View):
|
||||
(receive_address, change_address) = self.derive_single_sig(i)
|
||||
|
||||
if self.address == receive_address:
|
||||
print(f"Verified receive addr #{i}!")
|
||||
self.verified_index.set_value(i)
|
||||
self.verified_index_is_change.set_value(0)
|
||||
self.keep_running = False
|
||||
break
|
||||
|
||||
elif self.address == change_address:
|
||||
print(f"Verified change addr #{i}!")
|
||||
self.verified_index.set_value(i)
|
||||
self.verified_index_is_change.set_value(1)
|
||||
self.keep_running = False
|
||||
@@ -1490,8 +1482,6 @@ class MultisigWalletDescriptorView(View):
|
||||
|
||||
policy = descriptor.brief_policy.split("multisig")[0].strip()
|
||||
|
||||
print(fingerprints)
|
||||
|
||||
RETURN = "Return to PSBT"
|
||||
VERIFY = "Verify Addr"
|
||||
OK = "OK"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from seedsigner.gui.components import FontAwesomeIconConstants
|
||||
from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerCustomIconConstants
|
||||
from seedsigner.models.decode_qr import DecodeQR
|
||||
|
||||
from .view import View, Destination, BackStackView, MainMenuView
|
||||
@@ -16,6 +16,9 @@ class SettingsMenuView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
IO_TEST = "I/O test"
|
||||
DONATE = "Donate"
|
||||
|
||||
settings_entries = SettingsDefinition.get_settings_entries(
|
||||
visibiilty=self.visibility
|
||||
)
|
||||
@@ -32,16 +35,17 @@ class SettingsMenuView(View):
|
||||
title = "Settings"
|
||||
|
||||
# Set up the next nested level of menuing
|
||||
button_data.append(("Advanced", None, None, None, FontAwesomeIconConstants.CIRCLE_CHEVRON_RIGHT))
|
||||
button_data.append(("Advanced", None, None, None, SeedSignerCustomIconConstants.SMALL_CHEVRON_RIGHT))
|
||||
next = Destination(SettingsMenuView, view_args={"visibility": SettingsConstants.VISIBILITY__ADVANCED})
|
||||
|
||||
button_data.append("I/O test")
|
||||
button_data.append(IO_TEST)
|
||||
button_data.append(DONATE)
|
||||
|
||||
elif self.visibility == SettingsConstants.VISIBILITY__ADVANCED:
|
||||
title = "Advanced"
|
||||
|
||||
# So far there are no real Developer options; disabling for now
|
||||
# button_data.append(("Developer Options", None, None, None, FontAwesomeIconConstants.CIRCLE_CHEVRON_RIGHT))
|
||||
# button_data.append(("Developer Options", None, None, None, SeedSignerCustomIconConstants.SMALL_CHEVRON_RIGHT))
|
||||
# next = Destination(SettingsMenuView, view_args={"visibility": SettingsConstants.VISIBILITY__DEVELOPER})
|
||||
next = None
|
||||
|
||||
@@ -67,9 +71,12 @@ class SettingsMenuView(View):
|
||||
elif selected_menu_num == len(settings_entries):
|
||||
return next
|
||||
|
||||
elif selected_menu_num == len(settings_entries) + 1:
|
||||
elif len(button_data) > selected_menu_num and button_data[selected_menu_num] == IO_TEST:
|
||||
return Destination(IOTestView)
|
||||
|
||||
elif len(button_data) > selected_menu_num and button_data[selected_menu_num] == DONATE:
|
||||
return Destination(DonateView)
|
||||
|
||||
else:
|
||||
# TODO: Free-entry types (are there any?) will need their own SettingsEntryUpdateFreeEntryView(?).
|
||||
return Destination(SettingsEntryUpdateSelectionView, view_args={"attr_name": settings_entries[selected_menu_num].attr_name})
|
||||
@@ -179,3 +186,11 @@ class IOTestView(View):
|
||||
).display()
|
||||
|
||||
return Destination(SettingsMenuView)
|
||||
|
||||
|
||||
|
||||
class DonateView(View):
|
||||
def run(self):
|
||||
settings_screens.DonateScreen().display()
|
||||
|
||||
return Destination(SettingsMenuView)
|
||||
|
||||
@@ -4,7 +4,6 @@ import time
|
||||
|
||||
from PIL import Image
|
||||
from PIL.ImageOps import autocontrast
|
||||
from seedsigner.gui.screens.screen import LargeButtonScreen
|
||||
|
||||
from seedsigner.hardware.camera import Camera
|
||||
from seedsigner.gui.components import FontAwesomeIconConstants
|
||||
@@ -13,9 +12,9 @@ from seedsigner.gui.screens.tools_screens import ToolsDiceEntropyEntryScreen, To
|
||||
from seedsigner.helpers import mnemonic_generation
|
||||
from seedsigner.models.seed import Seed
|
||||
from seedsigner.models.settings_definition import SettingsConstants
|
||||
from seedsigner.views.seed_views import SeedDiscardView, SeedFinalizeView, SeedMnemonicEntryView, SeedWordsView, SeedWordsWarningView
|
||||
from seedsigner.views.seed_views import SeedDiscardView, SeedFinalizeView, SeedMnemonicEntryView, SeedWordsWarningView
|
||||
|
||||
from .view import NotYetImplementedView, View, Destination, BackStackView, MainMenuView
|
||||
from .view import View, Destination, BackStackView
|
||||
|
||||
|
||||
|
||||
@@ -207,9 +206,9 @@ class ToolsDiceEntropyEntryView(View):
|
||||
if ret == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
print(ret)
|
||||
print(f"Dice rolls: {ret}")
|
||||
dice_seed_phrase = mnemonic_generation.generate_mnemonic_from_dice(ret)
|
||||
print(dice_seed_phrase)
|
||||
print(f"""Mnemonic: "{dice_seed_phrase}" """)
|
||||
|
||||
# Add the mnemonic as an in-memory Seed
|
||||
seed = Seed(dice_seed_phrase, wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE))
|
||||
|
||||
@@ -3,6 +3,7 @@ import pytest
|
||||
from mock import MagicMock
|
||||
from seedsigner.controller import Controller
|
||||
from seedsigner.models.settings import Settings
|
||||
from seedsigner.models.settings_definition import SettingsConstants
|
||||
|
||||
|
||||
|
||||
@@ -11,10 +12,6 @@ def test_singleton_init_fails():
|
||||
with pytest.raises(Exception):
|
||||
c = Controller()
|
||||
|
||||
def test_singleton_get_instance_without_configure_fails():
|
||||
""" Calling get_instance() without first calling configure_instance() should fail """
|
||||
with pytest.raises(Exception):
|
||||
c = Controller.get_instance()
|
||||
|
||||
def test_singleton_get_instance_preserves_state():
|
||||
""" Changes to the Controller singleton should be preserved across calls to get_instance() """
|
||||
@@ -25,38 +22,17 @@ def test_singleton_get_instance_preserves_state():
|
||||
Settings._instance = None
|
||||
Controller._instance = None
|
||||
|
||||
settings = """
|
||||
[system]
|
||||
debug = False
|
||||
default_language = en
|
||||
persistent_settings = False
|
||||
|
||||
[display]
|
||||
text_color = ORANGE
|
||||
qr_background_color = FFFFFF
|
||||
camera_rotation = 0
|
||||
|
||||
[wallet]
|
||||
network = main
|
||||
software = Prompt
|
||||
qr_density = 2
|
||||
custom_derivation = m/0/0
|
||||
compact_seedqr_enabled = False
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
config.read_string(settings)
|
||||
|
||||
# Initialize the instance and verify that it read the config settings
|
||||
Controller.configure_instance(config, disable_hardware=True)
|
||||
Controller.configure_instance(disable_hardware=True)
|
||||
controller = Controller.get_instance()
|
||||
assert controller.color == "ORANGE"
|
||||
assert controller.unverified_address is None
|
||||
|
||||
# Change a value in the instance...
|
||||
controller.color = "purple"
|
||||
controller.unverified_address = "123abc"
|
||||
|
||||
# ...get a new copy of the instance and confirm change
|
||||
controller = Controller.get_instance()
|
||||
assert controller.color == "purple"
|
||||
assert controller.unverified_address == "123abc"
|
||||
|
||||
|
||||
def test_missing_settings_get_defaults():
|
||||
@@ -69,30 +45,9 @@ def test_missing_settings_get_defaults():
|
||||
Settings._instance = None
|
||||
Controller._instance = None
|
||||
|
||||
# Intentionally omit `compact_seedqr_enabled` from settings:
|
||||
settings = """
|
||||
[system]
|
||||
debug = False
|
||||
default_language = en
|
||||
persistent_settings = False
|
||||
|
||||
[display]
|
||||
text_color = ORANGE
|
||||
qr_background_color = FFFFFF
|
||||
camera_rotation = 0
|
||||
|
||||
[wallet]
|
||||
network = main
|
||||
software = Prompt
|
||||
qr_density = 2
|
||||
custom_derivation = m/0/0
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
config.read_string(settings)
|
||||
|
||||
# Controller should parse the settings fine, even though a field is missing
|
||||
Controller.configure_instance(config, disable_hardware=True)
|
||||
Controller.configure_instance(disable_hardware=True)
|
||||
|
||||
# Controller should still have a default value
|
||||
controller = Controller.get_instance()
|
||||
assert controller.settings.compact_seedqr_enabled is False
|
||||
assert controller.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) == SettingsConstants.OPTION__DISABLED
|
||||
|
||||
+25
-84
@@ -3,6 +3,8 @@ from mock import MagicMock
|
||||
from seedsigner.models import Seed, DecodeQR, DecodeQRStatus, QRType, PSBTParser
|
||||
from embit import psbt, bip39
|
||||
|
||||
from seedsigner.models.settings_definition import SettingsConstants
|
||||
|
||||
|
||||
|
||||
# this is an of this bug: https://github.com/Foundation-Devices/foundation-ur-py/issues/3
|
||||
@@ -78,15 +80,15 @@ def test_base64_single_frame_singlsig():
|
||||
|
||||
assert str(tx) == base64_psbt
|
||||
|
||||
mnemonic = "height demise useless trap grow lion found off key clown transfer enroll"
|
||||
mnemonic = "height demise useless trap grow lion found off key clown transfer enroll".split()
|
||||
pw = ""
|
||||
seed = Seed(mnemonic, passphrase=pw)
|
||||
|
||||
assert seed.mnemonic_str == mnemonic
|
||||
assert seed.mnemonic_str == " ".join(mnemonic)
|
||||
|
||||
assert seed.seed_bytes != None
|
||||
|
||||
pp = PSBTParser(tx,seed,"test")
|
||||
pp = PSBTParser(p=tx, seed=seed, network=SettingsConstants.TESTNET)
|
||||
|
||||
assert tx.inputs[0].witness_utxo.value == 25000 # input amount in psbt
|
||||
|
||||
@@ -135,8 +137,8 @@ def test_base64_2_input_p2wsh():
|
||||
|
||||
assert str(tx) == base64_psbt
|
||||
|
||||
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
pp = PSBTParser(tx, Seed(mnemonic), "test")
|
||||
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".split()
|
||||
pp = PSBTParser(p=tx, seed=Seed(mnemonic), network=SettingsConstants.TESTNET)
|
||||
|
||||
assert tx.inputs[0].witness_utxo.value == 10000000 # input amount 1 in psbt
|
||||
|
||||
@@ -162,8 +164,8 @@ def test_base64_1_input_p2sh_p2wsh():
|
||||
|
||||
assert str(tx) == base64_psbt
|
||||
|
||||
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
pp = PSBTParser(tx,Seed(mnemonic, wordlist=bip39.WORDLIST),"test")
|
||||
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".split()
|
||||
pp = PSBTParser(p=tx, seed=Seed(mnemonic), network=SettingsConstants.TESTNET)
|
||||
|
||||
assert tx.inputs[0].witness_utxo.value == 100000000 # input amount 1 in psbt
|
||||
|
||||
@@ -175,62 +177,6 @@ def test_base64_1_input_p2sh_p2wsh():
|
||||
|
||||
assert len(pp.destination_addresses) == 1
|
||||
|
||||
def test_ur_legacy():
|
||||
|
||||
qrcodes = [
|
||||
"UR:BYTES/2OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3DJV07U94KMSQQQQQQQQZQ8A0CQSYQQQQQQQZQHA78FG5TANFV9Z2TPDWE57AUVCN2T06TDGCAJNYPAT9LVG5QFMU5QSQQQQQRLLLLLL3NZ3KK6TMJ2TTRH6SKU05V76GHEANAT2QSDCDPFG67YZQQPFFZKSZQQQQQQ0LLLLLUPYQRGRQQQQQQQQYGQZP4XCCZ3WFNWK"
|
||||
, "UR:BYTES/2OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3DJV07U94KMSQQQQQQQQZQ8A0CQSYQQQQQQQZQHA78FG5TANFV9Z2TPDWE57AUVCN2T06TDGCAJNYPAT9LVG5QFMU5QSQQQQQRLLLLLL3NZ3KK6TMJ2TTRH6SKU05V76GHEANAT2QSDCDPFG67YZQQPFFZKSZQQQQQQ0LLLLLUPYQRGRQQQQQQQQYGQZP4XCCZ3WFNWK"
|
||||
, "UR:BYTES/3OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GMPEQQQQQQQQQQ9SQZSRR3CY9AYW5V4Y0A5DMWD57JD7FXX9G95PYWVZYQGSX2Y0YENNVNLEHXVMTDS86QM4VNDHG9V0K3PNDMGAJLWLSTS6504SZYP80TGGDPFZR8AM7XZNM77XWKRHP0D0Y540FLPTA5C2HRUQ0"
|
||||
, "UR:BYTES/3OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GMPEQQQQQQQQQQ9SQZSRR3CY9AYW5V4Y0A5DMWD57JD7FXX9G95PYWVZYQGSX2Y0YENNVNLEHXVMTDS86QM4VNDHG9V0K3PNDMGAJLWLSTS6504SZYP80TGGDPFZR8AM7XZNM77XWKRHP0D0Y540FLPTA5C2HRUQ0"
|
||||
, "UR:BYTES/3OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GMPEQQQQQQQQQQ9SQZSRR3CY9AYW5V4Y0A5DMWD57JD7FXX9G95PYWVZYQGSX2Y0YENNVNLEHXVMTDS86QM4VNDHG9V0K3PNDMGAJLWLSTS6504SZYP80TGGDPFZR8AM7XZNM77XWKRHP0D0Y540FLPTA5C2HRUQ0"
|
||||
, "UR:BYTES/4OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3Y0PYQFPQW6EJAC7ZGZUYVSCVLPJUS8CVFT8389KFXX82VM3YQMT26FRVHLVUQJ8XPZQYGR9FH3H26HSK99ZH3MQ4D7EHCS7Y752EP37HGG2X6RU5V3PKD7PMSPZQF9L7DESNZNS0H4V56978HCMAEG9D99CNY5LYCV247F58T7M66QUQYSS907LVPMZLKYAWMDGRRKT"
|
||||
, "UR:BYTES/4OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3Y0PYQFPQW6EJAC7ZGZUYVSCVLPJUS8CVFT8389KFXX82VM3YQMT26FRVHLVUQJ8XPZQYGR9FH3H26HSK99ZH3MQ4D7EHCS7Y752EP37HGG2X6RU5V3PKD7PMSPZQF9L7DESNZNS0H4V56978HCMAEG9D99CNY5LYCV247F58T7M66QUQYSS907LVPMZLKYAWMDGRRKT"
|
||||
, "UR:BYTES/5OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/GX8KAW6FYVRQ2HKD5T4WUJJ7K68T5262QQQQQQQPQY45QRGRQQQQQQQQYGQZP4XCCZ3WFNWK0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GQYZ5W53PQFMWVTS2CUAWJ43C87WR6ZZ705V5LU2AEKGEF8AM8FWP6TQFTD7AZGGZMKAVTUW8HDRTKMEET55FK6MX"
|
||||
, "UR:BYTES/5OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/GX8KAW6FYVRQ2HKD5T4WUJJ7K68T5262QQQQQQQPQY45QRGRQQQQQQQQYGQZP4XCCZ3WFNWK0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GQYZ5W53PQFMWVTS2CUAWJ43C87WR6ZZ705V5LU2AEKGEF8AM8FWP6TQFTD7AZGGZMKAVTUW8HDRTKMEET55FK6MX"
|
||||
, "UR:BYTES/5OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/GX8KAW6FYVRQ2HKD5T4WUJJ7K68T5262QQQQQQQPQY45QRGRQQQQQQQQYGQZP4XCCZ3WFNWK0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GQYZ5W53PQFMWVTS2CUAWJ43C87WR6ZZ705V5LU2AEKGEF8AM8FWP6TQFTD7AZGGZMKAVTUW8HDRTKMEET55FK6MX"
|
||||
, "UR:BYTES/6OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/KTS8U3XQP95WYR8VQ9V2KWHCTS399T3ZQCP8DE3WPTRN462K8QLEC0GGTE73JNL3THXER9YLHVA9C8FVP9DHM5GUKUGYR2PSQQQGQQGQQZQQQQQQSQPQQQYQQQQQQQQRQQQQQGSXQTWM4303C7A5DWM089WJ3XMTV6EWQLJYCQYK3CSVASQ432E6LPWZY8XYUTHDXVQQ"
|
||||
, "UR:BYTES/6OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/KTS8U3XQP95WYR8VQ9V2KWHCTS399T3ZQCP8DE3WPTRN462K8QLEC0GGTE73JNL3THXER9YLHVA9C8FVP9DHM5GUKUGYR2PSQQQGQQGQQZQQQQQQSQPQQQYQQQQQQQQRQQQQQGSXQTWM4303C7A5DWM089WJ3XMTV6EWQLJYCQYK3CSVASQ432E6LPWZY8XYUTHDXVQQ"
|
||||
, "UR:BYTES/7OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/QZQQZQQQSQQQQQYQQGQQPQQQQQQQQQCQQQQQQQQPQ9R4YGGZDYQESZAAQHLY3L9L5VMSYRCQAXPWR6ES83NEC5XTMD4HPKVNGF4ZZQ4XNF7SKKG5KVH0UZPD3XXVNDWVP2CQAP0GLNRGQP2DHAD0U9HYLAF2UGSZQF5SRXQTH5ZLUJ8UH73NWQS0QR5C9C0TXQ7X08ZS"
|
||||
, "UR:BYTES/7OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/QZQQZQQQSQQQQQYQQGQQPQQQQQQQQQCQQQQQQQQPQ9R4YGGZDYQESZAAQHLY3L9L5VMSYRCQAXPWR6ES83NEC5XTMD4HPKVNGF4ZZQ4XNF7SKKG5KVH0UZPD3XXVNDWVP2CQAP0GLNRGQP2DHAD0U9HYLAF2UGSZQF5SRXQTH5ZLUJ8UH73NWQS0QR5C9C0TXQ7X08ZS"
|
||||
, "UR:BYTES/1OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/TYPUUURNVF607QGQ05PQQQQQQ9VXGQQDUMWH3LD9GH8XF4ZYJQNMQ80L6MC0KMV6JMVV22RDPZP0SQQQQQQQPLLLLLLSYS8ZQYQQQQQQQQTQQ9P6MG599TND0NJ54AZSE0ZHSHLY7Q4P94C79GQSQQQQQQQZYQPQ68Q0NLXPZYTUVURY6X4G8U7T5H4QVCHY3KKXAW0R"
|
||||
, "UR:BYTES/1OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/TYPUUURNVF607QGQ05PQQQQQQ9VXGQQDUMWH3LD9GH8XF4ZYJQNMQ80L6MC0KMV6JMVV22RDPZP0SQQQQQQQPLLLLLLSYS8ZQYQQQQQQQQTQQ9P6MG599TND0NJ54AZSE0ZHSHLY7Q4P94C79GQSQQQQQQQZYQPQ68Q0NLXPZYTUVURY6X4G8U7T5H4QVCHY3KKXAW0R"
|
||||
, "UR:BYTES/2OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3DJV07U94KMSQQQQQQQQZQ8A0CQSYQQQQQQQZQHA78FG5TANFV9Z2TPDWE57AUVCN2T06TDGCAJNYPAT9LVG5QFMU5QSQQQQQRLLLLLL3NZ3KK6TMJ2TTRH6SKU05V76GHEANAT2QSDCDPFG67YZQQPFFZKSZQQQQQQ0LLLLLUPYQRGRQQQQQQQQYGQZP4XCCZ3WFNWK"
|
||||
, "UR:BYTES/2OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3DJV07U94KMSQQQQQQQQZQ8A0CQSYQQQQQQQZQHA78FG5TANFV9Z2TPDWE57AUVCN2T06TDGCAJNYPAT9LVG5QFMU5QSQQQQQRLLLLLL3NZ3KK6TMJ2TTRH6SKU05V76GHEANAT2QSDCDPFG67YZQQPFFZKSZQQQQQQ0LLLLLUPYQRGRQQQQQQQQYGQZP4XCCZ3WFNWK"
|
||||
, "UR:BYTES/3OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GMPEQQQQQQQQQQ9SQZSRR3CY9AYW5V4Y0A5DMWD57JD7FXX9G95PYWVZYQGSX2Y0YENNVNLEHXVMTDS86QM4VNDHG9V0K3PNDMGAJLWLSTS6504SZYP80TGGDPFZR8AM7XZNM77XWKRHP0D0Y540FLPTA5C2HRUQ0"
|
||||
, "UR:BYTES/3OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GMPEQQQQQQQQQQ9SQZSRR3CY9AYW5V4Y0A5DMWD57JD7FXX9G95PYWVZYQGSX2Y0YENNVNLEHXVMTDS86QM4VNDHG9V0K3PNDMGAJLWLSTS6504SZYP80TGGDPFZR8AM7XZNM77XWKRHP0D0Y540FLPTA5C2HRUQ0"
|
||||
, "UR:BYTES/3OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/0QXPCDECDVJ8PS0DPW59YDWSH262DUZV6VAT4E4GMPEQQQQQQQQQQ9SQZSRR3CY9AYW5V4Y0A5DMWD57JD7FXX9G95PYWVZYQGSX2Y0YENNVNLEHXVMTDS86QM4VNDHG9V0K3PNDMGAJLWLSTS6504SZYP80TGGDPFZR8AM7XZNM77XWKRHP0D0Y540FLPTA5C2HRUQ0"
|
||||
, "UR:BYTES/4OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3Y0PYQFPQW6EJAC7ZGZUYVSCVLPJUS8CVFT8389KFXX82VM3YQMT26FRVHLVUQJ8XPZQYGR9FH3H26HSK99ZH3MQ4D7EHCS7Y752EP37HGG2X6RU5V3PKD7PMSPZQF9L7DESNZNS0H4V56978HCMAEG9D99CNY5LYCV247F58T7M66QUQYSS907LVPMZLKYAWMDGRRKT"
|
||||
, "UR:BYTES/4OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/3Y0PYQFPQW6EJAC7ZGZUYVSCVLPJUS8CVFT8389KFXX82VM3YQMT26FRVHLVUQJ8XPZQYGR9FH3H26HSK99ZH3MQ4D7EHCS7Y752EP37HGG2X6RU5V3PKD7PMSPZQF9L7DESNZNS0H4V56978HCMAEG9D99CNY5LYCV247F58T7M66QUQYSS907LVPMZLKYAWMDGRRKT"
|
||||
, "UR:BYTES/6OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/KTS8U3XQP95WYR8VQ9V2KWHCTS399T3ZQCP8DE3WPTRN462K8QLEC0GGTE73JNL3THXER9YLHVA9C8FVP9DHM5GUKUGYR2PSQQQGQQGQQZQQQQQQSQPQQQYQQQQQQQQRQQQQQGSXQTWM4303C7A5DWM089WJ3XMTV6EWQLJYCQYK3CSVASQ432E6LPWZY8XYUTHDXVQQ"
|
||||
, "UR:BYTES/7OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/QZQQZQQQSQQQQQYQQGQQPQQQQQQQQQCQQQQQQQQPQ9R4YGGZDYQESZAAQHLY3L9L5VMSYRCQAXPWR6ES83NEC5XTMD4HPKVNGF4ZZQ4XNF7SKKG5KVH0UZPD3XXVNDWVP2CQAP0GLNRGQP2DHAD0U9HYLAF2UGSZQF5SRXQTH5ZLUJ8UH73NWQS0QR5C9C0TXQ7X08ZS"
|
||||
, "UR:BYTES/7OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/QZQQZQQQSQQQQQYQQGQQPQQQQQQQQQCQQQQQQQQPQ9R4YGGZDYQESZAAQHLY3L9L5VMSYRCQAXPWR6ES83NEC5XTMD4HPKVNGF4ZZQ4XNF7SKKG5KVH0UZPD3XXVNDWVP2CQAP0GLNRGQP2DHAD0U9HYLAF2UGSZQF5SRXQTH5ZLUJ8UH73NWQS0QR5C9C0TXQ7X08ZS"
|
||||
, "UR:BYTES/8OF8/MMH5LDF9RK3QKCUL3VM26M6270FFJU50CU6YT5WM65783GL5EJMQPG3WV3/E0DKKUXEJDPX589HZPQ6SVQQQZQQZQQQSQQQQQYQQGQQPQQPQQQQQPQQQQQZYQSZ56D86Z6EZJEJALSG9KYCEJD4ES9TQR59AR7VDQQ9FKL44LSKUNL3E38ZAMFNQQQQSQQSQQYQQQQQPQQZQQQGQQGQQQQQGQQQQQQQ0P6SEH"
|
||||
]
|
||||
|
||||
d = DecodeQR()
|
||||
for i in qrcodes:
|
||||
if d.add_data(i) == DecodeQRStatus.COMPLETE:
|
||||
break
|
||||
assert d.qr_type == QRType.PSBT__LEGACY_UR
|
||||
|
||||
#complete should be true
|
||||
assert d.is_complete
|
||||
|
||||
tx = d.get_psbt()
|
||||
|
||||
mnemonic = "zone zone zone zone zone abandon ability able abandon ability able abstract"
|
||||
pp = PSBTParser(tx,Seed(mnemonic, wordlist=bip39.WORDLIST),"test")
|
||||
|
||||
assert pp.input_amount == 200000
|
||||
|
||||
assert pp.fee_amount == 226
|
||||
|
||||
assert pp.spend_amount == 123456
|
||||
|
||||
assert pp.change_amount == (pp.input_amount - pp.spend_amount - pp.fee_amount)
|
||||
|
||||
assert pp.change_amount == 76318
|
||||
|
||||
|
||||
def test_specter_multisig_animated_qr():
|
||||
qrcodes = [
|
||||
@@ -252,8 +198,8 @@ def test_specter_multisig_animated_qr():
|
||||
|
||||
tx = d.get_psbt()
|
||||
|
||||
mnemonic = "zone zone zone zone zone abandon ability able abandon ability able abstract"
|
||||
pp = PSBTParser(tx,Seed(mnemonic, wordlist=bip39.WORDLIST),"test")
|
||||
mnemonic = "zone zone zone zone zone abandon ability able abandon ability able abstract".split()
|
||||
pp = PSBTParser(p=tx, seed=Seed(mnemonic), network=SettingsConstants.TESTNET)
|
||||
|
||||
assert pp.input_amount == 1052818
|
||||
|
||||
@@ -290,8 +236,8 @@ def test_specter_multisig_animated_qr():
|
||||
|
||||
tx2 = d2.get_psbt()
|
||||
|
||||
mnemonic2 = "able bacon cable able bacon cable abandon abandon abandon abandon abandon access"
|
||||
pp2 = PSBTParser(tx2,Seed(mnemonic2, wordlist=bip39.WORDLIST),"test")
|
||||
mnemonic2 = "able bacon cable able bacon cable abandon abandon abandon abandon abandon access".split()
|
||||
pp2 = PSBTParser(p=tx2, seed=Seed(mnemonic2), network=SettingsConstants.TESTNET)
|
||||
|
||||
assert pp2.input_amount == 1052818
|
||||
|
||||
@@ -334,8 +280,8 @@ def test_short_4_letter_mnemonic_qr():
|
||||
assert d.is_complete
|
||||
assert d.get_seed_phrase() == ["height", "demise", "useless", "trap", "grow", "lion", "found", "off", "key", "clown", "transfer", "enroll"]
|
||||
|
||||
def test_bitcoin_address():
|
||||
|
||||
|
||||
def test_bitcoin_address():
|
||||
bad1 = "loremipsum"
|
||||
bad2 = "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae"
|
||||
bad3 = "121802020768124106400009195602431595117715840445"
|
||||
@@ -361,49 +307,44 @@ def test_bitcoin_address():
|
||||
d.add_data(legacy_address1)
|
||||
|
||||
assert d.get_address() == legacy_address1
|
||||
assert d.get_address_type() == "P2PKH-main"
|
||||
assert d.get_address_type() == (SettingsConstants.LEGACY_P2PKH, SettingsConstants.MAINNET)
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(legacy_address2)
|
||||
|
||||
assert d.get_address() == legacy_address2
|
||||
assert d.get_address_type() == "P2PKH-main"
|
||||
assert d.get_address_type() == (SettingsConstants.LEGACY_P2PKH, SettingsConstants.MAINNET)
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(main_bech32_address)
|
||||
|
||||
assert d.get_address() == main_bech32_address
|
||||
assert d.get_address_type() == "Bech32-main"
|
||||
assert d.get_address_type() == (SettingsConstants.NATIVE_SEGWIT, SettingsConstants.MAINNET)
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(test_bech32_address)
|
||||
|
||||
assert d.get_address() == test_bech32_address
|
||||
assert d.get_address_type() == "Bech32-test"
|
||||
assert d.get_address_type() == (SettingsConstants.NATIVE_SEGWIT, SettingsConstants.TESTNET)
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(main_nested_segwit_address)
|
||||
|
||||
assert d.get_address() == main_nested_segwit_address
|
||||
assert d.get_address_type() == "P2SH-main"
|
||||
assert d.get_address_type() == (SettingsConstants.NESTED_SEGWIT, SettingsConstants.MAINNET)
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(test_nested_segwit_address)
|
||||
|
||||
|
||||
assert d.get_address() == test_nested_segwit_address
|
||||
assert d.get_address_type() == "P2SH-test"
|
||||
|
||||
assert d.get_address_type() == (SettingsConstants.NESTED_SEGWIT, SettingsConstants.TESTNET)
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(main_bech32_address2)
|
||||
|
||||
assert d.get_address() == "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"
|
||||
assert d.get_address_type() == "Bech32-main"
|
||||
|
||||
d = DecodeQR()
|
||||
d.add_data(main_bech32_address3)
|
||||
|
||||
assert d.get_address() == "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"
|
||||
assert d.get_address_type() == "Bech32-main"
|
||||
assert d.get_address_type() == (SettingsConstants.NATIVE_SEGWIT, SettingsConstants.MAINNET)
|
||||
|
||||
|
||||
|
||||
def test_seed_qr():
|
||||
|
||||
+24
-14
File diff suppressed because one or more lines are too long
+5
-4
File diff suppressed because one or more lines are too long
@@ -9,6 +9,7 @@ from seedsigner.models.qr_type import QRType
|
||||
from seedsigner.models.settings import SettingsConstants
|
||||
|
||||
|
||||
|
||||
def run_encode_decode_test(entropy: bytes, mnemonic_length, qr_type):
|
||||
""" Helper method to re-run multiple variations of the same encode/decode test """
|
||||
print(entropy)
|
||||
@@ -16,7 +17,7 @@ def run_encode_decode_test(entropy: bytes, mnemonic_length, qr_type):
|
||||
print(seed_phrase)
|
||||
assert len(seed_phrase) == mnemonic_length
|
||||
|
||||
e = EncodeQR(seed_phrase=seed_phrase, qr_type=qr_type, wordlist=bip39.WORDLIST)
|
||||
e = EncodeQR(seed_phrase=seed_phrase, qr_type=qr_type)
|
||||
data = e.next_part()
|
||||
print(data)
|
||||
|
||||
@@ -28,7 +29,7 @@ def run_encode_decode_test(entropy: bytes, mnemonic_length, qr_type):
|
||||
border=3
|
||||
)
|
||||
|
||||
decoder = DecodeQR(wordlist_language_code=SettingsConstants.LANGUAGE__ENGLISH)
|
||||
decoder = DecodeQR()
|
||||
status = decoder.add_image(image)
|
||||
assert status == DecodeQRStatus.COMPLETE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user