mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
BIP85 - Added test_bip85.py
- Updates to get_bip85_child_mnemonic
- Updates to BIP-85 naming used
- Modified the flow for BIP-85 Child to use one display per function
- fix to BIP85 Index input wasn't deleting the first two characters
This commit is contained in:
@@ -1577,7 +1577,7 @@ class MultisigWalletDescriptorScreen(ButtonListScreen):
|
||||
|
||||
@dataclass
|
||||
class SeedExportBIP85GetIndexScreen(BaseTopNavScreen):
|
||||
title: str = "BIP85 Seed Index"
|
||||
title: str = "BIP-85 Index"
|
||||
bip85_index: str = ""
|
||||
|
||||
def __post_init__(self):
|
||||
@@ -1693,10 +1693,10 @@ class SeedExportBIP85GetIndexScreen(BaseTopNavScreen):
|
||||
|
||||
elif ret_val in Keyboard.ADDITIONAL_KEYS and input == HardwareButtonsConstants.KEY_PRESS:
|
||||
if ret_val == Keyboard.KEY_BACKSPACE["code"]:
|
||||
if len(self.bip85_index) <= 2:
|
||||
if cursor_position == 0:
|
||||
pass
|
||||
elif cursor_position == len(self.bip85_index):
|
||||
self.derivation_path = self.bip85_index[:-1]
|
||||
self.bip85_index = self.bip85_index[:-1]
|
||||
cursor_position -= 1
|
||||
else:
|
||||
self.bip85_index = self.bip85_index[:cursor_position - 1] + self.bip85_index[
|
||||
@@ -1737,7 +1737,7 @@ class BIP85SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
self.title = f"BIP85 Words: {self.page_index+1}/{self.num_pages}"
|
||||
self.title = f"BIP-85 Words: {self.page_index+1}/{self.num_pages}"
|
||||
super().__post_init__()
|
||||
|
||||
#index = 0
|
||||
|
||||
@@ -122,19 +122,19 @@ class Seed:
|
||||
return xpub
|
||||
|
||||
# Derives a BIP85 mnemonic (seed word) from the master seed words using embit functions
|
||||
def get_bip85_child_mnemonic(self, bip85_index: int, bip85_num_words: int, wallet_path: str = '/',
|
||||
network: str = SettingsConstants.MAINNET):
|
||||
def get_bip85_child_mnemonic(self, bip85_index: int, bip85_num_words: int):
|
||||
passphrase = self._passphrase
|
||||
# language = 'english'
|
||||
# lang_code = 0
|
||||
# Need to add language later for path, defaults to English (0)
|
||||
path = "m/83696968'/39'/0'/{bip85_num_words}'/{bip85_index}'".format(bip85_num_words=bip85_num_words,
|
||||
bip85_index=bip85_index)
|
||||
seed = bip39.mnemonic_to_seed(self.mnemonic_str, password=self._passphrase, wordlist=self.wordlist)
|
||||
xprv = embit.bip32.HDKey.from_seed(seed)
|
||||
#seed = bip39.mnemonic_to_seed(self.mnemonic_str, password=self._passphrase, wordlist=self.wordlist)
|
||||
# xprv = embit.bip32.HDKey.from_seed(seed)
|
||||
root = bip32.HDKey.from_seed(self.seed_bytes, version=NETWORKS[SettingsConstants.map_network_to_embit(network)]["xprv"])
|
||||
|
||||
# Derive k
|
||||
xprv = xprv.derive(path)
|
||||
xprv = root.derive(path)
|
||||
entropy = hmac_sha512(xprv.secret)
|
||||
width = round(bip85_num_words / 12 * 16)
|
||||
return bip39.mnemonic_from_bytes(entropy[:width])
|
||||
|
||||
@@ -149,7 +149,7 @@ class SettingsConstants:
|
||||
SETTING__SIG_TYPES = "sig_types"
|
||||
SETTING__SCRIPT_TYPES = "script_types"
|
||||
SETTING__XPUB_DETAILS = "xpub_details"
|
||||
SETTING__GEN_BIP85 = "Generate BIP85"
|
||||
SETTING__BIP85_CHILD_SEEDS = "BIP-85_child_seeds"
|
||||
SETTING__PASSPHRASE = "passphrase"
|
||||
SETTING__CAMERA_ROTATION = "camera_rotation"
|
||||
SETTING__COMPACT_SEEDQR = "compact_seedqr"
|
||||
@@ -394,8 +394,8 @@ class SettingsDefinition:
|
||||
default_value=SettingsConstants.OPTION__ENABLED),
|
||||
|
||||
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
|
||||
attr_name=SettingsConstants.SETTING__GEN_BIP85,
|
||||
display_name="Generate BIP85",
|
||||
attr_name=SettingsConstants.SETTING__BIP85_CHILD_SEEDS,
|
||||
display_name="BIP-85 child seeds",
|
||||
visibility=SettingsConstants.VISIBILITY__ADVANCED,
|
||||
default_value=SettingsConstants.OPTION__DISABLED),
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ class SeedOptionsView(View):
|
||||
VERIFY_ADDRESS = "Verify Addr"
|
||||
EXPORT_XPUB = "Export Xpub"
|
||||
BACKUP = ("Backup Seed", None, None, None, SeedSignerCustomIconConstants.SMALL_CHEVRON_RIGHT)
|
||||
GENBIP85 = ("Gen BIP85 Child seed")
|
||||
BIP85_CHILD_SEED = "BIP-85 Child Seed"
|
||||
DISCARD = ("Discard Seed", None, None, "red")
|
||||
|
||||
button_data = []
|
||||
@@ -382,8 +382,8 @@ class SeedOptionsView(View):
|
||||
|
||||
if self.settings.get_value(SettingsConstants.SETTING__XPUB_EXPORT) == SettingsConstants.OPTION__ENABLED:
|
||||
button_data.append(EXPORT_XPUB)
|
||||
if self.settings.get_value(SettingsConstants.SETTING__GEN_BIP85) == SettingsConstants.OPTION__ENABLED:
|
||||
button_data.append(GENBIP85)
|
||||
if self.settings.get_value(SettingsConstants.SETTING__BIP85_CHILD_SEEDS) == SettingsConstants.OPTION__ENABLED:
|
||||
button_data.append(BIP85_CHILD_SEED)
|
||||
|
||||
button_data.append(BACKUP)
|
||||
button_data.append(DISCARD)
|
||||
@@ -418,36 +418,92 @@ class SeedOptionsView(View):
|
||||
elif button_data[selected_menu_num] == DISCARD:
|
||||
return Destination(SeedDiscardView, view_args={"seed_num": self.seed_num})
|
||||
|
||||
elif button_data[selected_menu_num] == GENBIP85:
|
||||
return Destination(SeedExportBIP85View, view_args={"seed_num": self.seed_num})
|
||||
elif button_data[selected_menu_num] == BIP85_CHILD_SEED:
|
||||
return Destination(BIP85ApplicationModeView, view_args={"seed_num": self.seed_num})
|
||||
|
||||
|
||||
|
||||
"""****************************************************************************
|
||||
Export BIP85 Child Flow
|
||||
****************************************************************************"""
|
||||
# This will ask user word number and display warning if neccessary before displaying the words
|
||||
class SeedExportBIP85View(View):
|
||||
BIP85 - Derive child mnemonic (seed) flow
|
||||
****************************************************************************
|
||||
* Ask the user the application type as defined in the BIP0085 spec.
|
||||
* Currently only Word mode of 12, 24 words.
|
||||
* Possible future additions are
|
||||
* WIF (HDSEED)
|
||||
* XPRV (BIP32)"""
|
||||
class BIP85ApplicationModeView(View):
|
||||
def __init__(self, seed_num: int):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
self.num_words = 0
|
||||
self.bip85_index = ""
|
||||
|
||||
self.bip85_index = 0
|
||||
self.bip85_app_no = None
|
||||
|
||||
def run(self):
|
||||
|
||||
WORDS_12 = "12 Words"
|
||||
WORDS_24 = "24 Words"
|
||||
button_data=[WORDS_12, WORDS_24]
|
||||
# Need to setup width as 32
|
||||
WIF = "WIF"
|
||||
#Need to setup width as 64
|
||||
XPRV = "XPRV"
|
||||
|
||||
# Future enhancement to display WIF (HD-SEED) and XPRV (Bip32)
|
||||
#button_data=[WORDS_12, WORDS_24, WIF, XPRV]
|
||||
button_data = [WORDS_12, WORDS_24]
|
||||
|
||||
selected_menu_num = ButtonListScreen(
|
||||
title="Generate BIP85 Seed",
|
||||
title="BIP-85 Num Words",
|
||||
button_data=button_data
|
||||
).display()
|
||||
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
if button_data[selected_menu_num] == WORDS_12:
|
||||
self.bip85_app_no = "words"
|
||||
self.num_words = 12
|
||||
elif button_data[selected_menu_num] == WORDS_24:
|
||||
self.bip85_app_no = "words"
|
||||
self.num_words = 24
|
||||
elif button_data[selected_menu_num] == WIF:
|
||||
print("WIF")
|
||||
self.bip85_app_no = "wif"
|
||||
return(Destination(NotYetImplementedView))
|
||||
elif button_data[selected_menu_num] == XPRV:
|
||||
self.bip85_app_no = "xprv"
|
||||
return(Destination(NotYetImplementedView))
|
||||
|
||||
destination = Destination(
|
||||
BIP85ChildSeedIndexView,
|
||||
view_args={"seed_num": self.seed_num, "num_words": self.num_words, "bip85_app_no" : self.bip85_app_no,
|
||||
"bip85_index": self.bip85_index})
|
||||
#skip_current_view=True, # Prevent going BACK to WarningViews
|
||||
|
||||
return(destination)
|
||||
|
||||
##
|
||||
|
||||
|
||||
# View to retrieve the derived seed index
|
||||
class BIP85ChildSeedIndexView(View):
|
||||
def __init__(self, seed_num: int, num_words: int, bip85_app_no: str, bip85_index: int):
|
||||
super().__init__()
|
||||
self.bip85_app_no = bip85_app_no
|
||||
self.seed_num = seed_num
|
||||
self.num_words = num_words
|
||||
self.bip85_index = bip85_index
|
||||
#if self.seed_num is None:
|
||||
# self.seed = self.controller.storage.get_pending_seed()
|
||||
#else:
|
||||
# self.seed = self.controller.get_seed(self.seed_num)
|
||||
|
||||
#self.num_pages=int(self.num_words/4)
|
||||
|
||||
def run(self):
|
||||
args = {"seed_num": self.seed_num, "num_words": self.num_words, "bip85_app_no" : self.bip85_app_no, "bip85_index": self.bip85_index}
|
||||
|
||||
# Change this later to use the generic Screen input keyboard
|
||||
ret = seed_screens.SeedExportBIP85GetIndexScreen(
|
||||
).display()
|
||||
|
||||
@@ -457,17 +513,31 @@ class SeedExportBIP85View(View):
|
||||
# ret should be the bip85_index let's convert to int
|
||||
self.bip85_index = int(ret)
|
||||
|
||||
if button_data[selected_menu_num] == WORDS_12:
|
||||
self.num_words = 12
|
||||
return Destination(
|
||||
BIP85ChildSeedWarningView,
|
||||
view_args={"seed_num": self.seed_num, "num_words": self.num_words,
|
||||
"bip85_index": self.bip85_index}
|
||||
)
|
||||
|
||||
elif button_data[selected_menu_num] == WORDS_24:
|
||||
self.num_words = 24
|
||||
class BIP85ChildSeedWarningView(View):
|
||||
def __init__(self, seed_num: int, num_words: int, bip85_index: int):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
self.num_words = num_words
|
||||
self.bip85_index = bip85_index
|
||||
# if self.seed_num is None:
|
||||
# self.seed = self.controller.storage.get_pending_seed()
|
||||
# else:
|
||||
# self.seed = self.controller.get_seed(self.seed_num)
|
||||
|
||||
destination = Destination(
|
||||
BIP85SeedWordsView,
|
||||
view_args={"seed_num": self.seed_num, "page_index": 0, "num_words": self.num_words,
|
||||
"bip85_index": self.bip85_index},
|
||||
skip_current_view=True, # Prevent going BACK to WarningViews
|
||||
# self.num_pages=int(self.num_words/4)
|
||||
|
||||
def run(self):
|
||||
args = {"seed_num": self.seed_num, "num_words": self.num_words, "bip85_index": self.bip85_index}
|
||||
|
||||
##
|
||||
destination = Destination(BIP85SeedWordsView, view_args={"seed_num": self.seed_num, "page_index": 0, "num_words": self.num_words,
|
||||
"bip85_index": self.bip85_index}, skip_current_view=True, # Prevent going BACK to WarningViews
|
||||
)
|
||||
|
||||
if self.settings.get_value(SettingsConstants.SETTING__DIRE_WARNINGS) == SettingsConstants.OPTION__DISABLED:
|
||||
@@ -486,7 +556,6 @@ class SeedExportBIP85View(View):
|
||||
return Destination(BackStackView)
|
||||
|
||||
|
||||
|
||||
class BIP85SeedWordsView(View):
|
||||
def __init__(self, seed_num: int, num_words: int, bip85_index: int, page_index: int = 0):
|
||||
super().__init__()
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import pytest
|
||||
from mock import MagicMock
|
||||
from seedsigner.models import Seed
|
||||
from embit import bip39
|
||||
|
||||
from seedsigner.models.settings import SettingsConstants
|
||||
|
||||
|
||||
|
||||
def test_derive_child_mnemonic():
|
||||
|
||||
expected = "unusual topic foot figure pulp target glimpse core electric spot neglect fame"
|
||||
seed = Seed(mnemonic="resource timber firm banner horror pupil frozen main pear direct pioneer broken grid core insane begin sister pony end debate task silk empty curious".split())
|
||||
|
||||
actual = seed.get_bip85_child_mnemonic(0, 12)
|
||||
assert actual == expected
|
||||
|
||||
|
||||
expected = "imitate post very mandate retreat prevent tiny snow fetch canvas town shrug fix food summer library symptom occur slam style cruise wolf phone key"
|
||||
seed = Seed(mnemonic="resource timber firm banner horror pupil frozen main pear direct pioneer broken grid core insane begin sister pony end debate task silk empty curious".split())
|
||||
|
||||
actual = seed.get_bip85_child_mnemonic(0, 24)
|
||||
assert actual == expected
|
||||
Reference in New Issue
Block a user