3
3
import readline
4
4
import sys
5
5
from cmd import Cmd
6
+ from getpass import getpass
6
7
from platform import platform
7
8
from pkg_resources import DistributionNotFound , get_distribution
8
9
32
33
RESET_TERMINAL_COLOR = "\033 [0m"
33
34
34
35
36
+ ADVANCED_WARNING_STRING = (
37
+ "WARNING: this feature is for advanced users ONLY. "
38
+ "A mistake here could cause loss of funds. "
39
+ "Are you sure you want to continue?"
40
+ )
41
+
42
+
35
43
def blue_fg (string ):
36
44
return f"\033 [34m{ string } { RESET_TERMINAL_COLOR } "
37
45
@@ -161,17 +169,39 @@ def _get_path(is_testnet):
161
169
if use_default_path :
162
170
return default_path
163
171
else :
164
- scary_string = (
165
- "WARNING: this feature is for advanced users. "
166
- "A mistake here could cause loss of funds. "
167
- "Are you sure you want to continue?"
168
- )
169
- if _get_bool (prompt = scary_string , default = False , scary_text = True ):
172
+ if _get_bool (prompt = ADVANCED_WARNING_STRING , default = False , scary_text = True ):
170
173
return _get_path_string ()
171
174
else :
172
175
return _get_path (is_testnet )
173
176
174
177
178
+ def _get_confirmed_pw ():
179
+ first = getpass (prompt = "Enter custom passphrase: " )
180
+ second = getpass (prompt = "Confirm custom passphrase: " )
181
+ if first != second :
182
+ print_red ("Passphrases don't match, please try again." )
183
+ return _get_confirmed_pw ()
184
+ return first
185
+
186
+
187
+ def _get_password ():
188
+ if not _get_bool (
189
+ prompt = "Use a passphrase (advanced users only)?" ,
190
+ default = False ,
191
+ ):
192
+ return ""
193
+
194
+ # Reconfirm
195
+ if _get_bool (
196
+ prompt = ADVANCED_WARNING_STRING ,
197
+ default = False ,
198
+ scary_text = True ,
199
+ ):
200
+ return _get_confirmed_pw ()
201
+ else :
202
+ return ""
203
+
204
+
175
205
class WordCompleter :
176
206
def __init__ (self , wordlist ):
177
207
self .wordlist = wordlist
@@ -393,9 +423,10 @@ def __init__(self):
393
423
394
424
def do_generate_seed (self , arg ):
395
425
"""Seedpicker implementation: calculate bitcoin public and private key information from BIP39 words you draw out of a hat"""
426
+
396
427
first_words = _get_bip39_firstwords ()
397
428
use_default_checksum = _get_bool (
398
- prompt = "Use default checksum index for BIP39 seed phrase ?" , default = True
429
+ prompt = "Use the default checksum index?" , default = True
399
430
)
400
431
valid_checksums_generator = calc_valid_seedpicker_checksums (
401
432
first_words = first_words
@@ -436,6 +467,8 @@ def do_generate_seed(self, arg):
436
467
)
437
468
last_word = valid_checksum_words [index ]
438
469
470
+ password = _get_password ()
471
+
439
472
is_testnet = not _get_bool (prompt = "Use Mainnet?" , default = False )
440
473
path_to_use = _get_path (is_testnet = is_testnet )
441
474
@@ -446,15 +479,18 @@ def do_generate_seed(self, arg):
446
479
else :
447
480
SLIP132_VERSION_BYTES = "02aa7ed3"
448
481
449
- hd_priv = HDPrivateKey .from_mnemonic (first_words + " " + last_word )
482
+ hd_priv = HDPrivateKey .from_mnemonic (
483
+ mnemonic = f"{ first_words } { last_word } " , password = password .encode ()
484
+ )
450
485
print (yellow_fg ("SECRET INFO" ) + red_fg (" (guard this VERY carefully)" ))
451
486
print_green (f"Last word: { last_word } " )
452
487
print_green (
453
- f"Full ({ len (first_words .split ()) + 1 } word) mnemonic (including last word: { first_words + ' ' + last_word } ) "
488
+ f"Full ({ len (first_words .split ()) + 1 } word) mnemonic (including last word) : { first_words + ' ' + last_word } "
454
489
)
455
- print ( "" )
456
- print_yellow ( f"PUBLIC KEY INFO ( { 'testnet' if is_testnet else 'mainnet' } ) " )
490
+ if password :
491
+ print_green ( f"Passphrase: { password } " )
457
492
493
+ print_yellow (f"\n PUBLIC KEY INFO ({ 'testnet' if is_testnet else 'mainnet' } )" )
458
494
print_yellow ("Copy-paste this into Specter-Desktop:" )
459
495
print_green (
460
496
" [{}{}]{}" .format (
0 commit comments