mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
Merge branch 'dev' into final_word_v2
This commit is contained in:
@@ -142,6 +142,14 @@ The Orange Pill enclosure design offers a more finished look that includes butto
|
||||
|
||||
The upper and lower portions of the enclosure can be printed using a standard FDM 3D printer, no supports necessary. The buttons and joystick nub should ideally be produced with a SLA/resin printer. An overview of the entire assembly process can be found [here](https://youtu.be/aIIc2DiZYcI). To access the design files and printable models, click [here](https://github.com/SeedSigner/seedsigner/tree/main/enclosures/orange_pill).
|
||||
|
||||
### Community Designs
|
||||
|
||||
* [Lil Pill](https://cults3d.com/en/3d-model/gadget/lil-pill-seedsigner-case) by @_CyberNomad
|
||||
* [OrangeSurf Case](https://github.com/orangesurf/orangesurf-seedsigner-case) by @OrangeSurfBTC
|
||||
* [PS4 Seedsigner](https://www.thingiverse.com/thing:5363525) by @Silexperience
|
||||
* [OpenPill Faceplate](https://www.printables.com/en/model/179924-seedsigner-open-pill-cover-plates-digital-cross-jo) by @Revetuzo
|
||||
* [Waveshare CoverPlate](https://cults3d.com/en/3d-model/various/seedsigner-coverplate-for-waveshare-1-3-inch-lcd-hat-with-240x240-pixel-display) by @Adathome1
|
||||
|
||||
---------------
|
||||
|
||||
# SeedQR Printable Templates
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,4 +1,4 @@
|
||||
embit==0.4.10
|
||||
embit @ git+https://github.com/seedsigner/embit.git@03cba13e0add64b241972c67e2aa970cb2e3a5d2
|
||||
numpy==1.21.1
|
||||
picamera==1.13
|
||||
Pillow==8.2.0
|
||||
|
||||
@@ -118,8 +118,12 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
# max_destination_col_width += curve_width
|
||||
|
||||
# Now let's maximize the actual destination col by adjusting our addr truncation
|
||||
def calculate_destination_col_width(truncate_at: int):
|
||||
def calculate_destination_col_width(truncate_at: int = 0):
|
||||
def truncate_destination_addr(addr):
|
||||
# TODO: Properly handle the ellipsis truncation in different languages
|
||||
if len(addr) <= truncate_at + len("..."):
|
||||
# No point in truncating
|
||||
return addr
|
||||
return f"{addr[:truncate_at]}..."
|
||||
|
||||
destination_column = []
|
||||
@@ -129,7 +133,7 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
destination_column.append(truncate_destination_addr(addr))
|
||||
|
||||
for i in range(0, self.num_self_transfer_outputs):
|
||||
destination_column.append(f"self-transfer")
|
||||
destination_column.append(truncate_destination_addr("self-transfer"))
|
||||
else:
|
||||
# destination_column.append(f"{len(self.destination_addresses)} recipients")
|
||||
destination_column.append(f"recipient 1")
|
||||
@@ -151,10 +155,10 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
|
||||
if len(self.destination_addresses) + self.num_self_transfer_outputs > 3:
|
||||
# We're not going to display any destination addrs so truncation doesn't matter
|
||||
(destination_text_width, destination_column) = calculate_destination_col_width(truncate_at=0)
|
||||
(destination_text_width, destination_column) = calculate_destination_col_width()
|
||||
else:
|
||||
# Steadliy widen out the destination column until we run out of space
|
||||
for i in range(6, 13):
|
||||
for i in range(6, 14):
|
||||
(new_width, new_col_text) = calculate_destination_col_width(truncate_at=i)
|
||||
if new_width > max_destination_col_width:
|
||||
break
|
||||
|
||||
@@ -451,7 +451,7 @@ class SeedOptionsScreen(ButtonListScreen):
|
||||
|
||||
@dataclass
|
||||
class SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
|
||||
seed: Seed = None
|
||||
words: List[str] = None
|
||||
page_index: int = 0
|
||||
num_pages: int = 3
|
||||
is_bottom_list: bool = True
|
||||
@@ -459,15 +459,9 @@ class SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
self.title = f"Seed Words: {self.page_index+1}/{self.num_pages}"
|
||||
super().__post_init__()
|
||||
|
||||
# Can only render 4 words per screen
|
||||
words_per_page = 4
|
||||
mnemonic = self.seed.mnemonic_display_list
|
||||
|
||||
# Slice the mnemonic to our current 4-word section
|
||||
self.mnemonic = mnemonic[self.page_index*words_per_page:(self.page_index + 1)*words_per_page]
|
||||
words_per_page = len(self.words)
|
||||
|
||||
self.body_x = 0
|
||||
self.body_y = self.top_nav.height - int(GUIConstants.COMPONENT_PADDING / 2)
|
||||
@@ -479,7 +473,7 @@ class SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
|
||||
|
||||
# Calc horizontal center based on longest word
|
||||
max_word_width = 0
|
||||
for word in self.mnemonic:
|
||||
for word in self.words:
|
||||
(left, top, right, bottom) = font.getbbox(word, anchor="ls")
|
||||
if right > max_word_width:
|
||||
max_word_width = right
|
||||
@@ -503,7 +497,7 @@ class SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
|
||||
)
|
||||
draw = ImageDraw.Draw(self.body_img)
|
||||
|
||||
for index, word in enumerate(self.mnemonic):
|
||||
for index, word in enumerate(self.words):
|
||||
draw.rounded_rectangle(
|
||||
(number_box_x, number_box_y, number_box_x + number_box_width, number_box_y + number_box_height),
|
||||
fill="#202020",
|
||||
|
||||
@@ -32,7 +32,8 @@ class PSBTSelectSeedView(View):
|
||||
seeds = self.controller.storage.seeds
|
||||
|
||||
SCAN_SEED = ("Scan a seed", FontAwesomeIconConstants.QRCODE)
|
||||
ENTER_WORDS = "Enter 12/24 words"
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
button_data = []
|
||||
for seed in seeds:
|
||||
button_str = seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
|
||||
@@ -45,7 +46,8 @@ class PSBTSelectSeedView(View):
|
||||
pass
|
||||
button_data.append((button_str, SeedSignerCustomIconConstants.FINGERPRINT, "blue"))
|
||||
button_data.append(SCAN_SEED)
|
||||
button_data.append(ENTER_WORDS)
|
||||
button_data.append(TYPE_12WORD)
|
||||
button_data.append(TYPE_24WORD)
|
||||
|
||||
selected_menu_num = ButtonListScreen(
|
||||
title="Select Signer",
|
||||
@@ -68,8 +70,12 @@ class PSBTSelectSeedView(View):
|
||||
from seedsigner.views.scan_views import ScanView
|
||||
return Destination(ScanView)
|
||||
|
||||
elif button_data[selected_menu_num] == ENTER_WORDS:
|
||||
elif button_data[selected_menu_num] in [TYPE_12WORD, TYPE_24WORD]:
|
||||
from seedsigner.views.seed_views import SeedMnemonicEntryView
|
||||
if button_data[selected_menu_num] == TYPE_12WORD:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=12)
|
||||
else:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=24)
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
|
||||
@@ -78,18 +84,22 @@ class PSBTOverviewView(View):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# The PSBTParser takes a while to read the PSBT. Run the loading screen while we
|
||||
# wait.
|
||||
self.loading_screen = LoadingScreenThread(text="Parsing PSBT...")
|
||||
self.loading_screen.start()
|
||||
self.loading_screen = None
|
||||
|
||||
if not self.controller.psbt_parser or self.controller.psbt_parser.seed != self.controller.psbt_seed:
|
||||
# Must run the PSBTParser or re-parse
|
||||
self.controller.psbt_parser = PSBTParser(
|
||||
self.controller.psbt,
|
||||
seed=self.controller.psbt_seed,
|
||||
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)
|
||||
)
|
||||
# The PSBTParser takes a while to read the PSBT. Run the loading screen while
|
||||
# we wait.
|
||||
self.loading_screen = LoadingScreenThread(text="Parsing PSBT...")
|
||||
self.loading_screen.start()
|
||||
try:
|
||||
self.controller.psbt_parser = PSBTParser(
|
||||
self.controller.psbt,
|
||||
seed=self.controller.psbt_seed,
|
||||
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)
|
||||
)
|
||||
except Exception as e:
|
||||
self.loading_screen.stop()
|
||||
raise e
|
||||
|
||||
|
||||
def run(self):
|
||||
@@ -126,7 +136,8 @@ class PSBTOverviewView(View):
|
||||
)
|
||||
|
||||
# Everything is set. Stop the loading screen
|
||||
self.loading_screen.stop()
|
||||
if self.loading_screen:
|
||||
self.loading_screen.stop()
|
||||
|
||||
# Run the overview screen
|
||||
selected_menu_num = screen.display()
|
||||
@@ -145,6 +156,8 @@ class PSBTOverviewView(View):
|
||||
else:
|
||||
return Destination(PSBTMathView)
|
||||
|
||||
|
||||
|
||||
class PSBTUnsupportedScriptTypeWarningView(View):
|
||||
def run(self):
|
||||
selected_menu_num = WarningScreen(
|
||||
@@ -462,11 +475,12 @@ class PSBTFinalizeView(View):
|
||||
loading_screen = LoadingScreenThread(text="Signing PSBT...")
|
||||
loading_screen.start()
|
||||
|
||||
sig_cnt = PSBTParser.sig_count(psbt)
|
||||
psbt.sign_with(psbt_parser.root)
|
||||
trimmed_psbt = PSBTParser.trim(psbt)
|
||||
|
||||
loading_screen.stop()
|
||||
try:
|
||||
sig_cnt = PSBTParser.sig_count(psbt)
|
||||
psbt.sign_with(psbt_parser.root)
|
||||
trimmed_psbt = PSBTParser.trim(psbt)
|
||||
finally:
|
||||
loading_screen.stop()
|
||||
|
||||
if sig_cnt == PSBTParser.sig_count(trimmed_psbt):
|
||||
# Signing failed / didn't do anything
|
||||
|
||||
@@ -73,13 +73,13 @@ class SeedsMenuView(View):
|
||||
class LoadSeedView(View):
|
||||
def run(self):
|
||||
SEED_QR = (" Scan a SeedQR", FontAwesomeIconConstants.QRCODE)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
CREATE = (" Create a seed", FontAwesomeIconConstants.PLUS)
|
||||
button_data=[
|
||||
SEED_QR,
|
||||
TYPE_24WORD,
|
||||
TYPE_12WORD,
|
||||
TYPE_24WORD,
|
||||
CREATE,
|
||||
]
|
||||
|
||||
@@ -96,14 +96,14 @@ class LoadSeedView(View):
|
||||
from .scan_views import ScanView
|
||||
return Destination(ScanView)
|
||||
|
||||
elif button_data[selected_menu_num] == TYPE_24WORD:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=24)
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
elif button_data[selected_menu_num] == TYPE_12WORD:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=12)
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
elif button_data[selected_menu_num] == TYPE_24WORD:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=24)
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
elif button_data[selected_menu_num] == CREATE:
|
||||
from .tools_views import ToolsMenuView
|
||||
return Destination(ToolsMenuView)
|
||||
@@ -647,46 +647,50 @@ class SeedExportXpubDetailsView(View):
|
||||
self.loading_screen = LoadingScreenThread(text="Generating xpub...")
|
||||
self.loading_screen.start()
|
||||
|
||||
if self.script_type == SettingsConstants.CUSTOM_DERIVATION:
|
||||
derivation_path = self.custom_derivation
|
||||
else:
|
||||
derivation_path = PSBTParser.calc_derivation(
|
||||
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
|
||||
wallet_type=self.sig_type,
|
||||
script_type=self.script_type
|
||||
)
|
||||
try:
|
||||
if self.script_type == SettingsConstants.CUSTOM_DERIVATION:
|
||||
derivation_path = self.custom_derivation
|
||||
else:
|
||||
derivation_path = PSBTParser.calc_derivation(
|
||||
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
|
||||
wallet_type=self.sig_type,
|
||||
script_type=self.script_type
|
||||
)
|
||||
|
||||
if self.settings.get_value(SettingsConstants.SETTING__XPUB_DETAILS) == SettingsConstants.OPTION__ENABLED:
|
||||
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(self.settings.get_value(SettingsConstants.SETTING__NETWORK))]
|
||||
version = embit.bip32.detect_version(
|
||||
derivation_path,
|
||||
default="xpub",
|
||||
network=embit_network
|
||||
)
|
||||
if self.settings.get_value(SettingsConstants.SETTING__XPUB_DETAILS) == SettingsConstants.OPTION__ENABLED:
|
||||
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(self.settings.get_value(SettingsConstants.SETTING__NETWORK))]
|
||||
version = embit.bip32.detect_version(
|
||||
derivation_path,
|
||||
default="xpub",
|
||||
network=embit_network
|
||||
)
|
||||
|
||||
root = embit.bip32.HDKey.from_seed(
|
||||
self.seed.seed_bytes,
|
||||
version=embit_network["xprv"]
|
||||
)
|
||||
root = embit.bip32.HDKey.from_seed(
|
||||
self.seed.seed_bytes,
|
||||
version=embit_network["xprv"]
|
||||
)
|
||||
|
||||
fingerprint = hexlify(root.child(0).fingerprint).decode('utf-8')
|
||||
xprv = root.derive(derivation_path)
|
||||
xpub = xprv.to_public()
|
||||
xpub_base58 = xpub.to_string(version=version)
|
||||
fingerprint = hexlify(root.child(0).fingerprint).decode('utf-8')
|
||||
xprv = root.derive(derivation_path)
|
||||
xpub = xprv.to_public()
|
||||
xpub_base58 = xpub.to_string(version=version)
|
||||
|
||||
screen = seed_screens.SeedExportXpubDetailsScreen(
|
||||
fingerprint=fingerprint,
|
||||
has_passphrase=self.seed.passphrase is not None,
|
||||
derivation_path=derivation_path,
|
||||
xpub=xpub_base58,
|
||||
)
|
||||
screen = seed_screens.SeedExportXpubDetailsScreen(
|
||||
fingerprint=fingerprint,
|
||||
has_passphrase=self.seed.passphrase is not None,
|
||||
derivation_path=derivation_path,
|
||||
xpub=xpub_base58,
|
||||
)
|
||||
|
||||
self.loading_screen.stop()
|
||||
selected_menu_num = screen.display()
|
||||
|
||||
else:
|
||||
selected_menu_num = 0
|
||||
|
||||
finally:
|
||||
self.loading_screen.stop()
|
||||
|
||||
selected_menu_num = screen.display()
|
||||
else:
|
||||
self.loading_screen.stop()
|
||||
selected_menu_num = 0
|
||||
|
||||
if selected_menu_num == 0:
|
||||
return Destination(
|
||||
@@ -792,6 +796,12 @@ class SeedWordsView(View):
|
||||
NEXT = "Next"
|
||||
DONE = "Done"
|
||||
|
||||
# Slice the mnemonic to our current 4-word section
|
||||
words_per_page = 4 # TODO: eventually make this configurable for bigger screens?
|
||||
|
||||
mnemonic = self.seed.mnemonic_display_list
|
||||
words = mnemonic[self.page_index*words_per_page:(self.page_index + 1)*words_per_page]
|
||||
|
||||
button_data = []
|
||||
if self.page_index < self.num_pages - 1 or self.seed_num is None:
|
||||
button_data.append(NEXT)
|
||||
@@ -799,7 +809,8 @@ class SeedWordsView(View):
|
||||
button_data.append(DONE)
|
||||
|
||||
selected_menu_num = seed_screens.SeedWordsScreen(
|
||||
seed=self.seed,
|
||||
title=f"Seed Words: {self.page_index+1}/{self.num_pages}",
|
||||
words=words,
|
||||
page_index=self.page_index,
|
||||
num_pages=self.num_pages,
|
||||
button_data=button_data,
|
||||
@@ -1313,7 +1324,8 @@ class SeedSingleSigAddressVerificationSelectSeedView(View):
|
||||
seeds = self.controller.storage.seeds
|
||||
|
||||
SCAN_SEED = ("Scan a seed", FontAwesomeIconConstants.QRCODE)
|
||||
ENTER_WORDS = "Enter 12/24 words"
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
button_data = []
|
||||
|
||||
text = "Load the seed to verify"
|
||||
@@ -1329,7 +1341,8 @@ class SeedSingleSigAddressVerificationSelectSeedView(View):
|
||||
text = "Select seed to verify"
|
||||
|
||||
button_data.append(SCAN_SEED)
|
||||
button_data.append(ENTER_WORDS)
|
||||
button_data.append(TYPE_12WORD)
|
||||
button_data.append(TYPE_24WORD)
|
||||
|
||||
selected_menu_num = seed_screens.SeedSingleSigAddressVerificationSelectSeedScreen(
|
||||
title="Verify Address",
|
||||
@@ -1356,7 +1369,12 @@ class SeedSingleSigAddressVerificationSelectSeedView(View):
|
||||
from seedsigner.views.scan_views import ScanView
|
||||
return Destination(ScanView)
|
||||
|
||||
elif button_data[selected_menu_num] == ENTER_WORDS:
|
||||
elif button_data[selected_menu_num] in [TYPE_12WORD, TYPE_24WORD]:
|
||||
from seedsigner.views.seed_views import SeedMnemonicEntryView
|
||||
if button_data[selected_menu_num] == TYPE_12WORD:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=12)
|
||||
else:
|
||||
self.controller.storage.init_pending_mnemonic(num_words=24)
|
||||
return Destination(SeedMnemonicEntryView)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user