seed v6
This commit is contained in:
parent
07bdd6c494
commit
31aaae8ed2
2
electrum
2
electrum
|
@ -262,7 +262,7 @@ if __name__ == '__main__':
|
|||
exit(1)
|
||||
# check password
|
||||
try:
|
||||
seed = wallet.decode_seed(password)
|
||||
seed = wallet.get_seed(password)
|
||||
except:
|
||||
print_msg("Error: This password does not decode this wallet.")
|
||||
exit(1)
|
||||
|
|
|
@ -717,7 +717,7 @@ def show_seed():
|
|||
password = None
|
||||
|
||||
try:
|
||||
seed = wallet.decode_seed(password)
|
||||
seed = wallet.get_seed(password)
|
||||
except:
|
||||
modal_dialog('error','incorrect password')
|
||||
return
|
||||
|
@ -733,7 +733,7 @@ def change_password_dialog():
|
|||
password = None
|
||||
|
||||
try:
|
||||
seed = wallet.decode_seed(password)
|
||||
wallet.get_seed(password)
|
||||
except:
|
||||
modal_dialog('error','incorrect password')
|
||||
return
|
||||
|
@ -748,7 +748,7 @@ def change_password_dialog():
|
|||
modal_dialog('error','passwords do not match')
|
||||
return
|
||||
|
||||
wallet.update_password(seed, password, new_password)
|
||||
wallet.update_password(password, new_password)
|
||||
if new_password:
|
||||
modal_dialog('Password updated','your wallet is encrypted')
|
||||
else:
|
||||
|
|
|
@ -69,7 +69,7 @@ def show_seed_dialog(wallet, password, parent):
|
|||
show_message("No seed")
|
||||
return
|
||||
try:
|
||||
seed = wallet.decode_seed(password)
|
||||
seed = wallet.get_seed(password)
|
||||
except:
|
||||
show_message("Incorrect password")
|
||||
return
|
||||
|
@ -435,7 +435,7 @@ def change_password_dialog(wallet, parent, icon):
|
|||
return
|
||||
|
||||
try:
|
||||
seed = wallet.decode_seed(password)
|
||||
wallet.get_seed(password)
|
||||
except:
|
||||
show_message("Incorrect password")
|
||||
return
|
||||
|
@ -444,7 +444,7 @@ def change_password_dialog(wallet, parent, icon):
|
|||
show_message("passwords do not match")
|
||||
return
|
||||
|
||||
wallet.update_password(seed, password, new_password)
|
||||
wallet.update_password(password, new_password)
|
||||
|
||||
if icon:
|
||||
if wallet.use_encryption:
|
||||
|
|
|
@ -89,10 +89,9 @@ class InstallWizard(QDialog):
|
|||
vbox = QVBoxLayout(self)
|
||||
if is_restore:
|
||||
msg = _("Please enter your wallet seed.") + "\n"
|
||||
msg += _("Your seed can be entered as a sequence of words, or as a hexadecimal string.")+ ' \n'
|
||||
else:
|
||||
msg = _("Your seed is important!") \
|
||||
+ "\n" + _("To make sure that you have properly saved your seed, please retype it here.") + ' '
|
||||
+ "\n" + _("To make sure that you have properly saved your seed, please retype it here.")
|
||||
|
||||
logo = QLabel()
|
||||
logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
|
||||
|
@ -119,15 +118,7 @@ class InstallWizard(QDialog):
|
|||
if not self.exec_():
|
||||
return
|
||||
|
||||
try:
|
||||
seed = str(seed_e.toPlainText())
|
||||
seed.decode('hex')
|
||||
except:
|
||||
try:
|
||||
seed = mnemonic.mn_decode( seed.split() )
|
||||
except:
|
||||
QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
|
||||
return
|
||||
seed = unicode(seed_e.toPlainText())
|
||||
|
||||
if not seed:
|
||||
QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
|
||||
|
@ -288,7 +279,12 @@ class InstallWizard(QDialog):
|
|||
seed = self.seed_dialog()
|
||||
if not seed:
|
||||
return
|
||||
wallet.init_seed(str(seed))
|
||||
try:
|
||||
wallet.init_seed(seed)
|
||||
except:
|
||||
QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
|
||||
return
|
||||
|
||||
wallet.save_seed()
|
||||
|
||||
elif action == 'watching':
|
||||
|
|
|
@ -1544,12 +1544,12 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
if self.wallet.seed:
|
||||
try:
|
||||
seed = self.wallet.decode_seed(password)
|
||||
mnemonic = self.wallet.get_mnemonic(password)
|
||||
except:
|
||||
QMessageBox.warning(self, _('Error'), _('Incorrect Password'), _('OK'))
|
||||
return
|
||||
from seed_dialog import SeedDialog
|
||||
d = SeedDialog(self, seed, self.wallet.imported_keys)
|
||||
d = SeedDialog(self, mnemonic, self.wallet.imported_keys)
|
||||
d.exec_()
|
||||
else:
|
||||
l = {}
|
||||
|
|
|
@ -84,7 +84,7 @@ def run_password_dialog(self, wallet, parent):
|
|||
new_password2 = unicode(self.conf_pw.text())
|
||||
|
||||
try:
|
||||
seed = wallet.decode_seed(password)
|
||||
wallet.get_seed(password)
|
||||
except:
|
||||
QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
|
||||
return
|
||||
|
@ -96,7 +96,7 @@ def run_password_dialog(self, wallet, parent):
|
|||
return
|
||||
|
||||
try:
|
||||
wallet.update_password(seed, password, new_password)
|
||||
wallet.update_password(password, new_password)
|
||||
except:
|
||||
QMessageBox.warning(parent, _('Error'), _('Failed to update password'), _('OK'))
|
||||
return
|
||||
|
|
|
@ -57,12 +57,11 @@ class PrivateKeysDialog(QDialog):
|
|||
|
||||
def make_seed_dialog(seed, imported_keys):
|
||||
|
||||
words = mnemonic.mn_encode(seed)
|
||||
brainwallet = ' '.join(words)
|
||||
words = seed.split()
|
||||
|
||||
label1 = QLabel(_("Your wallet generation seed is")+ ":")
|
||||
|
||||
seed_text = QTextEdit(brainwallet)
|
||||
seed_text = QTextEdit(seed)
|
||||
seed_text.setReadOnly(True)
|
||||
seed_text.setMaximumHeight(130)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
|
||||
import hashlib, base64, ecdsa, re
|
||||
import hmac
|
||||
from util import print_error
|
||||
|
||||
def rev_hex(s):
|
||||
|
@ -56,6 +57,8 @@ Hash = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest()
|
|||
hash_encode = lambda x: x[::-1].encode('hex')
|
||||
hash_decode = lambda x: x.decode('hex')[::-1]
|
||||
|
||||
hmac_sha_512 = lambda x,y: hmac.new(x, y, hashlib.sha512).digest()
|
||||
mnemonic_hash = lambda x: hmac_sha_512("Bitcoin mnemonic", x).encode('hex')
|
||||
|
||||
# pywallet openssl private key implementation
|
||||
|
||||
|
|
|
@ -215,9 +215,9 @@ class Commands:
|
|||
return self.network.get_servers()
|
||||
|
||||
def getseed(self):
|
||||
import mnemonic
|
||||
seed = self.wallet.decode_seed(self.password)
|
||||
return { "hex":seed, "mnemonic": ' '.join(mnemonic.mn_encode(seed)) }
|
||||
mnemonic = self.wallet.get_mnemonic(self.password)
|
||||
seed = self.wallet.get_seed(self.password)
|
||||
return { 'mnemonic':mnemonic, 'seed':seed, 'version':self.wallet.seed_version }
|
||||
|
||||
def importprivkey(self, sec):
|
||||
try:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
ELECTRUM_VERSION = "1.9" # version of the client package
|
||||
PROTOCOL_VERSION = '0.6' # protocol version requested
|
||||
SEED_VERSION = 5 # bump this every time the seed generation is modified
|
||||
SEED_VERSION = 6 # bump this every time the seed generation is modified
|
||||
SEED_PREFIX = '100' # the hash of a valid mnemonic seed must begin with this (12 bits)
|
||||
TRANSLATION_ID = 4127 # version of the wiki page
|
||||
|
|
101
lib/wallet.py
101
lib/wallet.py
|
@ -64,7 +64,7 @@ def pw_decode(s, password):
|
|||
|
||||
|
||||
|
||||
from version import ELECTRUM_VERSION, SEED_VERSION
|
||||
from version import *
|
||||
|
||||
|
||||
class WalletStorage:
|
||||
|
@ -176,8 +176,10 @@ class Wallet:
|
|||
|
||||
self.next_addresses = storage.get('next_addresses',{})
|
||||
|
||||
if self.seed_version < 4:
|
||||
raise ValueError("This wallet seed is deprecated.")
|
||||
if self.seed_version not in [4, 6]:
|
||||
msg = "This wallet seed is not supported."
|
||||
if self.seed_version in [5]: msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%self.seed_version
|
||||
raise ValueError(msg)
|
||||
|
||||
self.load_accounts()
|
||||
|
||||
|
@ -247,7 +249,7 @@ class Wallet:
|
|||
|
||||
def import_key(self, sec, password):
|
||||
# check password
|
||||
seed = self.decode_seed(password)
|
||||
seed = self.get_seed(password)
|
||||
try:
|
||||
address = address_from_private_key(sec)
|
||||
except:
|
||||
|
@ -269,10 +271,53 @@ class Wallet:
|
|||
self.storage.put('imported_keys', self.imported_keys, True)
|
||||
|
||||
|
||||
def make_seed(self):
|
||||
import mnemonic, ecdsa
|
||||
entropy = ecdsa.util.randrange( pow(2,160) )
|
||||
nonce = 0
|
||||
while True:
|
||||
ss = "%040x"%(entropy+nonce)
|
||||
s = hashlib.sha256(ss.decode('hex')).digest().encode('hex')
|
||||
# we keep only 13 words, that's approximately 139 bits of entropy
|
||||
words = mnemonic.mn_encode(s)[0:13]
|
||||
seed = ' '.join(words)
|
||||
if mnemonic_hash(seed)[0:3] == SEED_PREFIX:
|
||||
break # this removes 12 bits of entropy
|
||||
nonce += 1
|
||||
|
||||
return seed
|
||||
|
||||
|
||||
def init_seed(self, seed):
|
||||
if self.seed: raise BaseException("a seed exists")
|
||||
if self.seed:
|
||||
raise BaseException("a seed exists")
|
||||
|
||||
if not seed:
|
||||
seed = random_seed(128)
|
||||
self.seed = self.make_seed()
|
||||
self.seed_version = SEED_VERSION
|
||||
return
|
||||
|
||||
# find out what kind of wallet we are
|
||||
try:
|
||||
seed.decode('hex')
|
||||
self.seed_version = 4
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
words = seed.split()
|
||||
try:
|
||||
mnemonic.mn_decode(words)
|
||||
uses_electrum_words = True
|
||||
except:
|
||||
uses_electrum_words = False
|
||||
|
||||
if uses_electrum_words and len(words) != 13:
|
||||
self.seed_version = 4
|
||||
self.seed = mnemonic.mn_encode(seed)
|
||||
else:
|
||||
assert mnemonic_hash(seed)[0:3] == SEED_PREFIX
|
||||
self.seed_version = SEED_VERSION
|
||||
self.seed = seed
|
||||
|
||||
|
||||
|
@ -291,12 +336,12 @@ class Wallet:
|
|||
|
||||
def create_accounts(self):
|
||||
# create default account
|
||||
self.create_master_keys('1', self.seed)
|
||||
self.create_master_keys('1')
|
||||
self.create_account('1','Main account')
|
||||
|
||||
|
||||
def create_master_keys(self, account_type, seed):
|
||||
master_k, master_c, master_K, master_cK = bip32_init(self.seed)
|
||||
def create_master_keys(self, account_type):
|
||||
master_k, master_c, master_K, master_cK = bip32_init(self.get_seed(None))
|
||||
if account_type == '1':
|
||||
k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
|
||||
self.master_public_keys["m/0'/"] = (c0, K0, cK0)
|
||||
|
@ -339,7 +384,7 @@ class Wallet:
|
|||
|
||||
def deseed_root(self, seed, password):
|
||||
# for safety, we ask the user to enter their seed
|
||||
assert seed == self.decode_seed(password)
|
||||
assert seed == self.get_seed(password)
|
||||
self.seed = ''
|
||||
self.storage.put('seed', '', True)
|
||||
|
||||
|
@ -600,12 +645,26 @@ class Wallet:
|
|||
|
||||
|
||||
|
||||
def decode_seed(self, password):
|
||||
seed = pw_decode(self.seed, password)
|
||||
def get_seed(self, password):
|
||||
s = pw_decode(self.seed, password)
|
||||
if self.seed_version == 4:
|
||||
seed = s
|
||||
else:
|
||||
seed = mnemonic_hash(s)
|
||||
#todo: #self.sequences[0].check_seed(seed)
|
||||
return seed
|
||||
|
||||
|
||||
def get_mnemonic(self, password):
|
||||
import mnemonic
|
||||
s = pw_decode(self.seed, password)
|
||||
if self.seed_version == 4:
|
||||
return ' '.join(mnemonic.mn_encode(s))
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
|
||||
def get_private_key(self, address, password):
|
||||
out = []
|
||||
if address in self.imported_keys.keys():
|
||||
|
@ -613,7 +672,7 @@ class Wallet:
|
|||
else:
|
||||
account, sequence = self.get_address_index(address)
|
||||
if account == 0:
|
||||
seed = self.decode_seed(password)
|
||||
seed = self.get_seed(password)
|
||||
pk = self.accounts[account].get_private_key(seed, sequence)
|
||||
out.append(pk)
|
||||
return out
|
||||
|
@ -673,7 +732,7 @@ class Wallet:
|
|||
def signrawtransaction(self, tx, input_info, private_keys, password):
|
||||
|
||||
# check that the password is correct
|
||||
seed = self.decode_seed(password)
|
||||
seed = self.get_seed(password)
|
||||
|
||||
# add input info
|
||||
tx.add_input_info(input_info)
|
||||
|
@ -1291,10 +1350,11 @@ class Wallet:
|
|||
|
||||
|
||||
|
||||
def update_password(self, seed, old_password, new_password):
|
||||
def update_password(self, old_password, new_password):
|
||||
if new_password == '': new_password = None
|
||||
# this will throw an exception if unicode cannot be converted
|
||||
self.seed = pw_encode( seed, new_password)
|
||||
decoded = pw_decode(self.seed, old_password)
|
||||
self.seed = pw_encode( decoded, new_password)
|
||||
self.storage.put('seed', self.seed, True)
|
||||
self.use_encryption = (new_password != None)
|
||||
self.storage.put('use_encryption', self.use_encryption,True)
|
||||
|
@ -1485,15 +1545,10 @@ class Wallet:
|
|||
# wait until we are connected, because the user might have selected another server
|
||||
wait_for_network()
|
||||
|
||||
# try to restore old account
|
||||
self.create_old_account()
|
||||
wait_for_wallet()
|
||||
|
||||
if self.is_found():
|
||||
self.seed_version = 4
|
||||
self.storage.put('seed_version', self.seed_version, True)
|
||||
if self.seed_version == 4:
|
||||
self.create_old_account()
|
||||
else:
|
||||
self.accounts.pop(0)
|
||||
self.create_accounts()
|
||||
wait_for_wallet()
|
||||
|
||||
|
|
Loading…
Reference in New Issue