mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
Merge pull request #197 from kdmukai/final_word_v2
Updated "Final Word" calculation flow
This commit is contained in:
@@ -289,7 +289,9 @@ class TextArea(BaseComponent):
|
||||
# fits in its bounding rect (plus accounting for edge padding) using its given
|
||||
# font.
|
||||
# Measure from left baseline ("ls")
|
||||
# getbbox() seems to ignore "\n" so won't affect height calcs
|
||||
# TODO: getbbox() seems to ignore "\n" so isn't properly factored into height
|
||||
# calcs and yields incorrect full_text_width. For now must specify self.height to
|
||||
# render properly. Centering will be wrong.
|
||||
(left, top, full_text_width, bottom) = self.font.getbbox(self.text, anchor="ls")
|
||||
self.text_font_height = -1 * top
|
||||
self.bbox_height = self.text_font_height + bottom
|
||||
|
||||
@@ -151,7 +151,195 @@ class ToolsDiceEntropyEntryScreen(KeyboardScreen):
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolsCalcFinalWordShowFinalWordScreen(ButtonListScreen):
|
||||
class ToolsCalcFinalWordFinalizePromptScreen(ButtonListScreen):
|
||||
mnemonic_length: int = None
|
||||
num_entropy_bits: int = None
|
||||
|
||||
def __post_init__(self):
|
||||
self.title = "Build Final Word"
|
||||
self.is_bottom_list = True
|
||||
self.is_button_text_centered = True
|
||||
super().__post_init__()
|
||||
|
||||
self.components.append(TextArea(
|
||||
text=f"The {self.mnemonic_length}th word is built from {self.num_entropy_bits} more entropy bits plus auto-calculated checksum.",
|
||||
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolsCoinFlipEntryScreen(KeyboardScreen):
|
||||
def __post_init__(self):
|
||||
# Override values set by the parent class
|
||||
self.title = f"Coin Flip 1/{self.return_after_n_chars}"
|
||||
|
||||
# Specify the keys in the keyboard
|
||||
self.rows = 1
|
||||
self.cols = 4
|
||||
self.key_height = GUIConstants.TOP_NAV_TITLE_FONT_SIZE + 2 + 2*GUIConstants.EDGE_PADDING
|
||||
self.keys_charset = "10"
|
||||
|
||||
# Now initialize the parent class
|
||||
super().__post_init__()
|
||||
|
||||
self.components.append(TextArea(
|
||||
text="Heads = 1",
|
||||
screen_y = self.keyboard.rect[3] + 4*GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
self.components.append(TextArea(
|
||||
text="Tails = 0",
|
||||
screen_y = self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
|
||||
|
||||
def update_title(self) -> bool:
|
||||
self.title = f"Coin Flip {self.cursor_position + 1}/{self.return_after_n_chars}"
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
selected_final_word: str = None
|
||||
selected_final_bits: str = None
|
||||
checksum_bits: str = None
|
||||
actual_final_word: str = None
|
||||
|
||||
def __post_init__(self):
|
||||
self.is_bottom_list = True
|
||||
super().__post_init__()
|
||||
|
||||
# First what's the total bit display width and where do the checksum bits start?
|
||||
bit_font_size = GUIConstants.BUTTON_FONT_SIZE + 2
|
||||
font = Fonts.get_font(GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME, bit_font_size)
|
||||
(left, top, bit_display_width, bit_font_height) = font.getbbox("0" * 11, anchor="lt")
|
||||
(left, top, checksum_x, bottom) = font.getbbox("0" * (11 - len(self.checksum_bits)), anchor="lt")
|
||||
bit_display_x = int((self.canvas_width - bit_display_width)/2)
|
||||
checksum_x += bit_display_x
|
||||
|
||||
# Display the user's additional entropy input
|
||||
if self.selected_final_word:
|
||||
selection_text = self.selected_final_word
|
||||
keeper_selected_bits = self.selected_final_bits[:11 - len(self.checksum_bits)]
|
||||
|
||||
# The word's least significant bits will be rendered differently to convey
|
||||
# the fact that they're being discarded.
|
||||
discard_selected_bits = self.selected_final_bits[-1*len(self.checksum_bits):]
|
||||
else:
|
||||
# User entered coin flips or all zeros
|
||||
selection_text = self.selected_final_bits
|
||||
keeper_selected_bits = self.selected_final_bits
|
||||
|
||||
# We'll append spacer chars to preserve the vertical alignment (most
|
||||
# significant n bits always rendered in same column)
|
||||
discard_selected_bits = "_" * (len(self.checksum_bits))
|
||||
|
||||
self.components.append(TextArea(
|
||||
text=f"""Your input: \"{selection_text}\"""",
|
||||
screen_y=self.top_nav.height,
|
||||
))
|
||||
|
||||
# ...and that entropy's associated 11 bits
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
self.components.append(TextArea(
|
||||
text=keeper_selected_bits,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_size=bit_font_size,
|
||||
edge_padding=0,
|
||||
screen_x=bit_display_x,
|
||||
screen_y=screen_y,
|
||||
height=bit_font_height,
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
# Render the least significant bits that will be replaced by the checksum in a
|
||||
# de-emphasized font color.
|
||||
self.components.append(TextArea(
|
||||
text=discard_selected_bits,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_color=GUIConstants.LABEL_FONT_COLOR,
|
||||
font_size=bit_font_size,
|
||||
edge_padding=0,
|
||||
screen_x=checksum_x,
|
||||
screen_y=screen_y,
|
||||
height=bit_font_height,
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
# Show the checksum..
|
||||
self.components.append(TextArea(
|
||||
text="Checksum",
|
||||
edge_padding=0,
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + 2*GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
|
||||
# ...and its actual bits. Prepend spacers to keep vertical alignment
|
||||
checksum_spacer = "_" * (11 - len(self.checksum_bits))
|
||||
|
||||
screen_y = self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
|
||||
# This time we de-emphasize the prepended spacers that are irrelevant
|
||||
self.components.append(TextArea(
|
||||
text=checksum_spacer,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_color=GUIConstants.LABEL_FONT_COLOR,
|
||||
font_size=bit_font_size,
|
||||
edge_padding=0,
|
||||
screen_x=bit_display_x,
|
||||
screen_y=screen_y,
|
||||
height=bit_font_height,
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
# And especially highlight (orange!) the actual checksum bits
|
||||
self.components.append(TextArea(
|
||||
text=self.checksum_bits,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_size=bit_font_size,
|
||||
font_color=GUIConstants.ACCENT_COLOR,
|
||||
edge_padding=0,
|
||||
screen_x=checksum_x,
|
||||
screen_y=screen_y,
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
# And now the *actual* final word after merging the bit data
|
||||
self.components.append(TextArea(
|
||||
text=f"""Final Word: \"{self.actual_final_word}\"""",
|
||||
screen_y=self.components[-1].screen_y + self.components[-1].height + 2*GUIConstants.COMPONENT_PADDING,
|
||||
))
|
||||
|
||||
# Once again show the bits that came from the user's entropy...
|
||||
num_checksum_bits = len(self.checksum_bits)
|
||||
user_component = self.selected_final_bits[:11 - num_checksum_bits]
|
||||
screen_y = self.components[-1].screen_y + self.components[-1].height + GUIConstants.COMPONENT_PADDING
|
||||
self.components.append(TextArea(
|
||||
text=user_component,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_size=bit_font_size,
|
||||
edge_padding=0,
|
||||
screen_x=bit_display_x,
|
||||
screen_y=screen_y,
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
# ...and append the checksum's bits, still highlighted in orange
|
||||
self.components.append(TextArea(
|
||||
text=self.checksum_bits,
|
||||
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
|
||||
font_color=GUIConstants.ACCENT_COLOR,
|
||||
font_size=bit_font_size,
|
||||
edge_padding=0,
|
||||
screen_x=checksum_x,
|
||||
screen_y=screen_y,
|
||||
is_text_centered=False,
|
||||
))
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolsCalcFinalWordDoneScreen(ButtonListScreen):
|
||||
final_word: str = None
|
||||
mnemonic_word_length: int = 12
|
||||
fingerprint: str = None
|
||||
|
||||
@@ -8,23 +8,25 @@ from typing import List
|
||||
from seedsigner.models.seed import Seed
|
||||
|
||||
|
||||
def calculate_checksum(partial_mnemonic: list, wordlist_language_code: str) -> List[str]:
|
||||
""" Provide 11- or 23-word mnemonic, returns complete mnemonic w/checksum as a list """
|
||||
if len(partial_mnemonic) not in [11, 23]:
|
||||
raise Exception("Pass in a 11- or 23-word mnemonic")
|
||||
def calculate_checksum(mnemonic: list, wordlist_language_code: str) -> List[str]:
|
||||
"""
|
||||
Provide 12- or 24-word mnemonic, returns complete mnemonic w/checksum as a list.
|
||||
|
||||
If 11- or 23-words are provided, append word `0000` to end of list as temp final
|
||||
word.
|
||||
"""
|
||||
if len(mnemonic) in [11, 23]:
|
||||
mnemonic.append(Seed.get_wordlist(wordlist_language_code)[0])
|
||||
|
||||
if len(mnemonic) not in [12, 24]:
|
||||
raise Exception("Pass in a 12- or 24-word mnemonic")
|
||||
|
||||
# Work on a copy of the input list
|
||||
mnemonic_copy = partial_mnemonic.copy()
|
||||
|
||||
# 12-word seeds contribute 7 bits of entropy to the final word; 24-word seeds
|
||||
# contribute 3 bits. But we don't have any partial entropy bits to use to help us
|
||||
# create the final word. So just default to filling those missing values with zeroes
|
||||
# ("abandon" is word 0000, so effectively inserts zeroes).
|
||||
mnemonic_copy.append("abandon")
|
||||
mnemonic_copy = mnemonic.copy()
|
||||
|
||||
# Convert the resulting mnemonic to bytes, but we `ignore_checksum` validation
|
||||
# because we have to assume it's incorrect since we just hard-coded it above; we'll
|
||||
# fix that next.
|
||||
# because we assume it's incorrect since we either let the user select their own
|
||||
# final word OR we injected the 0000 word from the wordlist.
|
||||
mnemonic_bytes = bip39.mnemonic_to_bytes(unicodedata.normalize("NFKD", " ".join(mnemonic_copy)), ignore_checksum=True, wordlist=Seed.get_wordlist(wordlist_language_code))
|
||||
|
||||
# This function will convert the bytes back into a mnemonic, but it will also
|
||||
|
||||
@@ -127,13 +127,14 @@ class SeedMnemonicEntryView(View):
|
||||
|
||||
if ret == RET_CODE__BACK_BUTTON:
|
||||
if self.cur_word_index > 0:
|
||||
return Destination(
|
||||
SeedMnemonicEntryView,
|
||||
view_args={
|
||||
"cur_word_index": self.cur_word_index - 1,
|
||||
"is_calc_final_word": self.is_calc_final_word
|
||||
}
|
||||
)
|
||||
return Destination(BackStackView)
|
||||
# return Destination(
|
||||
# SeedMnemonicEntryView,
|
||||
# view_args={
|
||||
# "cur_word_index": self.cur_word_index - 1,
|
||||
# "is_calc_final_word": self.is_calc_final_word
|
||||
# }
|
||||
# )
|
||||
else:
|
||||
self.controller.storage.discard_pending_mnemonic()
|
||||
return Destination(MainMenuView)
|
||||
@@ -142,17 +143,15 @@ class SeedMnemonicEntryView(View):
|
||||
self.controller.storage.update_pending_mnemonic(ret, self.cur_word_index)
|
||||
|
||||
if self.is_calc_final_word and self.cur_word_index == self.controller.storage.pending_mnemonic_length - 2:
|
||||
# Time to calculate the last word
|
||||
# TODO: Option to add missing entropy for the last word:
|
||||
# * 3 bits for a 24-word seed
|
||||
# * 7 bits for a 12-word seed
|
||||
from seedsigner.helpers import mnemonic_generation
|
||||
# Time to calculate the last word. User must decide how they want to specify
|
||||
# the last bits of entropy for the final word.
|
||||
from seedsigner.views.tools_views import ToolsCalcFinalWordFinalizePromptView
|
||||
return Destination(ToolsCalcFinalWordFinalizePromptView)
|
||||
|
||||
if self.is_calc_final_word and self.cur_word_index == self.controller.storage.pending_mnemonic_length - 1:
|
||||
# Time to calculate the last word. User must either select a final word to
|
||||
# contribute entropy to the checksum word OR we assume 0 ("abandon").
|
||||
from seedsigner.views.tools_views import ToolsCalcFinalWordShowFinalWordView
|
||||
full_mnemonic = mnemonic_generation.calculate_checksum(
|
||||
self.controller.storage.pending_mnemonic[:-1], # Must omit the last word's empty value
|
||||
wordlist_language_code=self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)
|
||||
)
|
||||
self.controller.storage.update_pending_mnemonic(full_mnemonic[-1], self.cur_word_index+1)
|
||||
return Destination(ToolsCalcFinalWordShowFinalWordView)
|
||||
|
||||
if self.cur_word_index < self.controller.storage.pending_mnemonic_length - 1:
|
||||
|
||||
@@ -8,7 +8,7 @@ from PIL.ImageOps import autocontrast
|
||||
from seedsigner.hardware.camera import Camera
|
||||
from seedsigner.gui.components import FontAwesomeIconConstants
|
||||
from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen)
|
||||
from seedsigner.gui.screens.tools_screens import ToolsDiceEntropyEntryScreen, ToolsImageEntropyFinalImageScreen, ToolsImageEntropyLivePreviewScreen, ToolsCalcFinalWordShowFinalWordScreen
|
||||
from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordFinalizePromptScreen, ToolsCalcFinalWordScreen, ToolsCoinFlipEntryScreen, ToolsDiceEntropyEntryScreen, ToolsImageEntropyFinalImageScreen, ToolsImageEntropyLivePreviewScreen, ToolsCalcFinalWordDoneScreen
|
||||
from seedsigner.helpers import mnemonic_generation
|
||||
from seedsigner.models.seed import Seed
|
||||
from seedsigner.models.settings_definition import SettingsConstants
|
||||
@@ -240,15 +240,148 @@ class ToolsCalcFinalWordNumWordsView(View):
|
||||
|
||||
elif button_data[selected_menu_num] == TWELVE:
|
||||
self.controller.storage.init_pending_mnemonic(12)
|
||||
return Destination(SeedMnemonicEntryView, view_args={"is_calc_final_word": True})
|
||||
|
||||
# return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
|
||||
return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
|
||||
|
||||
elif button_data[selected_menu_num] == TWENTY_FOUR:
|
||||
self.controller.storage.init_pending_mnemonic(24)
|
||||
return Destination(SeedMnemonicEntryView, view_args={"is_calc_final_word": True})
|
||||
|
||||
# return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
|
||||
return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True))
|
||||
|
||||
|
||||
|
||||
class ToolsCalcFinalWordFinalizePromptView(View):
|
||||
def run(self):
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
if mnemonic_length == 12:
|
||||
num_entropy_bits = 7
|
||||
else:
|
||||
num_entropy_bits = 3
|
||||
|
||||
COIN_FLIPS = "Coin flip entropy"
|
||||
SELECT_WORD = f"Word selection entropy"
|
||||
ZEROS = "Finalize with zeros"
|
||||
|
||||
button_data = [COIN_FLIPS, SELECT_WORD, ZEROS]
|
||||
selected_menu_num = ToolsCalcFinalWordFinalizePromptScreen(
|
||||
mnemonic_length=mnemonic_length,
|
||||
num_entropy_bits=num_entropy_bits,
|
||||
button_data=button_data,
|
||||
).display()
|
||||
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
elif button_data[selected_menu_num] == COIN_FLIPS:
|
||||
return Destination(ToolsCalcFinalWordCoinFlipsView)
|
||||
|
||||
elif button_data[selected_menu_num] == SELECT_WORD:
|
||||
# Clear the final word slot, just in case we're returning via BACK button
|
||||
self.controller.storage.update_pending_mnemonic(None, mnemonic_length - 1)
|
||||
return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True, cur_word_index=mnemonic_length - 1))
|
||||
|
||||
elif button_data[selected_menu_num] == ZEROS:
|
||||
# User skipped the option to select a final word to provide last bits of
|
||||
# entropy. We'll insert all zeros and piggy-back on the coin flip attr
|
||||
wordlist_language_code = self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)
|
||||
self.controller.storage.update_pending_mnemonic(Seed.get_wordlist(wordlist_language_code)[0], mnemonic_length - 1)
|
||||
return Destination(ToolsCalcFinalWordShowFinalWordView, view_args=dict(coin_flips="0" * num_entropy_bits))
|
||||
|
||||
|
||||
|
||||
class ToolsCalcFinalWordCoinFlipsView(View):
|
||||
def run(self):
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
|
||||
if mnemonic_length == 12:
|
||||
total_flips = 7
|
||||
else:
|
||||
total_flips = 3
|
||||
|
||||
ret_val = ToolsCoinFlipEntryScreen(
|
||||
return_after_n_chars=total_flips,
|
||||
).display()
|
||||
|
||||
if ret_val == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
else:
|
||||
print(ret_val)
|
||||
binary_string = ret_val + "0" * (11 - total_flips)
|
||||
wordlist_index = int(binary_string, 2)
|
||||
wordlist = Seed.get_wordlist(self.controller.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE))
|
||||
word = wordlist[wordlist_index]
|
||||
self.controller.storage.update_pending_mnemonic(word, mnemonic_length - 1)
|
||||
|
||||
return Destination(ToolsCalcFinalWordShowFinalWordView, view_args=dict(coin_flips=ret_val))
|
||||
|
||||
|
||||
|
||||
class ToolsCalcFinalWordShowFinalWordView(View):
|
||||
def __init__(self, coin_flips=None):
|
||||
super().__init__()
|
||||
self.coin_flips = coin_flips
|
||||
|
||||
|
||||
def run(self):
|
||||
# Construct the actual final word. The user's selected_final_word
|
||||
# contributes:
|
||||
# * 3 bits to a 24-word seed (plus 8-bit checksum)
|
||||
# * 7 bits to a 12-word seed (plus 4-bit checksum)
|
||||
from seedsigner.helpers import mnemonic_generation
|
||||
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
wordlist_language_code = self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)
|
||||
wordlist = Seed.get_wordlist(wordlist_language_code)
|
||||
|
||||
final_mnemonic = mnemonic_generation.calculate_checksum(
|
||||
mnemonic=self.controller.storage.pending_mnemonic,
|
||||
wordlist_language_code=wordlist_language_code,
|
||||
)
|
||||
self.controller.storage.update_pending_mnemonic(final_mnemonic[-1], mnemonic_length - 1)
|
||||
|
||||
# Prep the user's selected word (if there was one) and the actual final word for
|
||||
# the display.
|
||||
if self.coin_flips:
|
||||
selected_final_word = None
|
||||
selected_final_bits = self.coin_flips
|
||||
else:
|
||||
# Convert the user's final word selection into its binary index equivalent
|
||||
selected_final_word = mnemonic[-1]
|
||||
selected_final_bits = format(wordlist.index(selected_final_word), '011b')
|
||||
|
||||
# And grab the actual final word's checksum bits
|
||||
actual_final_word = self.controller.storage.pending_mnemonic[-1]
|
||||
if mnemonic_length == 12:
|
||||
checksum_bits = format(wordlist.index(actual_final_word), '011b')[-4:]
|
||||
else:
|
||||
checksum_bits = format(wordlist.index(actual_final_word), '011b')[-8:]
|
||||
|
||||
NEXT = "Next"
|
||||
button_data = [NEXT]
|
||||
selected_menu_num = ToolsCalcFinalWordScreen(
|
||||
title="Final Word Calc",
|
||||
button_data=button_data,
|
||||
selected_final_word=selected_final_word,
|
||||
selected_final_bits=selected_final_bits,
|
||||
checksum_bits=checksum_bits,
|
||||
actual_final_word=actual_final_word,
|
||||
).display()
|
||||
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
elif button_data[selected_menu_num] == NEXT:
|
||||
return Destination(ToolsCalcFinalWordDoneView)
|
||||
|
||||
|
||||
|
||||
class ToolsCalcFinalWordDoneView(View):
|
||||
def run(self):
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_word_length = len(mnemonic)
|
||||
@@ -258,7 +391,7 @@ class ToolsCalcFinalWordShowFinalWordView(View):
|
||||
DISCARD = ("Discard", None, None, "red")
|
||||
button_data = [LOAD, DISCARD]
|
||||
|
||||
selected_menu_num = ToolsCalcFinalWordShowFinalWordScreen(
|
||||
selected_menu_num = ToolsCalcFinalWordDoneScreen(
|
||||
final_word=final_word,
|
||||
mnemonic_word_length=mnemonic_word_length,
|
||||
fingerprint=self.controller.storage.get_pending_mnemonic_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK)),
|
||||
|
||||
@@ -43,6 +43,26 @@ def test_calculate_checksum():
|
||||
|
||||
|
||||
|
||||
def test_calculate_checksum_with_default_final_word():
|
||||
""" 11-word and 23-word mnemonics use word `0000` as a temp final word to complete
|
||||
the mnemonic.
|
||||
"""
|
||||
partial_mnemonic = "crawl focus rescue cable view pledge rather dinner cousin unfair day"
|
||||
mnemonic1 = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "), wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
|
||||
|
||||
partial_mnemonic += " abandon"
|
||||
mnemonic2 = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "), wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
|
||||
assert mnemonic1 == mnemonic2
|
||||
|
||||
partial_mnemonic = "bubble father debate ankle injury fence mesh evolve section wet coyote violin pyramid flower rent arrow round clutch myth safe base skin mobile"
|
||||
mnemonic1 = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "), wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
|
||||
|
||||
partial_mnemonic += " abandon"
|
||||
mnemonic2 = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "), wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
|
||||
assert mnemonic1 == mnemonic2
|
||||
|
||||
|
||||
|
||||
def test_verify_against_coldcard_sample():
|
||||
""" https://coldcard.com/docs/verifying-dice-roll-math """
|
||||
dice_rolls = "123456"
|
||||
|
||||
Reference in New Issue
Block a user