tests: wizard: add test case for "restore from slip39"
What changed, and why it matters
This commit only adds new automated tests for restoring a Bitcoin wallet from SLIP39 seed shares in Electrum's wallet creation wizard. It does not change any production code, user-facing behavior, or security-sensitive logic. There is no vulnerability or security fix here.
No security action needed. This is a test-only change increasing coverage for SLIP39 wallet restoration.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds two async unit tests to tests/test_wizard.py: test_create_standard_wallet_haveseed_slip39 and test_create_standard_wallet_haveseed_slip39_passphrase. They exercise the NewWalletWizard flow when a user chooses ‘I already have a seed’ with SLIP39 variant, including optional passphrase extension. The tests import electrum.slip39, call slip39.recover_ems with fixed test mnemonics, feed the recovered encrypted seed through wizard views, and assert the resulting receive address matches a hard-coded bech32 address. A TODO comment for slip39 is removed. No runtime code is modified.
Changed components
tests/test_wizard.pyInspect captured patch +63 / −1
diff --git a/tests/test_wizard.py b/tests/test_wizard.py
index db26a50..fa8b95d 100644
--- a/tests/test_wizard.py
+++ b/tests/test_wizard.py
@@ -8,6 +8,7 @@ from electrum.wizard import ServerConnectWizard, NewWalletWizard, WizardViewStat
from electrum.daemon import Daemon
from electrum.wallet import Abstract_Wallet
from electrum import util
+from electrum import slip39
from . import ElectrumTestCase
from .test_wallet_vertical import UNICODE_HORROR
@@ -127,7 +128,6 @@ class WalletWizardTestCase(WizardTestCase):
# TODO imported addresses
# TODO imported WIF keys
# TODO multisig
- # TODO slip39
def _wizard_for(
self,
@@ -393,6 +393,68 @@ class WalletWizardTestCase(WizardTestCase):
v = w.resolve_next(v.view, d)
self._set_password_and_check_address(v=v, w=w, recv_addr="bc1qjexrunguxz8rlfuul8h4apafyh3sq5yp9kg98j")
+ async def test_create_standard_wallet_haveseed_slip39(self):
+ w = self._wizard_for(wallet_type='standard')
+ v = w._current
+ d = v.wizard_data
+ self.assertEqual('keystore_type', v.view)
+
+ d.update({'keystore_type': 'haveseed'})
+ v = w.resolve_next(v.view, d)
+ self.assertEqual('have_seed', v.view)
+
+ # SLIP39 shares (128 bits, 2 groups from 1 of 1, 1 of 1, 3 of 5, 2 of 6)
+ mnemonics = [
+ "fact else acrobat romp analysis usher havoc vitamins analysis garden prevent romantic silent dramatic adjust priority mailman plains vintage else",
+ "fact else ceramic round craft lips snake faint adorn square bucket deadline violence guitar greatest academic stadium snake frequent memory",
+ "fact else ceramic scatter counter remove club forbid busy cause taxi forecast prayer uncover living type training forward software pumps",
+ "fact else ceramic shaft clock crowd detect cleanup wildlife depict include trip profile isolate express category wealthy advance garden mixture",
+ ]
+ encrypted_seed = slip39.recover_ems(mnemonics)
+
+ d.update({'seed': encrypted_seed, 'seed_variant': 'slip39', 'seed_type': 'slip39', 'seed_extend': False})
+ v = w.resolve_next(v.view, d)
+ self.assertEqual('script_and_derivation', v.view)
+
+ d.update({
+ 'script_type': 'p2wpkh', 'derivation_path': 'm/84h/0h/0h',
+ 'multisig_master_pubkey': 'zpub6riQosasrLdM1rmmohyUHtseLYeCBKP55Xe1LTT7jyKFM6dMMZPYVx5ug6zH2gZ6XFGcUYubjbm43vXHecTzNmoMS3yfp6oeZT3GetsGFt4'})
+ v = w.resolve_next(v.view, d)
+ self._set_password_and_check_address(v=v, w=w, recv_addr="bc1q40ksvkl7wvc2l999ppl48swgt3rsl45ykyyrjn")
+
+ async def test_create_standard_wallet_haveseed_slip39_passphrase(self):
+ w = self._wizard_for(wallet_type='standard')
+ v = w._current
+ d = v.wizard_data
+ self.assertEqual('keystore_type', v.view)
+
+ d.update({'keystore_type': 'haveseed'})
+ v = w.resolve_next(v.view, d)
+ self.assertEqual('have_seed', v.view)
+
+ # SLIP39 shares (128 bits, 2 groups from 1 of 1, 1 of 1, 3 of 5, 2 of 6)
+ mnemonics = [
+ "fact else acrobat romp analysis usher havoc vitamins analysis garden prevent romantic silent dramatic adjust priority mailman plains vintage else",
+ "fact else ceramic round craft lips snake faint adorn square bucket deadline violence guitar greatest academic stadium snake frequent memory",
+ "fact else ceramic scatter counter remove club forbid busy cause taxi forecast prayer uncover living type training forward software pumps",
+ "fact else ceramic shaft clock crowd detect cleanup wildlife depict include trip profile isolate express category wealthy advance garden mixture",
+ ]
+ encrypted_seed = slip39.recover_ems(mnemonics)
+
+ d.update({'seed': encrypted_seed, 'seed_variant': 'slip39', 'seed_type': 'slip39', 'seed_extend': True})
+ v = w.resolve_next(v.view, d)
+ self.assertEqual('have_ext', v.view)
+
+ d.update({'seed_extra_words': 'TREZOR'})
+ v = w.resolve_next(v.view, d)
+ self.assertEqual('script_and_derivation', v.view)
+
+ d.update({
+ 'script_type': 'p2wpkh', 'derivation_path': 'm/84h/0h/0h',
+ 'multisig_master_pubkey': 'zpub6s6A9ynh7TT1sPXmQyu8S6g7kxMF6iSZkM3NmgF4w7CtpsGgg56aouYSWHgAoMy186a8FRT8zkmhcwV5SWKFFQfMpvV8C9Ft4woWSzD5sXz'})
+ v = w.resolve_next(v.view, d)
+ self._set_password_and_check_address(v=v, w=w, recv_addr="bc1qs2svwhfz47qv9qju2waa6prxzv5f522fc4p06t")
+
async def test_2fa_createseed(self):
self.assertTrue(self.config.get('enable_plugin_trustedcoin'))
w = self._wizard_for(wallet_type='2fa')
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.