Skip to content

Commit 30e579f

Browse files
committed
Add dice roll capability to multiwallet
1 parent 6de0630 commit 30e579f

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

multiwallet.py

+79-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
DEFAULT_P2WSH_PATH,
2222
)
2323
from buidl.libsec_status import is_libsec_enabled
24-
from buidl.mnemonic import BIP39
24+
from buidl.mnemonic import BIP39, dice_rolls_to_mnemonic
2525
from buidl.shamir import ShareSet
2626
from buidl.psbt import MixedNetwork, PSBT
2727

@@ -246,6 +246,35 @@ def _get_bip39_firstwords():
246246
return fw
247247

248248

249+
#####################################################################
250+
# Dice rolls
251+
#####################################################################
252+
253+
254+
def _get_num_words():
255+
num_words = _get_int(
256+
prompt="How many mnemonic words should be generated?",
257+
default=24,
258+
minimum=12,
259+
maximum=24,
260+
)
261+
if num_words not in (12, 15, 18, 21, 24):
262+
raise ValueError(
263+
"Provided number of mnemonic words must be 12, 15, 18, 21, or 24 words"
264+
)
265+
return num_words
266+
267+
268+
def _get_dice_values():
269+
dice_vals = input(
270+
blue_fg(
271+
"Enter the numbers from the 6-sided dice rolls"
272+
'\n(e.g., "14625363...", >= 100 values recommended): '
273+
)
274+
).strip()
275+
return dice_vals
276+
277+
249278
#####################################################################
250279
# PSBT Signer
251280
#####################################################################
@@ -487,6 +516,55 @@ def do_generate_seed(self, arg):
487516
print_yellow("Copy-paste this into Specter-Desktop:")
488517
print_green(key_record)
489518

519+
def do_generate_seed_from_dice(self, arg):
520+
"""Calculate bitcoin public and private key information from BIP39 words created from dice rolls"""
521+
522+
blue_fg("Generates a mnemonic from 6-sided dice rolls.")
523+
num_words = _get_num_words()
524+
dice_vals = _get_dice_values()
525+
526+
if self.ADVANCED_MODE:
527+
password = _get_password()
528+
else:
529+
password = ""
530+
531+
if _get_bool(prompt="Use Mainnet?", default=False):
532+
network = "mainnet"
533+
else:
534+
network = "testnet"
535+
536+
if self.ADVANCED_MODE:
537+
path_to_use = _get_path(network=network)
538+
use_slip132_version_byte = _get_bool(
539+
prompt="Encode with SLIP132 version byte?", default=True
540+
)
541+
else:
542+
path_to_use = None # buidl will use default path
543+
use_slip132_version_byte = True
544+
545+
words = dice_rolls_to_mnemonic(dice_vals, num_words)
546+
hd_priv = HDPrivateKey.from_mnemonic(
547+
mnemonic=words,
548+
password=password.encode(),
549+
network=network,
550+
)
551+
552+
key_record = hd_priv.generate_p2wsh_key_record(
553+
bip32_path=path_to_use, use_slip132_version_byte=use_slip132_version_byte
554+
)
555+
556+
print(yellow_fg("SECRET INFO") + red_fg(" (guard this VERY carefully)"))
557+
print_green(
558+
f"Dice rolls used: {dice_vals}"
559+
f"\nFull ({len(words.split())} word) mnemonic (including last word): {words}"
560+
)
561+
if password:
562+
print_green(f"Passphrase: {password}")
563+
564+
print_yellow(f"\nPUBLIC KEY INFO ({network})")
565+
print_yellow("Copy-paste this into Specter-Desktop:")
566+
print_green(key_record)
567+
490568
def do_create_output_descriptors(self, arg):
491569
"""Combine m-of-n public key records into a multisig output descriptor (account map)"""
492570

test_multiwallet.py

+53
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,59 @@ def test_seedpicker_basic(self):
7272
self.expect("Last word: bacon")
7373
self.expect(expected_key_record)
7474

75+
def test_dice_basic(self):
76+
dice_tests = [ # num_words, dice_rolls, is_mainnet, expected_mnemonic_str, expected_key_record
77+
[
78+
"24",
79+
"123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456",
80+
"Y",
81+
"Full (24 word) mnemonic (including last word): more matter caught bind tip twin indicate visa rifle angle defense lizard stock cave cradle injury always mule photo horse range opinion affair garlic",
82+
"[cd34af7b/48h/0h/0h/2h]Zpub75DH7vGKCEESq3UW3cL6fQe2VuF2LgZsteVsMvvjC2as9f2wuR2UxhYH9WV5xeNvgeHPhaZQuniaCxc6TP1tqMPNjMbsfnLkDf1S3dXAuHj",
83+
],
84+
[
85+
"24",
86+
"123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456",
87+
"N",
88+
"Full (24 word) mnemonic (including last word): more matter caught bind tip twin indicate visa rifle angle defense lizard stock cave cradle injury always mule photo horse range opinion affair garlic",
89+
"[cd34af7b/48h/1h/0h/2h]Vpub5mBykPsR2S12QVvWiC9eXr7zsLJHNfLbdjF8k5BE85Tk5moEH2ZuBGfn5XZmePouE62PG76GGR4Bu3dD9zs3KfGWCjA8hQYaU9WMqX1Ywgc",
90+
],
91+
[
92+
"12",
93+
"12345612345612345612345612345612345612345612345612",
94+
"Y",
95+
"Full (12 word) mnemonic (including last word): unveil nice picture region tragic fault cream strike tourist control recipe tourist",
96+
"85480bc5/48h/0h/0h/2h]Zpub75si7yux5xBVmPDFifgnh8WDUFqYLD9u1HNZXJ7zB14yBJCV1dXkLWokUkSXiYB3zaivyXnw57vFFXoPyXjEjhczEFHk8GtaU3s98wBxebV",
97+
],
98+
[
99+
"12",
100+
"12345612345612345612345612345612345612345612345612",
101+
"N",
102+
"Full (12 word) mnemonic (including last word): unveil nice picture region tragic fault cream strike tourist control recipe tourist",
103+
"85480bc5/48h/1h/0h/2h]Vpub5nVGWRi82pLmKvPbos31HGTJ6vtrUHB4p8h54qVBRmmph8KwpGmYwRAyz4GLMSf7TETRXZKm5u4ybtY2u7KYFevt5tqgYBZynJptbfa36QA",
104+
],
105+
]
106+
for (
107+
num_words,
108+
dice_rolls,
109+
is_mainnet,
110+
expected_mnemonic_str,
111+
expected_key_record,
112+
) in dice_tests:
113+
self.child.sendline("generate_seed_from_dice")
114+
115+
self.expect("How many mnemonic words should be generated?")
116+
self.child.sendline(num_words)
117+
118+
self.expect("100 values recommended")
119+
self.child.sendline(dice_rolls)
120+
121+
self.expect("Use Mainnet?")
122+
self.child.sendline(is_mainnet)
123+
124+
self.expect("Dice rolls used: " + dice_rolls)
125+
self.expect(expected_mnemonic_str)
126+
self.expect(expected_key_record)
127+
75128
def test_create_output_descriptors_blinded(self):
76129
# Blinded example from https://github.com/mflaxman/blind-xpub/blob/90af581695ef4ab1b7c40324c4cd7f2ce70e3403/README.md#create-output-descriptors
77130

0 commit comments

Comments
 (0)