diff --git a/src/seedsigner/gui/screens/screen.py b/src/seedsigner/gui/screens/screen.py index b229857da..a4b3a7422 100644 --- a/src/seedsigner/gui/screens/screen.py +++ b/src/seedsigner/gui/screens/screen.py @@ -10,6 +10,7 @@ from seedsigner.gui.keyboard import Keyboard, TextEntryDisplay from seedsigner.gui.renderer import Renderer from seedsigner.hardware.buttons import HardwareButtonsConstants, HardwareButtons +from seedsigner.models.encode_qr import BaseQrEncoder from seedsigner.models.settings import SettingsConstants from seedsigner.models.threads import BaseThread, ThreadsafeCounter @@ -657,10 +658,10 @@ def swap_selected_button(new_selected_button: int): @dataclass class QRDisplayScreen(BaseScreen): - qr_encoder: 'EncodeQR' = None + qr_encoder: BaseQrEncoder = None class QRDisplayThread(BaseThread): - def __init__(self, qr_encoder: 'EncodeQR', qr_brightness: ThreadsafeCounter, renderer: Renderer, + def __init__(self, qr_encoder: BaseQrEncoder, qr_brightness: ThreadsafeCounter, renderer: Renderer, tips_start_time: ThreadsafeCounter): super().__init__() self.qr_encoder = qr_encoder diff --git a/src/seedsigner/helpers/ur2/fountain_encoder.py b/src/seedsigner/helpers/ur2/fountain_encoder.py index ac43bb62c..af5dd3384 100644 --- a/src/seedsigner/helpers/ur2/fountain_encoder.py +++ b/src/seedsigner/helpers/ur2/fountain_encoder.py @@ -90,6 +90,7 @@ def __init__(self, message, max_fragment_len, first_seq_num = 0, min_fragment_le self.fragment_len = FountainEncoder.find_nominal_fragment_length(self.message_len, min_fragment_len, max_fragment_len) self.fragments = FountainEncoder.partition_message(message, self.fragment_len) self.seq_num = first_seq_num + self.current_part: Part = None @staticmethod def find_nominal_fragment_length(message_len, min_fragment_len, max_fragment_len): @@ -143,7 +144,17 @@ def next_part(self): indexes = choose_fragments(self.seq_num, self.seq_len(), self.checksum) mixed = self.mix(indexes) data = bytes(mixed) - return Part(self.seq_num, self.seq_len(), self.message_len, self.checksum, data) + self.current_part = Part(self.seq_num, self.seq_len(), self.message_len, self.checksum, data) + return self.current_part + + + def restart(self): + """ + Restart from the beginning; each cycle's first n frames are full data frames + (not XOR composites). + """ + self.seq_num = 0 + def mix(self, indexes): result = [0] * self.fragment_len diff --git a/src/seedsigner/helpers/ur2/ur_encoder.py b/src/seedsigner/helpers/ur2/ur_encoder.py index ac1cbc7d0..082ad17b2 100644 --- a/src/seedsigner/helpers/ur2/ur_encoder.py +++ b/src/seedsigner/helpers/ur2/ur_encoder.py @@ -34,12 +34,26 @@ def is_complete(self): def is_single_part(self): return self.fountain_encoder.is_single_part() - def next_part(self): - part = self.fountain_encoder.next_part() + def next_part(self) -> str: if self.is_single_part(): return UREncoder.encode(self.ur) else: + part = self.fountain_encoder.next_part() return UREncoder.encode_part(self.ur.type, part) + + def current_part(self) -> str: + if self.is_single_part(): + return UREncoder.encode(self.ur) + else: + part = self.fountain_encoder.current_part + if not part: + part = self.fountain_encoder.next_part() + return UREncoder.encode_part(self.ur.type, part) + + + def restart(self): + self.fountain_encoder.restart() + @staticmethod def encode_part(type, part): diff --git a/src/seedsigner/models/encode_qr.py b/src/seedsigner/models/encode_qr.py index 6efa715db..c368c05e9 100644 --- a/src/seedsigner/models/encode_qr.py +++ b/src/seedsigner/models/encode_qr.py @@ -2,7 +2,7 @@ from embit import bip32 from embit.networks import NETWORKS -from binascii import b2a_base64, hexlify +from binascii import hexlify from dataclasses import dataclass from typing import List from embit import bip32 @@ -11,7 +11,6 @@ from seedsigner.helpers.ur2.ur_encoder import UREncoder from seedsigner.helpers.ur2.ur import UR from seedsigner.helpers.qr import QR -from seedsigner.models.qr_type import QRType from seedsigner.models.seed import Seed from seedsigner.models.settings import SettingsConstants @@ -20,302 +19,108 @@ - @dataclass -class EncodeQR: - """ - Encode psbt for displaying as qr image - """ - # TODO: Refactor so that this is a base class with implementation classes for each - # QR type. No reason exterior code can't directly instantiate the encoder it needs. - - # Dataclass input vars on __init__() - psbt: PSBT = None - seed_phrase: List[str] = None - passphrase: str = None - derivation: str = None - network: str = SettingsConstants.MAINNET - qr_type: str = None +class BaseQrEncoder: qr_density: str = SettingsConstants.DENSITY__MEDIUM - wordlist_language_code: str = SettingsConstants.WORDLIST_LANGUAGE__ENGLISH - bitcoin_address: str = None - signed_message: str = None + def __post_init__(self): self.qr = QR() - if not self.qr_type: - raise Exception('qr_type is required') - - if self.qr_density == None: - self.qr_density = SettingsConstants.DENSITY__MEDIUM - - self.encoder: BaseQrEncoder = None - - # PSBT formats - if self.qr_type == QRType.PSBT__SPECTER: - self.encoder = SpecterPsbtQrEncoder(psbt=self.psbt, qr_density=self.qr_density) - - elif self.qr_type == QRType.PSBT__UR2: - self.encoder = UrPsbtQrEncoder(psbt=self.psbt, qr_density=self.qr_density) - - # XPUB formats - elif self.qr_type == QRType.XPUB: - self.encoder = XpubQrEncoder( - seed_phrase=self.seed_phrase, - passphrase=self.passphrase, - derivation=self.derivation, - network=self.network, - wordlist_language_code=self.wordlist_language_code - ) - - elif self.qr_type == QRType.XPUB__UR: - self.encoder = UrXpubQrEncoder( - qr_density=self.qr_density, - seed_phrase=self.seed_phrase, - passphrase=self.passphrase, - derivation=self.derivation, - network=self.network, - wordlist_language_code=self.wordlist_language_code - ) - - elif self.qr_type == QRType.XPUB__SPECTER: - self.encoder = SpecterXPubQrEncoder( - qr_density=self.qr_density, - seed_phrase=self.seed_phrase, - passphrase=self.passphrase, - derivation=self.derivation, - network=self.network, - wordlist_language_code=self.wordlist_language_code - ) - - - # SeedQR formats - elif self.qr_type == QRType.SEED__SEEDQR: - self.encoder = SeedQrEncoder(seed_phrase=self.seed_phrase, - wordlist_language_code=self.wordlist_language_code) - - elif self.qr_type == QRType.SEED__COMPACTSEEDQR: - self.encoder = CompactSeedQrEncoder(seed_phrase=self.seed_phrase, - wordlist_language_code=self.wordlist_language_code) - - # Misc formats - elif self.qr_type == QRType.BITCOIN_ADDRESS: - self.encoder = BitcoinAddressEncoder(address=self.bitcoin_address) - - elif self.qr_type == QRType.SIGN_MESSAGE: - self.encoder = SignedMessageEncoder(signed_message=self.signed_message) - - else: - raise Exception('QR Type not supported') - - - def total_parts(self) -> int: - return self.encoder.seq_len() - - - def next_part(self): - return self.encoder.next_part() - - - def part_to_image(self, part, width=240, height=240, border=3): - return self.qr.qrimage_io(part, width, height, border) - - def next_part_image(self, width=240, height=240, border=3, background_color="bdbdbd"): - part = self.next_part() - if self.qr_type == QRType.SEED__SEEDQR: - return self.qr.qrimage(part, width, height, border) - else: - return self.qr.qrimage_io(part, width, height, border, background_color=background_color) - - - # TODO: Make these properties? + @property def is_complete(self): - return self.encoder.is_complete - - - def get_qr_density(self): - return self.qr_density - - - def get_qr_type(self): - return self.qr_type - + raise Exception("Not implemented in child class") + @property + def qr_max_fragment_size(self): + raise Exception("Not implemented in child class") -class BaseQrEncoder: def seq_len(self): raise Exception("Not implemented in child class") def next_part(self) -> str: raise Exception("Not implemented in child class") - - @property - def is_complete(self): + + def cur_part(self) -> str: raise Exception("Not implemented in child class") + + def restart(self): + # only used by animated QR encoders + pass def _create_parts(self): raise Exception("Not implemented in child class") + def part_to_image(self, part, width, height, border: int = 3, background_color: str = "ffffff"): + return self.qr.qrimage_io(part, width, height, border, background_color=background_color) -class BaseStaticQrEncoder(BaseQrEncoder): - def seq_len(self): - return 1 - - - @property - def is_complete(self): - return True - - - -class BasePsbtQrEncoder(BaseQrEncoder): - def __init__(self, psbt: PSBT): - self.psbt = psbt - - - -class UrPsbtQrEncoder(BasePsbtQrEncoder): - def __init__(self, psbt, qr_density): - super().__init__(psbt) - self.qr_max_fragment_size = 20 - - qr_ur_bytes = UR("crypto-psbt", UR_PSBT(self.psbt.serialize()).to_cbor()) - - if qr_density == SettingsConstants.DENSITY__LOW: - self.qr_max_fragment_size = 10 - elif qr_density == SettingsConstants.DENSITY__MEDIUM: - self.qr_max_fragment_size = 30 - elif qr_density == SettingsConstants.DENSITY__HIGH: - self.qr_max_fragment_size = 120 - - self.ur2_encode = UREncoder(ur=qr_ur_bytes, max_fragment_len=self.qr_max_fragment_size) - - - def seq_len(self): - return self.ur2_encode.fountain_encoder.seq_len() - - - def next_part(self) -> str: - return self.ur2_encode.next_part().upper() - - - @property - def is_complete(self): - return self.ur2_encode.is_complete() - - - -class SpecterPsbtQrEncoder(BasePsbtQrEncoder): - def __init__(self, psbt, qr_density): - super().__init__(psbt) - self.qr_max_fragement_size = 65 - self.parts = [] - self.part_num_sent = 0 - self.sent_complete = False - - if qr_density == SettingsConstants.DENSITY__LOW: - self.qr_max_fragement_size = 40 - elif qr_density == SettingsConstants.DENSITY__MEDIUM: - self.qr_max_fragement_size = 65 - elif qr_density == SettingsConstants.DENSITY__HIGH: - self.qr_max_fragement_size = 90 - - self._create_parts() - - - def _create_parts(self): - base64_psbt = b2a_base64(self.psbt.serialize()) - - if base64_psbt[-1:] == b"\n": - base64_psbt = base64_psbt[:-1] - - base64_psbt = base64_psbt.decode('utf-8') - - start = 0 - stop = self.qr_max_fragement_size - qr_cnt = ((len(base64_psbt)-1) // self.qr_max_fragement_size) + 1 - if qr_cnt == 1: - self.parts.append(base64_psbt[start:stop]) + def next_part_image(self, width=240, height=240, border=3, background_color="bdbdbd"): + part = self.next_part() + return self.part_to_image(part, width, height, border, background_color=background_color) - cnt = 0 - while cnt < qr_cnt and qr_cnt != 1: - part = "p" + str(cnt+1) + "of" + str(qr_cnt) + " " + base64_psbt[start:stop] - self.parts.append(part) - start = start + self.qr_max_fragement_size - stop = stop + self.qr_max_fragement_size - if stop > len(base64_psbt): - stop = len(base64_psbt) - cnt += 1 +"""************************************************************************************** + STATIC QR encoders +**************************************************************************************""" +@dataclass +class BaseStaticQrEncoder(BaseQrEncoder): def seq_len(self): - return len(self.parts) - - - def next_part(self) -> str: - # if part num sent is gt number of parts, start at 0 - if self.part_num_sent > (len(self.parts) - 1): - self.part_num_sent = 0 - - part = self.parts[self.part_num_sent] - - # when parts sent eq num of parts in list - if self.part_num_sent == (len(self.parts) - 1): - self.sent_complete = True - - # increment to next part - self.part_num_sent += 1 - - return part + return 1 + + def cur_part(self) -> str: + """ static QRs only have a single part, which `next_part` always returns """ + return self.next_part() @property def is_complete(self): - return self.sent_complete + return True +@dataclass class SeedQrEncoder(BaseStaticQrEncoder): - def __init__(self, seed_phrase: List[str], wordlist_language_code: str): - super().__init__() - self.seed_phrase = seed_phrase - self.wordlist = Seed.get_wordlist(wordlist_language_code) - - if self.wordlist == None: - raise Exception('Wordlist Required') + mnemonic: List[str] = None + wordlist_language_code: str = SettingsConstants.WORDLIST_LANGUAGE__ENGLISH + def __post_init__(self): + self.wordlist = Seed.get_wordlist(self.wordlist_language_code) + super().__post_init__() - def next_part(self): - data = "" + self.data = "" # Output as Numeric data format - for word in self.seed_phrase: + for word in self.mnemonic: index = self.wordlist.index(word) - data += str("%04d" % index) - return data + self.data += str("%04d" % index) + + + def next_part(self): + return self.data +@dataclass class CompactSeedQrEncoder(SeedQrEncoder): def next_part(self): # Output as binary data format binary_str = "" - for word in self.seed_phrase: + for word in self.mnemonic: index = self.wordlist.index(word) # Convert index to binary, strip out '0b' prefix; zero-pad to 11 bits binary_str += bin(index).split('b')[1].zfill(11) # We can exclude the checksum bits at the end - if len(self.seed_phrase) == 24: + if len(self.mnemonic) == 24: # 8 checksum bits in a 24-word seed binary_str = binary_str[:-8] - elif len(self.seed_phrase) == 12: + elif len(self.mnemonic) == 12: # 4 checksum bits in a 12-word seed binary_str = binary_str[:-4] @@ -330,49 +135,36 @@ def next_part(self): -class BitcoinAddressEncoder(BaseStaticQrEncoder): - def __init__(self, address: str): - super().__init__() - self.address = address - +@dataclass +class GenericStaticQrEncoder(BaseStaticQrEncoder): + data: str = None def next_part(self): - return self.address + return self.data -class SignedMessageEncoder(BaseStaticQrEncoder): +@dataclass +class BaseXpubQrEncoder(BaseQrEncoder): """ - Assumes that a signed message will fit in a single-frame QR + Base Xpub QrEncoder for static and animated formats """ - def __init__(self, signed_message: str): - super().__init__() - self.signed_message = signed_message - - - def next_part(self): - return self.signed_message - + mnemonic: list = None + passphrase: str = None + derivation: str = None + network: str = SettingsConstants.MAINNET + wordlist_language_code: str = SettingsConstants.WORDLIST_LANGUAGE__ENGLISH - -class XpubQrEncoder(BaseQrEncoder): - def __init__(self, seed_phrase, passphrase, derivation, network, wordlist_language_code): - self.seed_phrase = seed_phrase - self.passphrase = passphrase - self.derivation = derivation - self.network = network - self.wordlist = Seed.get_wordlist(wordlist_language_code) - self.parts = [] - self.part_num_sent = 0 - self.sent_complete = False + def prep_xpub(self): + self.wordlist = Seed.get_wordlist(self.wordlist_language_code) if self.wordlist == None: raise Exception('Wordlist Required') version = bip32.detect_version(self.derivation, default="xpub", network=NETWORKS[SettingsConstants.map_network_to_embit(self.network)]) - self.seed = Seed(mnemonic=self.seed_phrase, + self.seed = Seed(mnemonic=self.mnemonic, passphrase=self.passphrase, - wordlist_language_code=wordlist_language_code) + wordlist_language_code=self.wordlist_language_code) self.root = bip32.HDKey.from_seed(self.seed.seed_bytes, version=NETWORKS[SettingsConstants.map_network_to_embit(self.network)]["xprv"]) self.fingerprint = self.root.child(0).fingerprint self.xprv = self.root.derive(self.derivation) @@ -385,22 +177,31 @@ def __init__(self, seed_phrase, passphrase, derivation, network, wordlist_langua self.xpub_base58 ) - self._create_parts() - def _create_parts(self): - self.parts = [] - self.parts.append(self.xpubstring) +class StaticXpubQrEncoder(BaseXpubQrEncoder, BaseStaticQrEncoder): + def __post_init__(self): + super().__post_init__() + self.prep_xpub() - def next_part(self) -> str: - if len(self.parts) > 0: - self.sent_complete = True - return self.parts[0] + def next_part(self): + self.prep_xpub() + return self.xpubstring - def seq_len(self): - return len(self.parts) + +"""************************************************************************************** + Simple animated QR encoders +**************************************************************************************""" +@dataclass +class BaseSimpleAnimatedQREncoder(BaseQrEncoder): + def __post_init__(self): + super().__post_init__() + self.parts = [] + self.part_num_sent = 0 + self.sent_complete = False + self._create_parts() @property @@ -408,24 +209,55 @@ def is_complete(self): return self.sent_complete + def seq_len(self): + return len(self.parts) -class SpecterXPubQrEncoder(XpubQrEncoder): - def __init__(self, qr_density, **kwargs): - # Must set up qr_max_fragment_size before calling super().__init__() - self.qr_max_fragment_size = 65 - if qr_density == SettingsConstants.DENSITY__LOW: - self.qr_max_fragment_size = 40 - elif qr_density == SettingsConstants.DENSITY__MEDIUM: - self.qr_max_fragment_size = 65 - elif qr_density == SettingsConstants.DENSITY__HIGH: - self.qr_max_fragment_size = 90 - super().__init__(**kwargs) + def next_part(self) -> str: + # if part num sent is gt number of parts, start at 0 + if self.part_num_sent > (len(self.parts) - 1): + self.part_num_sent = 0 + part = self.parts[self.part_num_sent] - def _create_parts(self): - self.parts = [] + # when parts sent eq num of parts in list + if self.part_num_sent == (len(self.parts) - 1): + self.sent_complete = True + + # increment to next part + self.part_num_sent += 1 + + return part + + def cur_part(self) -> str: + if self.part_num_sent == 0: + # Rewind all the way back to the end + self.part_num_sent = len(self.parts) - 1 + else: + self.part_num_sent -= 1 + return self.next_part() + + + def restart(self) -> str: + self.part_num_sent = 0 + + + +@dataclass +class SpecterXPubQrEncoder(BaseSimpleAnimatedQREncoder, BaseXpubQrEncoder): + @property + def qr_max_fragment_size(self): + density_mapping = { + SettingsConstants.DENSITY__LOW: 40, + SettingsConstants.DENSITY__MEDIUM: 65, + SettingsConstants.DENSITY__HIGH: 90, + } + return density_mapping.get(self.qr_density, 65) + + + def _create_parts(self): + self.prep_xpub() start = 0 stop = self.qr_max_fragment_size qr_cnt = ((len(self.xpubstring)-1) // self.qr_max_fragment_size) + 1 @@ -445,34 +277,60 @@ def _create_parts(self): cnt += 1 - def next_part(self) -> str: - # if part num sent is gt number of parts, start at 0 - if self.part_num_sent > (len(self.parts) - 1): - self.part_num_sent = 0 - part = self.parts[self.part_num_sent] +"""************************************************************************************** + Fountain encoded animated QR encoders +**************************************************************************************""" +@dataclass +class BaseFountainQrEncoder(BaseQrEncoder): + def __post_init__(self): + super().__post_init__() - # when parts sent eq num of parts in list - if self.part_num_sent == (len(self.parts) - 1): - self.sent_complete = True + self.ur2_encode: UREncoder = None - # increment to next part - self.part_num_sent += 1 - return part + @property + def is_complete(self): + return self.ur2_encode.is_complete() + @property + def qr_max_fragment_size(self): + density_mapping = { + SettingsConstants.DENSITY__LOW: 10, + SettingsConstants.DENSITY__MEDIUM: 30, + SettingsConstants.DENSITY__HIGH: 120, + } + return density_mapping.get(self.qr_density, 30) -class UrXpubQrEncoder(XpubQrEncoder): - def __init__(self, qr_density, **kwargs): - super().__init__(**kwargs) - - if qr_density == SettingsConstants.DENSITY__LOW: - self.qr_max_fragment_size = 10 - elif qr_density == SettingsConstants.DENSITY__MEDIUM: - self.qr_max_fragment_size = 30 - elif qr_density == SettingsConstants.DENSITY__HIGH: - self.qr_max_fragment_size = 120 + + def _create_parts(self): + """ parts are dynamically generated by the fountain encoder """ + pass + + + def seq_len(self): + return self.ur2_encode.fountain_encoder.seq_len() + + + def next_part(self) -> str: + return self.ur2_encode.next_part().upper() + + + def cur_part(self) -> str: + return self.ur2_encode.current_part().upper() + + + def restart(self): + self.ur2_encode.fountain_encoder.restart() + + + +@dataclass +class UrXpubQrEncoder(BaseFountainQrEncoder, BaseXpubQrEncoder): + def __post_init__(self): + super().__post_init__() + self.prep_xpub() def derivation_to_keypath(path: str) -> list: arr = path.split("/") @@ -530,9 +388,12 @@ def derivation_to_keypath(path: str) -> list: self.ur2_encode = UREncoder(ur=qr_ur_bytes, max_fragment_len=self.qr_max_fragment_size) - def seq_len(self): - return self.ur2_encode.fountain_encoder.seq_len() +@dataclass +class UrPsbtQrEncoder(BaseFountainQrEncoder): + psbt: PSBT = None - def next_part(self) -> str: - return self.ur2_encode.next_part().upper() + def __post_init__(self): + super().__post_init__() + qr_ur_bytes = UR("crypto-psbt", UR_PSBT(self.psbt.serialize()).to_cbor()) + self.ur2_encode = UREncoder(ur=qr_ur_bytes, max_fragment_len=self.qr_max_fragment_size) diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py index 9c0d9c60f..69e12073a 100644 --- a/src/seedsigner/views/psbt_views.py +++ b/src/seedsigner/views/psbt_views.py @@ -4,9 +4,8 @@ from seedsigner.controller import Controller from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants -from seedsigner.models.encode_qr import EncodeQR +from seedsigner.models.encode_qr import UrPsbtQrEncoder from seedsigner.models.psbt_parser import PSBTParser -from seedsigner.models.qr_type import QRType from seedsigner.models.settings import SettingsConstants from seedsigner.gui.screens.psbt_screens import PSBTOverviewScreen, PSBTMathScreen, PSBTAddressDetailsScreen, PSBTChangeDetailsScreen, PSBTFinalizeScreen from seedsigner.gui.screens.screen import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen, DireWarningScreen, QRDisplayScreen) @@ -498,11 +497,9 @@ def run(self): class PSBTSignedQRDisplayView(View): def run(self): - qr_encoder = EncodeQR( + qr_encoder = UrPsbtQrEncoder( psbt=self.controller.psbt, - qr_type=QRType.PSBT__UR2, # All coordinators (as of 2022-08) use this format qr_density=self.settings.get_value(SettingsConstants.SETTING__QR_DENSITY), - wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE), ) self.run_screen(QRDisplayScreen, qr_encoder=qr_encoder) diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index afd70e10d..0df0889e3 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -16,7 +16,7 @@ from seedsigner.gui.screens.screen import LargeIconStatusScreen, QRDisplayScreen from seedsigner.helpers import embit_utils from seedsigner.models.decode_qr import DecodeQR -from seedsigner.models.encode_qr import EncodeQR +from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder from seedsigner.models.psbt_parser import PSBTParser from seedsigner.models.qr_type import QRType from seedsigner.models.seed import InvalidSeedException, Seed @@ -718,20 +718,25 @@ def run(self): args["coordinator"] = self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)[0] return Destination(SeedExportXpubWarningView, view_args=args, skip_current_view=True) + button_data = self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS) + selected_menu_num = self.run_screen( ButtonListScreen, title="Export Xpub", is_button_text_centered=False, - button_data=self.settings.get_multiselect_value_display_names(SettingsConstants.SETTING__COORDINATORS), + button_data=button_data, ) - if selected_menu_num < len(self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)): - args["coordinator"] = self.settings.get_value(SettingsConstants.SETTING__COORDINATORS)[selected_menu_num] - return Destination(SeedExportXpubWarningView, view_args=args) - - elif selected_menu_num == RET_CODE__BACK_BUTTON: + if selected_menu_num == RET_CODE__BACK_BUTTON: return Destination(BackStackView) + coordinators_settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__COORDINATORS) + selected_display_name = button_data[selected_menu_num] + args["coordinator"] = coordinators_settings_entry.get_selection_option_value_by_display_name(selected_display_name) + + return Destination(SeedExportXpubWarningView, view_args=args) + + class SeedExportXpubWarningView(View): @@ -858,35 +863,25 @@ def __init__(self, seed_num: int, coordinator: str, derivation_path: str): super().__init__() self.seed = self.controller.get_seed(seed_num) - qr_density = self.settings.get_value(SettingsConstants.SETTING__QR_DENSITY) - if coordinator == SettingsConstants.COORDINATOR__SPECTER_DESKTOP: - qr_type = QRType.XPUB__SPECTER - - elif coordinator == SettingsConstants.COORDINATOR__BLUE_WALLET: - qr_type = QRType.XPUB - - elif coordinator == SettingsConstants.COORDINATOR__KEEPER: - qr_type = QRType.XPUB - - elif coordinator == SettingsConstants.COORDINATOR__NUNCHUK: - qr_type = QRType.XPUB__UR - - # As of 2022-03-02 Nunchuk doesn't seem to support animated QRs for Xpub import - qr_density = SettingsConstants.DENSITY__HIGH - - else: - qr_type = QRType.XPUB__UR - - self.qr_encoder = EncodeQR( - seed_phrase=self.seed.mnemonic_list, + encoder_args = dict( + mnemonic=self.seed.mnemonic_list, passphrase=self.seed.passphrase, derivation=derivation_path, network=self.settings.get_value(SettingsConstants.SETTING__NETWORK), - qr_type=qr_type, - qr_density=qr_density, - wordlist_language_code=self.seed.wordlist_language_code + qr_density=self.settings.get_value(SettingsConstants.SETTING__QR_DENSITY) ) + if coordinator == SettingsConstants.COORDINATOR__SPECTER_DESKTOP: + self.qr_encoder = SpecterXPubQrEncoder(**encoder_args) + + elif coordinator in [SettingsConstants.COORDINATOR__BLUE_WALLET, + SettingsConstants.COORDINATOR__KEEPER, + SettingsConstants.COORDINATOR__NUNCHUK]: + self.qr_encoder = StaticXpubQrEncoder(**encoder_args) + + else: + self.qr_encoder = UrXpubQrEncoder(**encoder_args) + def run(self): self.run_screen( @@ -1386,11 +1381,13 @@ def __init__(self, seed_num: int, seedqr_format: str, num_modules: int): def run(self): - e = EncodeQR( - seed_phrase=self.seed.mnemonic_list, - qr_type=self.seedqr_format, - wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE) - ) + encoder_args = dict(mnemonic=self.seed.mnemonic_list, + wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)) + if self.seedqr_format == QRType.SEED__SEEDQR: + e = SeedQrEncoder(**encoder_args) + elif self.seedqr_format == QRType.SEED__COMPACTSEEDQR: + e = CompactSeedQrEncoder(**encoder_args) + data = e.next_part() ret = seed_screens.SeedTranscribeSeedQRWholeQRScreen( @@ -1421,11 +1418,13 @@ def __init__(self, seed_num: int, seedqr_format: str): def run(self): - e = EncodeQR( - seed_phrase=self.seed.mnemonic_list, - qr_type=self.seedqr_format, - wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE) - ) + encoder_args = dict(mnemonic=self.seed.mnemonic_list, + wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)) + if self.seedqr_format == QRType.SEED__SEEDQR: + e = SeedQrEncoder(**encoder_args) + elif self.seedqr_format == QRType.SEED__COMPACTSEEDQR: + e = CompactSeedQrEncoder(**encoder_args) + data = e.next_part() if len(self.seed.mnemonic_list) == 24: @@ -2066,7 +2065,7 @@ def __init__(self): def run(self): - qr_encoder = EncodeQR(qr_type=QRType.SIGN_MESSAGE, signed_message=self.signed_message) + qr_encoder = GenericStaticQrEncoder(data=self.signed_message) self.run_screen( QRDisplayScreen, diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py index 12a4493b3..162c874ae 100644 --- a/src/seedsigner/views/tools_views.py +++ b/src/seedsigner/views/tools_views.py @@ -14,8 +14,7 @@ ToolsCalcFinalWordScreen, ToolsCoinFlipEntryScreen, ToolsDiceEntropyEntryScreen, ToolsImageEntropyFinalImageScreen, ToolsImageEntropyLivePreviewScreen, ToolsAddressExplorerAddressTypeScreen) from seedsigner.helpers import embit_utils, mnemonic_generation -from seedsigner.models.encode_qr import EncodeQR -from seedsigner.models.qr_type import QRType +from seedsigner.models.encode_qr import GenericStaticQrEncoder from seedsigner.models.seed import Seed from seedsigner.models.settings_definition import SettingsConstants from seedsigner.views.seed_views import SeedDiscardView, SeedFinalizeView, SeedMnemonicEntryView, SeedOptionsView, SeedWordsWarningView, SeedExportXpubScriptTypeView @@ -695,7 +694,7 @@ def __init__(self, index: int, address: str, is_change: bool, start_index: int, def run(self): from seedsigner.gui.screens.screen import QRDisplayScreen - qr_encoder = EncodeQR(qr_type=QRType.BITCOIN_ADDRESS, bitcoin_address=self.address) + qr_encoder = GenericStaticQrEncoder(data=self.address) self.run_screen( QRDisplayScreen, qr_encoder=qr_encoder, diff --git a/tests/test_encodepsbtqr.py b/tests/test_encodepsbtqr.py index 75ed796fb..8a8ec3688 100644 --- a/tests/test_encodepsbtqr.py +++ b/tests/test_encodepsbtqr.py @@ -1,5 +1,4 @@ -from seedsigner.models.encode_qr import EncodeQR -from seedsigner.models.qr_type import QRType +from seedsigner.models.encode_qr import CompactSeedQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrPsbtQrEncoder, UrXpubQrEncoder from embit import psbt from binascii import a2b_base64 @@ -7,75 +6,47 @@ -def test_ur_qr_encode(): +def test_ur_psbt_qr_encode(): base64_psbt = "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" tx = psbt.PSBT.parse(a2b_base64(base64_psbt)) - e = EncodeQR(psbt=tx, qr_type=QRType.PSBT__UR2) + e = UrPsbtQrEncoder(psbt=tx, qr_density=SettingsConstants.DENSITY__MEDIUM) cnt = 0 while cnt <= 10: fragment = e.next_part() - img = e.part_to_image(fragment) - cnt += 1 - - - -def test_specter_qr_encode(): - base64_psbt = "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" - - tx = psbt.PSBT.parse(a2b_base64(base64_psbt)) - - e = EncodeQR(psbt=tx, qr_type=QRType.PSBT__SPECTER) - - cnt = 0 - while cnt <= 10: - fragment = e.next_part() - if (cnt+1) == 1: - assert fragment == "p1of117 cHNidP8BAIkCAAAAAaLlQ/VRNpx3IFtoRTOCnq2xfJwg/n7R9XB0TTTnlX/UHQAAA" - elif (cnt+1) == 2: - assert fragment == "p2of117 AD9////AtzQAwAAAAAAIgAgCwVSg4Ae1lGNHzy76jLN6GSQaSVnktnmNDByu/wkn7" - elif (cnt+1) == 3: - assert fragment == "p3of117 FQwwAAAAAAACIAIJyFZJe7xxQjXpoEBhb8mIkau9OhobDS7xbYxnRIjJUSAAAAAE8" - elif (cnt+1) == 4: - assert fragment == "p4of117 BBIiyHgQFgrfagAAAAqP8rWjHFRBmTEWK39AFjd6Wo1sw1UxlgIvROVHUOHbiAzre" - elif (cnt+1) == 5: - assert fragment == "p5of117 +t61zOqKFV1xXtDPuUcQRh3M92zh0Zar8rDLPJKQFH7fnFkwAACAAAAAgAAAAIACA" - elif (cnt+1) == 6: - assert fragment == "p6of117 ACATwEEiLIeBFIg7+eAAAACubwMfJNby3zfn9owhFfgl/Xe/GiHciMMxxB9v6q7BW" - elif (cnt+1) == 7: - assert fragment == "p7of117 cCurV9rH+K8ucVU3w52mcEttDldz7kh5cS0xBtWs7wmTYU4IEbazAAAIAAAACAAAA" - elif (cnt+1) == 8: - assert fragment == "p8of117 AgAIAAIBPAQSIsh4EX+8GLoAAAALvSlncnGchVCfK7tnzHPVYcBRcck0JGQuspGFp" - elif (cnt+1) == 9: - assert fragment == "p9of117 cGP+YQIAXYODa8PIF3hOOnUeYHhlv4PQ+UZCYynQCOoKgVJRhhQYTQfrMAAAgAAAA" - elif (cnt+1) == 10: - assert fragment == "p10of117 IAAAACAAgAAgE8BBIiyHgRgkAVVgAAAAsgLKl/ahhLHvS/3Cth+9Hde12MHJO5PP8" - elif (cnt+1) == 11: - assert fragment == "p11of117 REKtbWkqONAvETqIlMPWJ/f1uBvSCGFm+zzDYnnEBtuAYjZiQrzj9mFLQz4JUwAAC" - - img = e.part_to_image(fragment) + e.part_to_image(fragment, 240, 240) cnt += 1 def test_seedsigner_qr(): - mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash" + # test vector 1 from the SeedQR docs + mnemonic = "attack pizza motion avocado network gather crop fresh patrol unusual wild holiday candy pony ranch winter theme error hybrid van cereal salon goddess expire".split() + e = SeedQrEncoder(mnemonic=mnemonic) + assert e.next_part() == "011513251154012711900771041507421289190620080870026613431420201617920614089619290300152408010643" - e = EncodeQR(seed_phrase=mnemonic.split(), qr_type=QRType.SEED__SEEDQR) + e = CompactSeedQrEncoder(mnemonic=mnemonic) + assert e.next_part() == b'\x0et\xb6A\x07\xf9L\xc0\xcc\xfa\xe6\xa1=\xcb\xec6b\x15O\xecg\xe0\xe0\t\x99\xc0x\x92Y}\x19\n' - print(e.next_part()) + # test vector 4 from the SeedQR docs + mnemonic="forum undo fragile fade shy sign arrest garment culture tube off merit".split() + e = SeedQrEncoder(mnemonic=mnemonic) + assert e.next_part() == "073318950739065415961602009907670428187212261116" - assert e.next_part() == "121802020768124106400009195602431595117715840445" + e = CompactSeedQrEncoder(mnemonic=mnemonic) + assert e.next_part() == b'[\xbd\x9dq\xa8\xecy\x90\x83\x1a\xff5\x9dBeE' def test_xpub_qr(): mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash" - e = EncodeQR(seed_phrase=mnemonic.split(), passphrase="pass", qr_type=QRType.XPUB, network=SettingsConstants.TESTNET, derivation="m/48h/1h/0h/2h") - + e = StaticXpubQrEncoder(mnemonic=mnemonic.split(), + passphrase="pass", + derivation="m/48h/1h/0h/2h", + network=SettingsConstants.TESTNET) assert e.next_part() == "[c49122a5/48h/1h/0h/2h]Vpub5mXgECaX5yYDNc5VnUG4jVNptyEg65qUjuofWchQeuMWWiq8rcPBoMxfrVggXj5NJmaNEToWpax8GMMucozvAdqf1bW1JsZsfdBzsK3VUC5" @@ -83,7 +54,7 @@ def test_xpub_qr(): def test_specter_xpub_qr(): mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash" - e = EncodeQR(seed_phrase=mnemonic.split(" "), passphrase="pass", qr_type=QRType.XPUB__SPECTER, network=SettingsConstants.TESTNET, derivation="m/48h/1h/0h/2h", qr_density=SettingsConstants.DENSITY__LOW) + e = SpecterXPubQrEncoder(mnemonic=mnemonic.split(" "), passphrase="pass", network=SettingsConstants.TESTNET, derivation="m/48h/1h/0h/2h", qr_density=SettingsConstants.DENSITY__LOW) assert e.next_part() == "p1of4 [c49122a5/48h/1h/0h/2h]Vpub5mXgECaX5yYDN" assert e.next_part() == "p2of4 c5VnUG4jVNptyEg65qUjuofWchQeuMWWiq8rcPBo" @@ -95,10 +66,9 @@ def test_specter_xpub_qr(): def test_ur_xpub_qr(): mnemonic = "obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash" - e = EncodeQR( - seed_phrase=mnemonic.split(), + e = UrXpubQrEncoder( + mnemonic=mnemonic.split(), passphrase="pass", - qr_type=QRType.XPUB__UR, network=SettingsConstants.TESTNET, derivation="m/48h/1h/0h/2h", qr_density=SettingsConstants.DENSITY__MEDIUM diff --git a/tests/test_flows_psbt.py b/tests/test_flows_psbt.py index 02b714463..93d4847d8 100644 --- a/tests/test_flows_psbt.py +++ b/tests/test_flows_psbt.py @@ -1,6 +1,5 @@ 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 diff --git a/tests/test_seedqr.py b/tests/test_seedqr.py index 7e2d63856..826cd9415 100644 --- a/tests/test_seedqr.py +++ b/tests/test_seedqr.py @@ -1,23 +1,22 @@ import os -import pyzbar from embit import bip39 from seedsigner.helpers.qr import QR -from seedsigner.helpers.ur2.bytewords import decode from seedsigner.models.decode_qr import DecodeQR, DecodeQRStatus -from seedsigner.models.encode_qr import EncodeQR +from seedsigner.models.encode_qr import SeedQrEncoder, CompactSeedQrEncoder 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) - seed_phrase = bip39.mnemonic_from_bytes(entropy).split() - print(seed_phrase) - assert len(seed_phrase) == mnemonic_length + mnemonic = bip39.mnemonic_from_bytes(entropy).split() + assert len(mnemonic) == mnemonic_length + + if qr_type == QRType.SEED__SEEDQR: + e = SeedQrEncoder(mnemonic=mnemonic) + elif qr_type == QRType.SEED__COMPACTSEEDQR: + e = CompactSeedQrEncoder(mnemonic=mnemonic) - e = EncodeQR(seed_phrase=seed_phrase, qr_type=qr_type) data = e.next_part() print(data) @@ -34,8 +33,7 @@ def run_encode_decode_test(entropy: bytes, mnemonic_length, qr_type): assert status == DecodeQRStatus.COMPLETE decoded_seed_phrase = decoder.get_seed_phrase() - print(decoded_seed_phrase) - assert seed_phrase == decoded_seed_phrase + assert mnemonic == decoded_seed_phrase