mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
More tests; minor reformatting
This commit is contained in:
@@ -42,7 +42,7 @@ class Seed:
|
||||
raise Exception(f"Unrecognized wordlist_language_code {wordlist_language_code}")
|
||||
|
||||
|
||||
def _generate_seed(self) -> bool:
|
||||
def _generate_seed(self):
|
||||
try:
|
||||
self.seed_bytes = bip39.mnemonic_to_seed(self.mnemonic_str, password=self._passphrase, wordlist=self.wordlist)
|
||||
except Exception as e:
|
||||
@@ -64,6 +64,7 @@ class Seed:
|
||||
def wordlist_language_code(self) -> str:
|
||||
return self._wordlist_language_code
|
||||
|
||||
|
||||
@property
|
||||
def mnemonic_display_str(self) -> str:
|
||||
return unicodedata.normalize("NFC", " ".join(self._mnemonic))
|
||||
@@ -106,29 +107,36 @@ class Seed:
|
||||
# TODO: Support other BIP-39 wordlist languages!
|
||||
raise Exception("Not yet implemented!")
|
||||
|
||||
|
||||
@property
|
||||
def script_override(self) -> list:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def derivation_override(self, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
return None
|
||||
|
||||
|
||||
def detect_version(self, derivation_path: str, network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(network)]
|
||||
return bip32.detect_version(derivation_path, default="xpub", network=embit_network)
|
||||
|
||||
|
||||
@property
|
||||
def passphrase_label(self) -> str:
|
||||
return SettingsConstants.LABEL__BIP39_PASSPHRASE
|
||||
|
||||
|
||||
@property
|
||||
def seedqr_supported(self) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
@property
|
||||
def bip85_supported(self) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def get_fingerprint(self, network: str = SettingsConstants.MAINNET) -> str:
|
||||
root = bip32.HDKey.from_seed(self.seed_bytes, version=NETWORKS[SettingsConstants.map_network_to_embit(network)]["xprv"])
|
||||
return hexlify(root.child(0).fingerprint).decode('utf-8')
|
||||
@@ -158,19 +166,20 @@ class Seed:
|
||||
|
||||
class ElectrumSeed(Seed):
|
||||
|
||||
|
||||
def _generate_seed(self) -> bool:
|
||||
def _generate_seed(self):
|
||||
if len(self._mnemonic) != 12:
|
||||
return False
|
||||
raise InvalidSeedException(f"Unsupported Electrum seed length: {len(self._mnemonic)}")
|
||||
|
||||
s = hmac.digest(b"Seed version", self.mnemonic_str.encode('utf8'), hashlib.sha512).hex()
|
||||
prefix = s[0:3]
|
||||
|
||||
# only support Electrum Segwit version for now
|
||||
if SettingsConstants.ELECTRUM_SEED_SEGWIT == prefix:
|
||||
self.seed_bytes=hashlib.pbkdf2_hmac('sha512', self.mnemonic_str.encode('utf-8'), b'electrum' + self._passphrase.encode('utf-8'), iterations = SettingsConstants.ELECTRUM_PBKDF2_ROUNDS)
|
||||
return True
|
||||
|
||||
else:
|
||||
raise InvalidSeedException("Unsupported electrum seed input")
|
||||
return False
|
||||
raise InvalidSeedException(f"Unsupported Electrum seed format: {prefix}")
|
||||
|
||||
|
||||
def set_passphrase(self, passphrase: str, regenerate_seed: bool = True):
|
||||
if passphrase:
|
||||
@@ -184,6 +193,7 @@ class ElectrumSeed(Seed):
|
||||
# Regenerate the internal seed since passphrase changes the result
|
||||
self._generate_seed()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def normalize_electrum_passphrase(passphrase : str) -> str:
|
||||
passphrase = unicodedata.normalize('NFKD', passphrase)
|
||||
@@ -193,25 +203,31 @@ class ElectrumSeed(Seed):
|
||||
passphrase = u' '.join(passphrase.split())
|
||||
return passphrase
|
||||
|
||||
|
||||
@property
|
||||
def script_override(self) -> list:
|
||||
return [SettingsConstants.NATIVE_SEGWIT]
|
||||
|
||||
|
||||
def derivation_override(self, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
return "m/0h" if SettingsConstants.SINGLE_SIG == wallet_type else "m/1h"
|
||||
|
||||
|
||||
def detect_version(self, derivation_path: str, network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG) -> str:
|
||||
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(network)]
|
||||
return embit_network["zpub"] if SettingsConstants.SINGLE_SIG == wallet_type else embit_network["Zpub"]
|
||||
|
||||
|
||||
@property
|
||||
def passphrase_label(self) -> str:
|
||||
return SettingsConstants.LABEL__CUSTOM_EXTENSION
|
||||
|
||||
|
||||
@property
|
||||
def seedqr_supported(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
@property
|
||||
def bip85_supported(self) -> bool:
|
||||
return False
|
||||
|
||||
@@ -209,6 +209,8 @@ class SettingsConstants:
|
||||
LABEL__BIP39_PASSPHRASE = "BIP-39 Passphrase"
|
||||
LABEL__CUSTOM_EXTENSION = "Custom Extension"
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SettingsEntry:
|
||||
"""
|
||||
|
||||
@@ -620,6 +620,7 @@ class SeedBackupView(View):
|
||||
|
||||
def run(self):
|
||||
button_data = [self.VIEW_WORDS]
|
||||
|
||||
if self.seed.seedqr_supported:
|
||||
button_data.append(self.EXPORT_SEEDQR)
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ class TestSeedFlows(FlowTest):
|
||||
]
|
||||
|
||||
else:
|
||||
# Or we bomb out if the mnemonic is invalid for Electrum
|
||||
sequence.append(FlowStep(seed_views.SeedMnemonicInvalidView, button_data_selection=seed_views.SeedMnemonicInvalidView.DISCARD))
|
||||
|
||||
self.run_sequence(sequence)
|
||||
@@ -164,10 +165,8 @@ class TestSeedFlows(FlowTest):
|
||||
test_with_mnemonic("morning pretty hobby click extend color wait joke define sausage boost salmon".split())
|
||||
test_with_mnemonic("basket print toy noodle betray weird filter ticket insect copy force machine".split())
|
||||
|
||||
# Most BIP-39 seeds should fail; test seeds generated by bitcoiner.guide
|
||||
# Most BIP-39 seeds should fail; test seed generated by bitcoiner.guide
|
||||
test_with_mnemonic("pioneer divide volcano art victory family grow novel mandate bicycle senior adjust".split(), expects_electrum_seed_is_valid=False)
|
||||
test_with_mnemonic("gentle combine cool hamster ghost harvest gossip lend dismiss slam any toast".split(), expects_electrum_seed_is_valid=False)
|
||||
test_with_mnemonic("enough board blossom stamp fire buffalo digital solution sadness random number stone".split(), expects_electrum_seed_is_valid=False)
|
||||
|
||||
|
||||
def test_export_xpub_standard_flow(self):
|
||||
|
||||
+41
-1
@@ -1,8 +1,10 @@
|
||||
from seedsigner.models.seed import Seed, ElectrumSeed
|
||||
import pytest
|
||||
from seedsigner.models.seed import InvalidSeedException, Seed, ElectrumSeed
|
||||
|
||||
from seedsigner.models.settings import SettingsConstants
|
||||
|
||||
|
||||
# TODO: Change TAB indents to SPACE
|
||||
|
||||
def test_seed():
|
||||
seed = Seed(mnemonic="obscure bone gas open exotic abuse virus bunker shuffle nasty ship dash".split())
|
||||
@@ -37,9 +39,47 @@ def test_seed():
|
||||
|
||||
|
||||
def test_electrum_seed():
|
||||
"""
|
||||
ElectrumSeed should correctly parse a modern Electrum mnemonic.
|
||||
"""
|
||||
seed = ElectrumSeed(mnemonic="regular reject rare profit once math fringe chase until ketchup century escape".split())
|
||||
|
||||
intended_seed = b'\xcan|\xf8\x8a\x8d\xf78=Pq\xc4_\xe6\x02\x91\xfcs\xb2[\xed*\xdc\xc7%\xb6[_-(~D\xe5\x1e\x85%N\x9c\x03\x9dh\xafX}\x16\xb1\x99,\xbe\xc4\x11\xfaW\x0f\xb0\x89yD\xf4\x0f\xd5?\x8eA'
|
||||
|
||||
assert seed.seed_bytes == intended_seed
|
||||
|
||||
|
||||
def test_electrum_mnemonic_format():
|
||||
"""
|
||||
ElectrumSeed should reject mnemonics that are not 12 words long.
|
||||
"""
|
||||
with pytest.raises(InvalidSeedException):
|
||||
ElectrumSeed(mnemonic=["regular"] * 11)
|
||||
|
||||
with pytest.raises(InvalidSeedException):
|
||||
ElectrumSeed(mnemonic=["regular"] * 13)
|
||||
|
||||
with pytest.raises(InvalidSeedException):
|
||||
ElectrumSeed(mnemonic=["regular"] * 24)
|
||||
|
||||
|
||||
def test_electrum_seed_rejects_most_bip39_mnemonics():
|
||||
"""
|
||||
ElectrumSeed should throw an exception for most BIP-39 mnemonics.
|
||||
|
||||
There are 1/16 odds that a seed will be valid for both formats.
|
||||
"""
|
||||
# Most BIP-39 seeds should fail; test seeds generated by bitcoiner.guide
|
||||
with pytest.raises(InvalidSeedException):
|
||||
ElectrumSeed(mnemonic="pioneer divide volcano art victory family grow novel mandate bicycle senior adjust".split())
|
||||
|
||||
with pytest.raises(InvalidSeedException):
|
||||
ElectrumSeed(mnemonic="gentle combine cool hamster ghost harvest gossip lend dismiss slam any toast".split())
|
||||
|
||||
with pytest.raises(InvalidSeedException):
|
||||
ElectrumSeed(mnemonic="enough board blossom stamp fire buffalo digital solution sadness random number stone".split())
|
||||
|
||||
# This one is valid for both formats
|
||||
mnemonic = "only gain spot output unknown craft simple cram absorb suggest ridge famous".split()
|
||||
Seed(mnemonic)
|
||||
ElectrumSeed(mnemonic)
|
||||
|
||||
Reference in New Issue
Block a user