upgrade to type 2 wallet
This commit is contained in:
parent
deaa5745a4
commit
ca32d29a9c
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
import sys, base64, os, re, hashlib, socket, getpass, copy, operator, ast
|
import sys, base64, os, re, hashlib, socket, getpass, copy, operator, ast
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from ecdsa.util import string_to_number
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import ecdsa
|
import ecdsa
|
||||||
|
@ -215,8 +216,7 @@ class InvalidPassword(Exception):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SEED_VERSION = 3 # bump this everytime the seed generation is modified
|
from version import ELECTRUM_VERSION, SEED_VERSION
|
||||||
from version import ELECTRUM_VERSION
|
|
||||||
|
|
||||||
|
|
||||||
class Wallet:
|
class Wallet:
|
||||||
|
@ -230,13 +230,13 @@ class Wallet:
|
||||||
self.port = 50000
|
self.port = 50000
|
||||||
self.fee = 50000
|
self.fee = 50000
|
||||||
self.servers = ['ecdsa.org','electrum.novit.ro'] # list of default servers
|
self.servers = ['ecdsa.org','electrum.novit.ro'] # list of default servers
|
||||||
|
self.master_public_key = None
|
||||||
|
|
||||||
# saved fields
|
# saved fields
|
||||||
self.use_encryption = False
|
self.use_encryption = False
|
||||||
self.addresses = []
|
self.addresses = [] # receiving addresses visible for user
|
||||||
|
self.change_addresses = [] # addresses used as change
|
||||||
self.seed = '' # encrypted
|
self.seed = '' # encrypted
|
||||||
self.private_keys = repr([]) # encrypted
|
|
||||||
self.change_addresses = [] # index of addresses used as change
|
|
||||||
self.status = {} # current status of addresses
|
self.status = {} # current status of addresses
|
||||||
self.history = {}
|
self.history = {}
|
||||||
self.labels = {} # labels for addresses and transactions
|
self.labels = {} # labels for addresses and transactions
|
||||||
|
@ -271,16 +271,22 @@ class Wallet:
|
||||||
|
|
||||||
def new_seed(self, password):
|
def new_seed(self, password):
|
||||||
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
|
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
|
||||||
|
self.init_mpk(seed)
|
||||||
|
# encrypt
|
||||||
self.seed = wallet.pw_encode( seed, password )
|
self.seed = wallet.pw_encode( seed, password )
|
||||||
|
|
||||||
|
def init_mpk(self,seed):
|
||||||
|
# public key
|
||||||
|
curve = SECP256k1
|
||||||
|
secexp = self.stretch_key(seed)
|
||||||
|
master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
|
||||||
|
self.master_public_key = master_private_key.get_verifying_key().to_string()
|
||||||
|
|
||||||
def is_mine(self, address):
|
def is_mine(self, address):
|
||||||
return address in self.addresses
|
return address in self.addresses or address in self.change_addresses
|
||||||
|
|
||||||
def is_change(self, address):
|
def is_change(self, address):
|
||||||
if not self.is_mine(address):
|
return address in self.change_addresses
|
||||||
return False
|
|
||||||
k = self.addresses.index(address)
|
|
||||||
return k in self.change_addresses
|
|
||||||
|
|
||||||
def is_valid(self,addr):
|
def is_valid(self,addr):
|
||||||
ADDRESS_RE = re.compile('[1-9A-HJ-NP-Za-km-z]{26,}\\Z')
|
ADDRESS_RE = re.compile('[1-9A-HJ-NP-Za-km-z]{26,}\\Z')
|
||||||
|
@ -288,43 +294,61 @@ class Wallet:
|
||||||
h = bc_address_to_hash_160(addr)
|
h = bc_address_to_hash_160(addr)
|
||||||
return addr == hash_160_to_bc_address(h)
|
return addr == hash_160_to_bc_address(h)
|
||||||
|
|
||||||
def create_new_address(self, for_change, password):
|
def stretch_key(self,seed):
|
||||||
seed = self.pw_decode( self.seed, password)
|
|
||||||
# strenghtening
|
|
||||||
oldseed = seed
|
oldseed = seed
|
||||||
for i in range(100000):
|
for i in range(100000):
|
||||||
seed = hashlib.sha512(seed + oldseed).digest()
|
seed = hashlib.sha256(seed + oldseed).digest()
|
||||||
i = len( self.addresses ) - len(self.change_addresses) if not for_change else len(self.change_addresses)
|
return string_to_number( seed )
|
||||||
seed = Hash( "%d:%d:"%(i,for_change) + seed )
|
|
||||||
|
def get_sequence(self,n,for_change):
|
||||||
|
return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key ) )
|
||||||
|
|
||||||
|
def get_private_key2(self, address, password):
|
||||||
|
""" Privatekey(type,n) = Master_private_key + H(n|S|type) """
|
||||||
|
if address in self.addresses:
|
||||||
|
n = self.addresses.index(address)
|
||||||
|
for_change = False
|
||||||
|
elif address in self.change_addresses:
|
||||||
|
n = self.change_addresses.index(address)
|
||||||
|
for_change = True
|
||||||
|
else:
|
||||||
|
raise BaseException("unknown address")
|
||||||
|
|
||||||
|
seed = self.pw_decode( self.seed, password)
|
||||||
|
secexp = self.stretch_key(seed)
|
||||||
order = generator_secp256k1.order()
|
order = generator_secp256k1.order()
|
||||||
secexp = ecdsa.util.randrange_from_seed__trytryagain( seed, order )
|
privkey_number = ( secexp + self.get_sequence(n,for_change) ) % order
|
||||||
secret = SecretToASecret( ('%064x' % secexp).decode('hex') )
|
private_key = ecdsa.SigningKey.from_secret_exponent( privkey_number, curve = SECP256k1 )
|
||||||
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
|
# sanity check
|
||||||
public_key = private_key.get_verifying_key()
|
#public_key = private_key.get_verifying_key()
|
||||||
address = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() )
|
#assert address == public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() )
|
||||||
try:
|
return private_key
|
||||||
private_keys = ast.literal_eval( self.pw_decode( self.private_keys, password) )
|
|
||||||
private_keys.append(secret)
|
|
||||||
except:
|
def create_new_address2(self, for_change):
|
||||||
raise InvalidPassword("")
|
""" Publickey(type,n) = Master_public_key + H(n|S|type)*point """
|
||||||
self.private_keys = self.pw_encode( repr(private_keys), password)
|
curve = SECP256k1
|
||||||
|
n = len(self.change_addresses) if for_change else len(self.addresses)
|
||||||
|
z = self.get_sequence(n,for_change)
|
||||||
|
master_public_key = ecdsa.VerifyingKey.from_string( self.master_public_key, curve = SECP256k1 )
|
||||||
|
pubkey_point = master_public_key.pubkey.point + z*curve.generator
|
||||||
|
public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 )
|
||||||
|
address = public_key_to_bc_address( '04'.decode('hex') + public_key2.to_string() )
|
||||||
|
if for_change:
|
||||||
|
self.change_addresses.append(address)
|
||||||
|
else:
|
||||||
self.addresses.append(address)
|
self.addresses.append(address)
|
||||||
if for_change: self.change_addresses.append( len(self.addresses) - 1 )
|
|
||||||
self.history[address] = []
|
|
||||||
self.status[address] = None
|
|
||||||
self.save()
|
self.save()
|
||||||
return address
|
return address
|
||||||
|
|
||||||
|
def recover(self):
|
||||||
def recover(self, password):
|
|
||||||
seed = self.pw_decode( self.seed, password)
|
|
||||||
# todo: recover receiving addresses from tx
|
# todo: recover receiving addresses from tx
|
||||||
is_found = False
|
is_found = False
|
||||||
while True:
|
while True:
|
||||||
addr = self.create_new_address(True, password)
|
addr = self.create_new_address2(True)
|
||||||
self.history[addr] = h = self.retrieve_history(addr)
|
self.history[addr] = h = self.retrieve_history(addr)
|
||||||
self.status[addr] = h[-1]['blk_hash'] if h else None
|
self.status[addr] = h[-1]['blk_hash'] if h else None
|
||||||
#print "recovering", addr
|
print "recovering", addr
|
||||||
if self.status[addr] is not None:
|
if self.status[addr] is not None:
|
||||||
is_found = True
|
is_found = True
|
||||||
else:
|
else:
|
||||||
|
@ -332,10 +356,10 @@ class Wallet:
|
||||||
|
|
||||||
num_gap = 0
|
num_gap = 0
|
||||||
while True:
|
while True:
|
||||||
addr = self.create_new_address(False, password)
|
addr = self.create_new_address2(False)
|
||||||
self.history[addr] = h = self.retrieve_history(addr)
|
self.history[addr] = h = self.retrieve_history(addr)
|
||||||
self.status[addr] = h[-1]['blk_hash'] if h else None
|
self.status[addr] = h[-1]['blk_hash'] if h else None
|
||||||
#print "recovering", addr
|
print "recovering", addr
|
||||||
if self.status[addr] is None:
|
if self.status[addr] is None:
|
||||||
num_gap += 1
|
num_gap += 1
|
||||||
if num_gap == self.gap_limit: break
|
if num_gap == self.gap_limit: break
|
||||||
|
@ -345,12 +369,9 @@ class Wallet:
|
||||||
|
|
||||||
if not is_found: return False
|
if not is_found: return False
|
||||||
|
|
||||||
# remove limit-1 addresses. [ this is ok, because change addresses are at the beginning of the list]
|
# remove limit-1 addresses.
|
||||||
n = self.gap_limit
|
n = self.gap_limit
|
||||||
self.addresses = self.addresses[:-n]
|
self.addresses = self.addresses[:-n]
|
||||||
private_keys = ast.literal_eval( self.pw_decode( self.private_keys, password))
|
|
||||||
private_keys = private_keys[:-n]
|
|
||||||
self.private_keys = self.pw_encode( repr(private_keys), password)
|
|
||||||
|
|
||||||
# history and addressbook
|
# history and addressbook
|
||||||
self.update_tx_history()
|
self.update_tx_history()
|
||||||
|
@ -364,12 +385,24 @@ class Wallet:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
s = repr( (self.seed_version, self.use_encryption, self.fee, self.host, self.port, self.blocks,
|
s = {
|
||||||
self.seed, self.addresses, self.private_keys,
|
'seed_version':self.seed_version,
|
||||||
self.change_addresses, self.status, self.history,
|
'use_encryption':self.use_encryption,
|
||||||
self.labels, self.addressbook) )
|
'master_public_key': self.master_public_key,
|
||||||
|
'fee':self.fee,
|
||||||
|
'host':self.host,
|
||||||
|
'port':self.port,
|
||||||
|
'blocks':self.blocks,
|
||||||
|
'seed':self.seed,
|
||||||
|
'addresses':self.addresses,
|
||||||
|
'change_addresses':self.change_addresses,
|
||||||
|
'status':self.status,
|
||||||
|
'history':self.history,
|
||||||
|
'labels':self.labels,
|
||||||
|
'contacts':self.addressbook
|
||||||
|
}
|
||||||
f = open(self.path,"w")
|
f = open(self.path,"w")
|
||||||
f.write(s)
|
f.write( repr(s) )
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
|
@ -380,32 +413,41 @@ class Wallet:
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
sequence = ast.literal_eval( data )
|
d = ast.literal_eval( data )
|
||||||
(self.seed_version, self.use_encryption, self.fee, self.host, self.port, self.blocks,
|
self.seed_version = d.get('seed_version')
|
||||||
self.seed, self.addresses, self.private_keys,
|
self.master_public_key = d.get('master_public_key')
|
||||||
self.change_addresses, self.status, self.history,
|
self.use_encryption = d.get('use_encryption')
|
||||||
self.labels, self.addressbook) = sequence
|
self.fee = int( d.get('fee') )
|
||||||
self.fee = int(self.fee)
|
self.host = d.get('host')
|
||||||
|
self.port = d.get('port')
|
||||||
|
self.blocks = d.get('blocks')
|
||||||
|
self.seed = d.get('seed')
|
||||||
|
self.addresses = d.get('addresses')
|
||||||
|
self.change_addresses = d.get('change_addresses')
|
||||||
|
self.status = d.get('status')
|
||||||
|
self.history = d.get('history')
|
||||||
|
self.labels = d.get('labels')
|
||||||
|
self.addressbook = d.get('contacts')
|
||||||
except:
|
except:
|
||||||
# it is safer to exit immediately
|
raise BaseException("Error; could not parse wallet. If this is an old wallet format, please use upgrade.py.",0)
|
||||||
print "Error; could not parse wallet."
|
|
||||||
exit(1)
|
|
||||||
if self.seed_version != SEED_VERSION:
|
if self.seed_version != SEED_VERSION:
|
||||||
raise BaseException("Seed version mismatch.\nPlease move your balance to a new wallet.\nSee the release notes for more information.")
|
raise BaseException("""Seed version mismatch: your wallet seed is deprecated.
|
||||||
|
Please create a new wallet, and send your coins to the new wallet.
|
||||||
|
We apologize for the inconvenience. We try to keep this kind of upgrades as rare as possible.
|
||||||
|
See the release notes for more information.""",1)
|
||||||
|
|
||||||
|
|
||||||
self.update_tx_history()
|
self.update_tx_history()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_new_address(self, password):
|
def get_new_address(self):
|
||||||
n = 0
|
n = 0
|
||||||
for addr in self.addresses[-self.gap_limit:]:
|
for addr in self.addresses[-self.gap_limit:]:
|
||||||
if self.history[addr] == []:
|
if not self.history.get(addr):
|
||||||
n = n + 1
|
n = n + 1
|
||||||
if n < self.gap_limit:
|
if n < self.gap_limit:
|
||||||
try:
|
new_address = self.create_new_address2(False)
|
||||||
new_address = self.create_new_address(False, password)
|
self.history[new_address] = [] #get from server
|
||||||
except InvalidPassword:
|
|
||||||
return False, "wrong password"
|
|
||||||
self.save()
|
|
||||||
return True, new_address
|
return True, new_address
|
||||||
else:
|
else:
|
||||||
return False, "The last %d addresses in your list have never been used. You should use them first, or increase the allowed gap size in your preferences. "%self.gap_limit
|
return False, "The last %d addresses in your list have never been used. You should use them first, or increase the allowed gap size in your preferences. "%self.gap_limit
|
||||||
|
@ -516,19 +558,17 @@ class Wallet:
|
||||||
inputs = []
|
inputs = []
|
||||||
return inputs, total, fee
|
return inputs, total, fee
|
||||||
|
|
||||||
def choose_tx_outputs( self, to_addr, amount, fee, total, password ):
|
def choose_tx_outputs( self, to_addr, amount, fee, total ):
|
||||||
outputs = [ (to_addr, amount) ]
|
outputs = [ (to_addr, amount) ]
|
||||||
change_amount = total - ( amount + fee )
|
change_amount = total - ( amount + fee )
|
||||||
if change_amount != 0:
|
if change_amount != 0:
|
||||||
# first look for unused change addresses
|
# first look for unused change addresses
|
||||||
for addr in self.addresses:
|
for addr in self.change_addresses:
|
||||||
i = self.addresses.index(addr)
|
|
||||||
if i not in self.change_addresses: continue
|
|
||||||
if self.history.get(addr): continue
|
if self.history.get(addr): continue
|
||||||
change_address = addr
|
change_address = addr
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
change_address = self.create_new_address(True, password)
|
change_address = self.create_new_address2(True)
|
||||||
print "new change address", change_address
|
print "new change address", change_address
|
||||||
outputs.append( (change_address, change_amount) )
|
outputs.append( (change_address, change_amount) )
|
||||||
return outputs
|
return outputs
|
||||||
|
@ -537,7 +577,7 @@ class Wallet:
|
||||||
s_inputs = []
|
s_inputs = []
|
||||||
for i in range(len(inputs)):
|
for i in range(len(inputs)):
|
||||||
addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
|
addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
|
||||||
private_key = self.get_private_key(addr, password)
|
private_key = self.get_private_key2(addr, password)
|
||||||
public_key = private_key.get_verifying_key()
|
public_key = private_key.get_verifying_key()
|
||||||
pubkey = public_key.to_string()
|
pubkey = public_key.to_string()
|
||||||
tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
|
tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
|
||||||
|
@ -556,24 +596,15 @@ class Wallet:
|
||||||
def pw_decode(self, s, password):
|
def pw_decode(self, s, password):
|
||||||
if password:
|
if password:
|
||||||
secret = Hash(password)
|
secret = Hash(password)
|
||||||
return DecodeAES(secret, s)
|
d = DecodeAES(secret, s)
|
||||||
|
try:
|
||||||
|
d.decode('hex')
|
||||||
|
except:
|
||||||
|
raise InvalidPassword()
|
||||||
|
return d
|
||||||
else:
|
else:
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def get_private_key( self, addr, password ):
|
|
||||||
try:
|
|
||||||
private_keys = ast.literal_eval( self.pw_decode( self.private_keys, password ) )
|
|
||||||
except:
|
|
||||||
raise InvalidPassword("")
|
|
||||||
k = self.addresses.index(addr)
|
|
||||||
secret = private_keys[k]
|
|
||||||
b = ASecretToSecret(secret)
|
|
||||||
secexp = int( b.encode('hex'), 16)
|
|
||||||
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve=SECP256k1 )
|
|
||||||
public_key = private_key.get_verifying_key()
|
|
||||||
assert addr == public_key_to_bc_address( chr(4) + public_key.to_string() )
|
|
||||||
return private_key
|
|
||||||
|
|
||||||
def get_tx_history(self):
|
def get_tx_history(self):
|
||||||
lines = self.tx_history.values()
|
lines = self.tx_history.values()
|
||||||
lines = sorted(lines, key=operator.itemgetter("nTime"))
|
lines = sorted(lines, key=operator.itemgetter("nTime"))
|
||||||
|
@ -623,7 +654,7 @@ class Wallet:
|
||||||
inputs, total, fee = wallet.choose_tx_inputs( amount, fee )
|
inputs, total, fee = wallet.choose_tx_inputs( amount, fee )
|
||||||
if not inputs: return False, "Not enough funds %d %d"%(total, fee)
|
if not inputs: return False, "Not enough funds %d %d"%(total, fee)
|
||||||
try:
|
try:
|
||||||
outputs = wallet.choose_tx_outputs( to_address, amount, fee, total, password )
|
outputs = wallet.choose_tx_outputs( to_address, amount, fee, total )
|
||||||
s_inputs = wallet.sign_inputs( inputs, outputs, password )
|
s_inputs = wallet.sign_inputs( inputs, outputs, password )
|
||||||
except InvalidPassword:
|
except InvalidPassword:
|
||||||
return False, "Wrong password"
|
return False, "Wrong password"
|
||||||
|
@ -648,7 +679,7 @@ class Wallet:
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed']
|
known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','t2']
|
||||||
|
|
||||||
usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands))
|
usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands))
|
||||||
|
|
||||||
|
@ -713,7 +744,7 @@ if __name__ == '__main__':
|
||||||
gap = raw_input("gap limit (default 5):")
|
gap = raw_input("gap limit (default 5):")
|
||||||
if gap: wallet.gap_limit = int(gap)
|
if gap: wallet.gap_limit = int(gap)
|
||||||
print "recovering wallet..."
|
print "recovering wallet..."
|
||||||
r = wallet.recover(password)
|
r = wallet.recover()
|
||||||
if r:
|
if r:
|
||||||
print "recovery successful"
|
print "recovery successful"
|
||||||
wallet.save()
|
wallet.save()
|
||||||
|
@ -724,7 +755,7 @@ if __name__ == '__main__':
|
||||||
print "Your seed is", wallet.seed
|
print "Your seed is", wallet.seed
|
||||||
print "Please store it safely"
|
print "Please store it safely"
|
||||||
# generate first key
|
# generate first key
|
||||||
wallet.create_new_address(False, None)
|
wallet.create_new_address2(False)
|
||||||
|
|
||||||
# check syntax
|
# check syntax
|
||||||
if cmd in ['payto', 'mktx']:
|
if cmd in ['payto', 'mktx']:
|
||||||
|
@ -744,7 +775,7 @@ if __name__ == '__main__':
|
||||||
wallet.save()
|
wallet.save()
|
||||||
|
|
||||||
# commands needing password
|
# commands needing password
|
||||||
if cmd in ['payto', 'password', 'newaddress','mktx','seed'] or ( cmd=='addresses' and options.show_keys):
|
if cmd in ['payto', 'password', 'mktx', 'seed' ] or ( cmd=='addresses' and options.show_keys):
|
||||||
password = getpass.getpass('Password:') if wallet.use_encryption else None
|
password = getpass.getpass('Password:') if wallet.use_encryption else None
|
||||||
|
|
||||||
if cmd=='help':
|
if cmd=='help':
|
||||||
|
@ -790,6 +821,9 @@ if __name__ == '__main__':
|
||||||
addr = args[1]
|
addr = args[1]
|
||||||
print wallet.is_valid(addr)
|
print wallet.is_valid(addr)
|
||||||
|
|
||||||
|
elif cmd == 't2':
|
||||||
|
wallet.create_t2_address(password)
|
||||||
|
|
||||||
elif cmd == 'balance':
|
elif cmd == 'balance':
|
||||||
c, u = wallet.get_balance()
|
c, u = wallet.get_balance()
|
||||||
if u:
|
if u:
|
||||||
|
@ -867,13 +901,12 @@ if __name__ == '__main__':
|
||||||
print h
|
print h
|
||||||
|
|
||||||
elif cmd == 'newaddress':
|
elif cmd == 'newaddress':
|
||||||
s, a = wallet.get_new_address(password)
|
s, a = wallet.get_new_address()
|
||||||
print a
|
print a
|
||||||
|
|
||||||
elif cmd == 'password':
|
elif cmd == 'password':
|
||||||
try:
|
try:
|
||||||
seed = wallet.pw_decode( wallet.seed, password)
|
seed = wallet.pw_decode( wallet.seed, password)
|
||||||
private_keys = ast.literal_eval( wallet.pw_decode( wallet.private_keys, password) )
|
|
||||||
except:
|
except:
|
||||||
print "sorry"
|
print "sorry"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -881,7 +914,6 @@ if __name__ == '__main__':
|
||||||
if new_password == getpass.getpass('Confirm new password:'):
|
if new_password == getpass.getpass('Confirm new password:'):
|
||||||
wallet.use_encryption = (new_password != '')
|
wallet.use_encryption = (new_password != '')
|
||||||
wallet.seed = wallet.pw_encode( seed, new_password)
|
wallet.seed = wallet.pw_encode( seed, new_password)
|
||||||
wallet.private_keys = wallet.pw_encode( repr( private_keys ), new_password)
|
|
||||||
wallet.save()
|
wallet.save()
|
||||||
else:
|
else:
|
||||||
print "error: mismatch"
|
print "error: mismatch"
|
||||||
|
|
|
@ -64,7 +64,6 @@ def show_seed_dialog(wallet, password, parent):
|
||||||
import mnemonic
|
import mnemonic
|
||||||
try:
|
try:
|
||||||
seed = wallet.pw_decode( wallet.seed, password)
|
seed = wallet.pw_decode( wallet.seed, password)
|
||||||
private_keys = ast.literal_eval( wallet.pw_decode( wallet.private_keys, password) )
|
|
||||||
except:
|
except:
|
||||||
show_message("Incorrect password")
|
show_message("Incorrect password")
|
||||||
return
|
return
|
||||||
|
@ -85,8 +84,13 @@ def init_wallet(wallet):
|
||||||
try:
|
try:
|
||||||
found = wallet.read()
|
found = wallet.read()
|
||||||
except BaseException, e:
|
except BaseException, e:
|
||||||
show_message(e.message)
|
show_message(e.args[0])
|
||||||
|
if e.args[1] == 0: exit(1)
|
||||||
found = 1
|
found = 1
|
||||||
|
except:
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
# ask if the user wants to create a new wallet, or recover from a seed.
|
# ask if the user wants to create a new wallet, or recover from a seed.
|
||||||
|
@ -114,7 +118,7 @@ def init_wallet(wallet):
|
||||||
run_settings_dialog(wallet, is_create=True, is_recovery=False, parent=None)
|
run_settings_dialog(wallet, is_create=True, is_recovery=False, parent=None)
|
||||||
|
|
||||||
# generate first key
|
# generate first key
|
||||||
wallet.create_new_address(False, None)
|
wallet.create_new_address2(False)
|
||||||
|
|
||||||
# run a dialog indicating the seed, ask the user to remember it
|
# run a dialog indicating the seed, ask the user to remember it
|
||||||
show_seed_dialog(wallet, None, None)
|
show_seed_dialog(wallet, None, None)
|
||||||
|
@ -133,13 +137,14 @@ def init_wallet(wallet):
|
||||||
message_format = "Please wait..." )
|
message_format = "Please wait..." )
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def recover_thread( wallet, dialog, password ):
|
def recover_thread( wallet, dialog ):
|
||||||
wallet.is_found = wallet.recover( password )
|
wallet.init_mpk( wallet.seed ) # not encrypted at this point
|
||||||
|
wallet.is_found = wallet.recover()
|
||||||
if wallet.is_found:
|
if wallet.is_found:
|
||||||
wallet.save()
|
wallet.save()
|
||||||
gobject.idle_add( dialog.destroy )
|
gobject.idle_add( dialog.destroy )
|
||||||
|
|
||||||
thread.start_new_thread( recover_thread, ( wallet, dialog, None ) ) # no password
|
thread.start_new_thread( recover_thread, ( wallet, dialog ) )
|
||||||
r = dialog.run()
|
r = dialog.run()
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
if r==gtk.RESPONSE_CANCEL: sys.exit(1)
|
if r==gtk.RESPONSE_CANCEL: sys.exit(1)
|
||||||
|
@ -354,7 +359,6 @@ def change_password_dialog(wallet, parent, icon):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
seed = wallet.pw_decode( wallet.seed, password)
|
seed = wallet.pw_decode( wallet.seed, password)
|
||||||
private_keys = ast.literal_eval( wallet.pw_decode( wallet.private_keys, password) )
|
|
||||||
except:
|
except:
|
||||||
show_message("Incorrect password")
|
show_message("Incorrect password")
|
||||||
return
|
return
|
||||||
|
@ -365,7 +369,6 @@ def change_password_dialog(wallet, parent, icon):
|
||||||
|
|
||||||
wallet.use_encryption = (new_password != '')
|
wallet.use_encryption = (new_password != '')
|
||||||
wallet.seed = wallet.pw_encode( seed, new_password)
|
wallet.seed = wallet.pw_encode( seed, new_password)
|
||||||
wallet.private_keys = wallet.pw_encode( repr( private_keys ), new_password)
|
|
||||||
wallet.save()
|
wallet.save()
|
||||||
|
|
||||||
if icon:
|
if icon:
|
||||||
|
@ -1020,8 +1023,7 @@ class BitcoinGUI:
|
||||||
errorDialog.run()
|
errorDialog.run()
|
||||||
errorDialog.destroy()
|
errorDialog.destroy()
|
||||||
else:
|
else:
|
||||||
password = password_dialog() if self.wallet.use_encryption else None
|
success, ret = self.wallet.get_new_address()
|
||||||
success, ret = self.wallet.get_new_address(password)
|
|
||||||
self.update_session = True # we created a new address
|
self.update_session = True # we created a new address
|
||||||
if success:
|
if success:
|
||||||
address = ret
|
address = ret
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import electrum, getpass, base64,ast,sys
|
import electrum, getpass, base64,ast,sys,os
|
||||||
|
from version import SEED_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade_wallet(wallet):
|
def upgrade_wallet(wallet):
|
||||||
if wallet.version == 1 and wallet.use_encryption:
|
print "walet path:",wallet.path
|
||||||
|
print "seed version:", wallet.seed_version
|
||||||
|
if wallet.seed_version == 1 and wallet.use_encryption:
|
||||||
# version 1 used pycrypto for wallet encryption
|
# version 1 used pycrypto for wallet encryption
|
||||||
import Crypto
|
import Crypto
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
|
@ -27,21 +30,65 @@ def upgrade_wallet(wallet):
|
||||||
wallet.private_keys = wallet.pw_encode( repr( private_keys ), password)
|
wallet.private_keys = wallet.pw_encode( repr( private_keys ), password)
|
||||||
wallet.save()
|
wallet.save()
|
||||||
print "upgraded to version 2"
|
print "upgraded to version 2"
|
||||||
if wallet.version < 3:
|
exit(1)
|
||||||
print """
|
|
||||||
Your wallet is deprecated; its generation seed will not work with versions 0.31 and above.
|
|
||||||
In order to upgrade, you need to create a new wallet (you may use your current seed), and
|
|
||||||
to send your bitcoins to the new wallet.
|
|
||||||
|
|
||||||
We apologize for the inconvenience. We try to keep this kind of upgrades as rare as possible.
|
if wallet.seed_version < SEED_VERSION:
|
||||||
"""
|
print """Note: your wallet seed is deprecated. Please create a new wallet, and move your coins to the new wallet."""
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
path = sys.argv[1]
|
path = sys.argv[1]
|
||||||
except:
|
except:
|
||||||
path = None
|
# backward compatibility: look for wallet file in the default data directory
|
||||||
|
if "HOME" in os.environ:
|
||||||
|
wallet_dir = os.path.join( os.environ["HOME"], '.electrum')
|
||||||
|
elif "LOCALAPPDATA" in os.environ:
|
||||||
|
wallet_dir = os.path.join( os.environ["LOCALAPPDATA"], 'Electrum' )
|
||||||
|
elif "APPDATA" in os.environ:
|
||||||
|
wallet_dir = os.path.join( os.environ["APPDATA"], 'Electrum' )
|
||||||
|
else:
|
||||||
|
raise BaseException("No home directory found in environment variables.")
|
||||||
|
path = os.path.join( wallet_dir, 'electrum.dat')
|
||||||
|
|
||||||
|
try:
|
||||||
|
f = open(path,"r")
|
||||||
|
data = f.read()
|
||||||
|
f.close()
|
||||||
|
except:
|
||||||
|
print "file not found", path
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
x = ast.literal_eval(data)
|
||||||
|
except:
|
||||||
|
print "error: could not parse wallet"
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if type(x) == tuple:
|
||||||
|
seed_version, use_encryption, fee, host, port, blocks, seed, addresses, private_keys, change_addresses, status, history, labels, addressbook = x
|
||||||
|
s = {
|
||||||
|
'seed_version':seed_version,
|
||||||
|
'use_encryption':use_encryption,
|
||||||
|
'master_public_key':None,
|
||||||
|
'fee':fee,
|
||||||
|
'host':host,
|
||||||
|
'port':port,
|
||||||
|
'blocks':blocks,
|
||||||
|
'seed':seed,
|
||||||
|
'addresses':addresses,
|
||||||
|
'change_addresses':change_addresses,
|
||||||
|
'status':status,
|
||||||
|
'history':history,
|
||||||
|
'labels':labels,
|
||||||
|
'contacts':addressbook
|
||||||
|
}
|
||||||
|
f = open(path,"w")
|
||||||
|
f.write( repr(s) )
|
||||||
|
f.close()
|
||||||
|
print "wallet format was upgraded."
|
||||||
|
exit(1)
|
||||||
|
|
||||||
wallet = electrum.Wallet(path)
|
wallet = electrum.Wallet(path)
|
||||||
try:
|
try:
|
||||||
found = wallet.read()
|
found = wallet.read()
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
ELECTRUM_VERSION = "0.33"
|
ELECTRUM_VERSION = "0.34"
|
||||||
|
SEED_VERSION = 4 # bump this everytime the seed generation is modified
|
||||||
|
|
Loading…
Reference in New Issue