Browse Source

add bip39 seeds for segwit wallets

master
Adam Gibson 9 years ago
parent
commit
95d7b7ef81
No known key found for this signature in database
GPG Key ID: B3AE09F1E9A3197A
  1. 2
      jmclient/jmclient/__init__.py
  2. 2
      jmclient/jmclient/configure.py
  3. 20
      jmclient/jmclient/wallet.py
  4. 85
      jmclient/jmclient/wallet_utils.py
  5. 2
      jmclient/setup.py

2
jmclient/jmclient/__init__.py

@ -18,7 +18,7 @@ from .slowaes import decryptData, encryptData
from .taker import Taker
from .wallet import (AbstractWallet, BitcoinCoreInterface, Wallet,
BitcoinCoreWallet, estimate_tx_fee, WalletError,
create_wallet_file, SegwitWallet)
create_wallet_file, SegwitWallet, Bip39Wallet)
from .configure import (load_program_config, jm_single, get_p2pk_vbyte,
get_network, jm_single, get_network, validate_address, get_irc_mchannels,
check_utxo_blacklist, get_blockchain_interface_instance, get_p2sh_vbyte,

2
jmclient/jmclient/configure.py

@ -137,6 +137,8 @@ unconfirm_timeout_sec = 90
confirm_timeout_hours = 6
[POLICY]
#Use segwit style wallets and transactions
segwit = true
# for dust sweeping, try merge_algorithm = gradual
# for more rapid dust sweeping, try merge_algorithm = greedy
# for most rapid dust sweeping, try merge_algorithm = greediest

20
jmclient/jmclient/wallet.py

@ -5,7 +5,7 @@ import pprint
import sys
import datetime
from decimal import Decimal
from mnemonic import Mnemonic
from ConfigParser import NoSectionError
from getpass import getpass
@ -153,7 +153,8 @@ class Wallet(AbstractWallet):
self.unspent = {}
self.spent_utxos = []
self.imported_privkeys = {}
self.seed = self.read_wallet_file_data(seedarg, pwd, wallet_dir=wallet_dir)
self.seed = self.entropy_to_seed(
self.read_wallet_file_data(seedarg, pwd, wallet_dir=wallet_dir))
if not self.seed:
raise WalletError("Failed to decrypt wallet")
if extend_mixdepth and len(self.index_cache) > max_mix_depth:
@ -172,6 +173,12 @@ class Wallet(AbstractWallet):
for i in range(self.max_mix_depth):
self.index.append([0, 0])
def entropy_to_seed(self, entropy):
"""for base/legacy wallet type, this is a passthrough.
for bip39 style wallets, this will convert from one to the other
"""
return entropy
def get_txtype(self):
"""Return string defining wallet type
for purposes of transaction size estimates
@ -364,10 +371,17 @@ class Wallet(AbstractWallet):
log.debug('get_utxos_by_mixdepth = \n' + pprint.pformat(mix_utxo_list))
return mix_utxo_list
class SegwitWallet(Wallet):
class Bip39Wallet(Wallet):
def entropy_to_seed(self, entropy):
self.entropy = entropy.decode('hex')
m = Mnemonic("english")
return m.to_seed(m.to_mnemonic(entropy)).encode('hex')
class SegwitWallet(Bip39Wallet):
def __init__(self, seedarg, pwd, max_mix_depth=2, gaplimit=6,
extend_mixdepth=False, storepassword=False, wallet_dir=None):
self.entropy = None
super(SegwitWallet, self).__init__(seedarg, pwd, max_mix_depth, gaplimit,
extend_mixdepth, storepassword,
wallet_dir=wallet_dir)

85
jmclient/jmclient/wallet_utils.py

@ -4,10 +4,11 @@ import os
import pprint
import sys
import datetime
from decimal import Decimal
import binascii
from mnemonic import Mnemonic
from optparse import OptionParser
from jmclient import (get_network, Wallet,
import getpass
from jmclient import (get_network, Wallet, Bip39Wallet,
encryptData, get_p2pk_vbyte, jm_single,
mn_decode, mn_encode, BitcoinCoreInterface,
JsonRpcError, sync_wallet, WalletError, SegwitWallet)
@ -323,27 +324,16 @@ def wallet_display(wallet, gaplimit, showprivkey, displayall=False):
walletview = WalletView("m/0", acctlist)
return walletview.serialize()
def wallet_generate_recover(method, walletspath, default_wallet_name='wallet.json'):
if method == 'generate':
seed = btc.sha256(os.urandom(64))[:32]
words = mn_encode(seed)
print('Write down this wallet recovery seed\n\n' + ' '.join(words) +
'\n')
elif method == 'recover':
words = raw_input('Input 12 word recovery seed: ')
words = words.split() # default for split is 1 or more whitespace chars
if len(words) != 12:
print('ERROR: Recovery seed phrase must be exactly 12 words.')
return False
seed = mn_decode(words)
print(seed)
password = getpass.getpass('Enter wallet encryption passphrase: ')
password2 = getpass.getpass('Reenter wallet encryption passphrase: ')
def get_password_check():
password = get_password('Enter wallet encryption passphrase: ')
password2 = get_password('Reenter wallet encryption passphrase: ')
if password != password2:
print('ERROR. Passwords did not match')
return False
return False, False
password_key = btc.bin_dbl_sha256(password)
encrypted_seed = encryptData(password_key, seed.decode('hex'))
return password, password_key
def persist_walletfile(walletspath, default_wallet_name, encrypted_seed):
timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
walletfile = json.dumps({'creator': 'joinmarket project',
'creation_time': timestamp,
@ -364,9 +354,55 @@ def wallet_generate_recover(method, walletspath, default_wallet_name='wallet.jso
print('saved to ' + walletname)
return True
def wallet_generate_recover_bip39(method, walletspath, default_wallet_name):
#using 128 bit entropy, 12 words, mnemonic module
m = Mnemonic("english")
if method == "generate":
words = m.generate()
print('Write down this wallet recovery seed\n\n' + words +'\n')
elif method == 'recover':
words = raw_input('Input 12 word recovery seed: ')
entropy = str(m.to_entropy(words))
password, password_key = get_password_check()
if not password:
return False
encrypted_entropy = encryptData(password_key, entropy)
return persist_walletfile(walletspath, default_wallet_name, encrypted_entropy)
def wallet_generate_recover(method, walletspath,
default_wallet_name='wallet.json'):
if jm_single().config.get("POLICY", "segwit") == "true":
return wallet_generate_recover_bip39(method, walletspath, default_wallet_name)
if method == 'generate':
seed = btc.sha256(os.urandom(64))[:32]
words = mn_encode(seed)
print('Write down this wallet recovery seed\n\n' + ' '.join(words) +
'\n')
elif method == 'recover':
words = raw_input('Input 12 word recovery seed: ')
words = words.split() # default for split is 1 or more whitespace chars
if len(words) != 12:
print('ERROR: Recovery seed phrase must be exactly 12 words.')
return False
seed = mn_decode(words)
print(seed)
password, password_key = get_password_check()
if not password:
return False
encrypted_seed = encryptData(password_key, seed.decode('hex'))
return persist_walletfile(walletspath, default_wallet_name, encrypted_seed)
def wallet_showseed(wallet):
if isinstance(wallet, Bip39Wallet):
if not wallet.entropy:
return "Entropy is not initialized."
m = Mnemonic("english")
return "Wallet recovery seed\n\n" + m.to_mnemonic(wallet.entropy) + "\n"
hexseed = wallet.seed
print("hexseed = " + hexseed)
if bip39:
m = Mnemonic("english")
words = mn_encode(hexseed)
return "Wallet recovery seed\n\n" + " ".join(words) + "\n"
@ -424,7 +460,8 @@ def wallet_tool_main(wallet_root_path):
"""
parser = get_wallettool_parser()
(options, args) = parser.parse_args()
walletclass = SegwitWallet if jm_single().config.get(
"POLICY", "segwit") == "true" else Wallet
# if the index_cache stored in wallet.json is longer than the default
# then set maxmixdepth to the length of index_cache
maxmixdepth_configured = True
@ -448,7 +485,7 @@ def wallet_tool_main(wallet_root_path):
seed = args[0]
method = ('display' if len(args) == 1 else args[1].lower())
if not os.path.exists(os.path.join(wallet_root_path, seed)):
wallet = SegwitWallet(seed, None, options.maxmixdepth,
wallet = walletclass(seed, None, options.maxmixdepth,
options.gaplimit, extend_mixdepth= not maxmixdepth_configured,
storepassword=(method == 'importprivkey'),
wallet_dir=wallet_root_path)
@ -456,7 +493,7 @@ def wallet_tool_main(wallet_root_path):
while True:
try:
pwd = get_password("Enter wallet decryption passphrase: ")
wallet = SegwitWallet(seed, pwd,
wallet = walletclass(seed, pwd,
options.maxmixdepth,
options.gaplimit,
extend_mixdepth=not maxmixdepth_configured,

2
jmclient/setup.py

@ -9,5 +9,5 @@ setup(name='joinmarketclient',
author_email='ekaggata@gmail.com',
license='GPL',
packages=['jmclient'],
install_requires=['joinmarketbase==0.2.2'],
install_requires=['joinmarketbase==0.2.2', 'mnemonic'],
zip_safe=False)

Loading…
Cancel
Save