Skip to content

Commit

Permalink
Add dice roll capability to multiwallet
Browse files Browse the repository at this point in the history
  • Loading branch information
tdb3 committed Feb 20, 2024
1 parent 6de0630 commit 30e579f
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 1 deletion.
80 changes: 79 additions & 1 deletion multiwallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
DEFAULT_P2WSH_PATH,
)
from buidl.libsec_status import is_libsec_enabled
from buidl.mnemonic import BIP39
from buidl.mnemonic import BIP39, dice_rolls_to_mnemonic
from buidl.shamir import ShareSet
from buidl.psbt import MixedNetwork, PSBT

Expand Down Expand Up @@ -246,6 +246,35 @@ def _get_bip39_firstwords():
return fw


#####################################################################
# Dice rolls
#####################################################################


def _get_num_words():
num_words = _get_int(
prompt="How many mnemonic words should be generated?",
default=24,
minimum=12,
maximum=24,
)
if num_words not in (12, 15, 18, 21, 24):
raise ValueError(
"Provided number of mnemonic words must be 12, 15, 18, 21, or 24 words"
)
return num_words


def _get_dice_values():
dice_vals = input(
blue_fg(
"Enter the numbers from the 6-sided dice rolls"
'\n(e.g., "14625363...", >= 100 values recommended): '
)
).strip()
return dice_vals


#####################################################################
# PSBT Signer
#####################################################################
Expand Down Expand Up @@ -487,6 +516,55 @@ def do_generate_seed(self, arg):
print_yellow("Copy-paste this into Specter-Desktop:")
print_green(key_record)

def do_generate_seed_from_dice(self, arg):
"""Calculate bitcoin public and private key information from BIP39 words created from dice rolls"""

blue_fg("Generates a mnemonic from 6-sided dice rolls.")
num_words = _get_num_words()
dice_vals = _get_dice_values()

if self.ADVANCED_MODE:
password = _get_password()
else:
password = ""

if _get_bool(prompt="Use Mainnet?", default=False):
network = "mainnet"
else:
network = "testnet"

if self.ADVANCED_MODE:
path_to_use = _get_path(network=network)
use_slip132_version_byte = _get_bool(
prompt="Encode with SLIP132 version byte?", default=True
)
else:
path_to_use = None # buidl will use default path
use_slip132_version_byte = True

words = dice_rolls_to_mnemonic(dice_vals, num_words)
hd_priv = HDPrivateKey.from_mnemonic(
mnemonic=words,
password=password.encode(),
network=network,
)

key_record = hd_priv.generate_p2wsh_key_record(
bip32_path=path_to_use, use_slip132_version_byte=use_slip132_version_byte
)

print(yellow_fg("SECRET INFO") + red_fg(" (guard this VERY carefully)"))
print_green(
f"Dice rolls used: {dice_vals}"
f"\nFull ({len(words.split())} word) mnemonic (including last word): {words}"
)
if password:
print_green(f"Passphrase: {password}")

print_yellow(f"\nPUBLIC KEY INFO ({network})")
print_yellow("Copy-paste this into Specter-Desktop:")
print_green(key_record)

def do_create_output_descriptors(self, arg):
"""Combine m-of-n public key records into a multisig output descriptor (account map)"""

Expand Down
53 changes: 53 additions & 0 deletions test_multiwallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,59 @@ def test_seedpicker_basic(self):
self.expect("Last word: bacon")
self.expect(expected_key_record)

def test_dice_basic(self):
dice_tests = [ # num_words, dice_rolls, is_mainnet, expected_mnemonic_str, expected_key_record
[
"24",
"123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456",
"Y",
"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",
"[cd34af7b/48h/0h/0h/2h]Zpub75DH7vGKCEESq3UW3cL6fQe2VuF2LgZsteVsMvvjC2as9f2wuR2UxhYH9WV5xeNvgeHPhaZQuniaCxc6TP1tqMPNjMbsfnLkDf1S3dXAuHj",
],
[
"24",
"123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456",
"N",
"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",
"[cd34af7b/48h/1h/0h/2h]Vpub5mBykPsR2S12QVvWiC9eXr7zsLJHNfLbdjF8k5BE85Tk5moEH2ZuBGfn5XZmePouE62PG76GGR4Bu3dD9zs3KfGWCjA8hQYaU9WMqX1Ywgc",
],
[
"12",
"12345612345612345612345612345612345612345612345612",
"Y",
"Full (12 word) mnemonic (including last word): unveil nice picture region tragic fault cream strike tourist control recipe tourist",
"85480bc5/48h/0h/0h/2h]Zpub75si7yux5xBVmPDFifgnh8WDUFqYLD9u1HNZXJ7zB14yBJCV1dXkLWokUkSXiYB3zaivyXnw57vFFXoPyXjEjhczEFHk8GtaU3s98wBxebV",
],
[
"12",
"12345612345612345612345612345612345612345612345612",
"N",
"Full (12 word) mnemonic (including last word): unveil nice picture region tragic fault cream strike tourist control recipe tourist",
"85480bc5/48h/1h/0h/2h]Vpub5nVGWRi82pLmKvPbos31HGTJ6vtrUHB4p8h54qVBRmmph8KwpGmYwRAyz4GLMSf7TETRXZKm5u4ybtY2u7KYFevt5tqgYBZynJptbfa36QA",
],
]
for (
num_words,
dice_rolls,
is_mainnet,
expected_mnemonic_str,
expected_key_record,
) in dice_tests:
self.child.sendline("generate_seed_from_dice")

self.expect("How many mnemonic words should be generated?")
self.child.sendline(num_words)

self.expect("100 values recommended")
self.child.sendline(dice_rolls)

self.expect("Use Mainnet?")
self.child.sendline(is_mainnet)

self.expect("Dice rolls used: " + dice_rolls)
self.expect(expected_mnemonic_str)
self.expect(expected_key_record)

def test_create_output_descriptors_blinded(self):
# Blinded example from https://github.com/mflaxman/blind-xpub/blob/90af581695ef4ab1b7c40324c4cd7f2ce70e3403/README.md#create-output-descriptors

Expand Down

0 comments on commit 30e579f

Please sign in to comment.