|
|
|
@ -295,27 +295,22 @@ class NewWallet: |
|
|
|
return seed |
|
|
|
return seed |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_seed(self, seed): |
|
|
|
def prepare_seed(self, seed): |
|
|
|
import mnemonic, unicodedata |
|
|
|
import unicodedata |
|
|
|
|
|
|
|
return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip())) |
|
|
|
if self.seed: |
|
|
|
|
|
|
|
raise Exception("a seed exists") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.seed_version = NEW_SEED_VERSION |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not seed: |
|
|
|
|
|
|
|
self.seed = self.make_seed() |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.seed = unicodedata.normalize('NFC', unicode(seed.strip())) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_seed(self, seed, password): |
|
|
|
|
|
|
|
if self.seed: |
|
|
|
def save_seed(self, password): |
|
|
|
raise Exception("a seed exists") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.seed_version, self.seed = self.prepare_seed(seed) |
|
|
|
if password: |
|
|
|
if password: |
|
|
|
self.seed = pw_encode( self.seed, password) |
|
|
|
self.seed = pw_encode( self.seed, password) |
|
|
|
self.use_encryption = True |
|
|
|
self.use_encryption = True |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
self.use_encryption = False |
|
|
|
|
|
|
|
|
|
|
|
self.storage.put('seed', self.seed, True) |
|
|
|
self.storage.put('seed', self.seed, True) |
|
|
|
self.storage.put('seed_version', self.seed_version, True) |
|
|
|
self.storage.put('seed_version', self.seed_version, True) |
|
|
|
self.storage.put('use_encryption', self.use_encryption,True) |
|
|
|
self.storage.put('use_encryption', self.use_encryption,True) |
|
|
|
@ -323,11 +318,11 @@ class NewWallet: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_watching_only_wallet(self, xpub): |
|
|
|
def create_watching_only_wallet(self, xpub): |
|
|
|
self.master_public_keys = { "m/": xpub } |
|
|
|
|
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
|
|
|
|
self.storage.put('seed_version', self.seed_version, True) |
|
|
|
self.storage.put('seed_version', self.seed_version, True) |
|
|
|
account = BIP32_Account({'xpub':xpub}) |
|
|
|
self.add_master_public_key("m/", xpub) |
|
|
|
self.add_account("m/", account) |
|
|
|
xpub0 = self.add_master_keys("m/", "m/0'", None) |
|
|
|
|
|
|
|
account = BIP32_Account({'xpub':xpub0}) |
|
|
|
|
|
|
|
self.add_account("m/0'", account) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_accounts(self, password): |
|
|
|
def create_accounts(self, password): |
|
|
|
@ -340,11 +335,37 @@ class NewWallet: |
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_master_private_key(self, name, xpriv, password): |
|
|
|
|
|
|
|
self.master_private_keys[name] = pw_encode(xpriv, password) |
|
|
|
|
|
|
|
self.storage.put('master_private_keys', self.master_private_keys, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_master_keys(self, root, account_id, password): |
|
|
|
|
|
|
|
x = self.master_private_keys.get(root) |
|
|
|
|
|
|
|
if x: |
|
|
|
|
|
|
|
master_xpriv = pw_decode(x, password ) |
|
|
|
|
|
|
|
xpriv, xpub = bip32_private_derivation(master_xpriv, root, account_id) |
|
|
|
|
|
|
|
self.add_master_public_key(account_id, xpub) |
|
|
|
|
|
|
|
self.add_master_private_key(account_id, xpriv, password) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
master_xpub = self.master_public_keys[root] |
|
|
|
|
|
|
|
xpub = bip32_public_derivation(master_xpub, root, account_id) |
|
|
|
|
|
|
|
self.add_master_public_key(account_id, xpub) |
|
|
|
|
|
|
|
return xpub |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_root(self, name, mnemonic, password, add_private = True): |
|
|
|
|
|
|
|
seed = mnemonic_to_seed(mnemonic,'').encode('hex') |
|
|
|
|
|
|
|
xpriv, xpub = bip32_root(seed) |
|
|
|
|
|
|
|
self.add_master_public_key(name, xpub) |
|
|
|
|
|
|
|
if add_private: |
|
|
|
|
|
|
|
self.add_master_private_key(name, xpriv, password) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_master_keys(self, password): |
|
|
|
def create_master_keys(self, password): |
|
|
|
xpriv, xpub = bip32_root(self.get_seed(password)) |
|
|
|
xpriv, xpub = bip32_root(self.get_seed(password)) |
|
|
|
self.add_master_public_key("m/", xpub) |
|
|
|
self.add_master_public_key("m/", xpub) |
|
|
|
self.master_private_keys["m/"] = pw_encode(xpriv, password) |
|
|
|
self.add_master_private_key("m/", xpriv, password) |
|
|
|
self.storage.put('master_private_keys', self.master_private_keys, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_root_by_master_key(self, xpub): |
|
|
|
def find_root_by_master_key(self, xpub): |
|
|
|
@ -357,19 +378,19 @@ class NewWallet: |
|
|
|
return (self.seed == '') and (self.master_private_keys == {}) |
|
|
|
return (self.seed == '') and (self.master_private_keys == {}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def num_accounts(self, account_type = '1of1'): |
|
|
|
def num_accounts(self): |
|
|
|
keys = self.accounts.keys() |
|
|
|
keys = self.accounts.keys() |
|
|
|
i = 0 |
|
|
|
i = 0 |
|
|
|
while True: |
|
|
|
while True: |
|
|
|
account_id = self.account_id(account_type, i) |
|
|
|
account_id = self.account_id(i) |
|
|
|
if account_id not in keys: break |
|
|
|
if account_id not in keys: break |
|
|
|
i += 1 |
|
|
|
i += 1 |
|
|
|
return i |
|
|
|
return i |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def next_account_address(self, account_type, password): |
|
|
|
def next_account_address(self, password): |
|
|
|
i = self.num_accounts(account_type) |
|
|
|
i = self.num_accounts() |
|
|
|
account_id = self.account_id(account_type, i) |
|
|
|
account_id = self.account_id(i) |
|
|
|
|
|
|
|
|
|
|
|
addr = self.next_addresses.get(account_id) |
|
|
|
addr = self.next_addresses.get(account_id) |
|
|
|
if not addr: |
|
|
|
if not addr: |
|
|
|
@ -380,20 +401,12 @@ class NewWallet: |
|
|
|
|
|
|
|
|
|
|
|
return account_id, addr |
|
|
|
return account_id, addr |
|
|
|
|
|
|
|
|
|
|
|
def account_id(self, account_type, i): |
|
|
|
def account_id(self, i): |
|
|
|
if account_type == '1of1': |
|
|
|
return "m/%d'"%i |
|
|
|
return "m/%d'"%i |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
raise |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_account(self, account_id, password): |
|
|
|
def make_account(self, account_id, password): |
|
|
|
"""Creates and saves the master keys, but does not save the account""" |
|
|
|
"""Creates and saves the master keys, but does not save the account""" |
|
|
|
master_xpriv = pw_decode( self.master_private_keys["m/"] , password ) |
|
|
|
xpub = self.add_master_keys("m/", account_id, password) |
|
|
|
xpriv, xpub = bip32_private_derivation(master_xpriv, "m/", account_id) |
|
|
|
|
|
|
|
self.master_private_keys[account_id] = pw_encode(xpriv, password) |
|
|
|
|
|
|
|
self.master_public_keys[account_id] = xpub |
|
|
|
|
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
|
|
|
|
self.storage.put('master_private_keys', self.master_private_keys, True) |
|
|
|
|
|
|
|
account = BIP32_Account({'xpub':xpub}) |
|
|
|
account = BIP32_Account({'xpub':xpub}) |
|
|
|
return account |
|
|
|
return account |
|
|
|
|
|
|
|
|
|
|
|
@ -418,15 +431,15 @@ class NewWallet: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_account(self, name, password): |
|
|
|
def create_account(self, name, password): |
|
|
|
i = self.num_accounts('1of1') |
|
|
|
i = self.num_accounts() |
|
|
|
account_id = self.account_id('1of1', i) |
|
|
|
account_id = self.account_id(i) |
|
|
|
account = self.make_account(account_id, password) |
|
|
|
account = self.make_account(account_id, password) |
|
|
|
self.add_account(account_id, account) |
|
|
|
self.add_account(account_id, account) |
|
|
|
if name: |
|
|
|
if name: |
|
|
|
self.set_label(account_id, name) |
|
|
|
self.set_label(account_id, name) |
|
|
|
|
|
|
|
|
|
|
|
# add address of the next account |
|
|
|
# add address of the next account |
|
|
|
_, _ = self.next_account_address('1of1', password) |
|
|
|
_, _ = self.next_account_address(password) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_account(self, account_id, account): |
|
|
|
def add_account(self, account_id, account): |
|
|
|
@ -471,8 +484,8 @@ class NewWallet: |
|
|
|
def account_is_pending(self, k): |
|
|
|
def account_is_pending(self, k): |
|
|
|
return k in self.pending_accounts |
|
|
|
return k in self.pending_accounts |
|
|
|
|
|
|
|
|
|
|
|
def create_pending_account(self, acct_type, name, password): |
|
|
|
def create_pending_account(self, name, password): |
|
|
|
account_id, addr = self.next_account_address(acct_type, password) |
|
|
|
account_id, addr = self.next_account_address(password) |
|
|
|
self.set_label(account_id, name) |
|
|
|
self.set_label(account_id, name) |
|
|
|
self.pending_accounts[account_id] = addr |
|
|
|
self.pending_accounts[account_id] = addr |
|
|
|
self.storage.put('pending_accounts', self.pending_accounts) |
|
|
|
self.storage.put('pending_accounts', self.pending_accounts) |
|
|
|
@ -1472,32 +1485,16 @@ class Wallet_2of2(NewWallet): |
|
|
|
NewWallet.__init__(self, storage) |
|
|
|
NewWallet.__init__(self, storage) |
|
|
|
self.storage.put('wallet_type', '2of2', True) |
|
|
|
self.storage.put('wallet_type', '2of2', True) |
|
|
|
|
|
|
|
|
|
|
|
def init_cold_seed(self): |
|
|
|
|
|
|
|
cold_seed = self.make_seed() |
|
|
|
|
|
|
|
seed = mnemonic_to_seed(cold_seed,'').encode('hex') |
|
|
|
|
|
|
|
xpriv, xpub = bip32_root(seed) |
|
|
|
|
|
|
|
self.master_public_keys["cold/"] = xpub |
|
|
|
|
|
|
|
return cold_seed |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_cold_seed(self): |
|
|
|
|
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_account(self, account_id, password): |
|
|
|
def make_account(self, account_id, password): |
|
|
|
# if accounts are hardened, we cannot make it symmetric on the other wallet |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Creates and saves the master keys, but does not save the account""" |
|
|
|
"""Creates and saves the master keys, but does not save the account""" |
|
|
|
master_xpriv = pw_decode( self.master_private_keys["m/"] , password ) |
|
|
|
xpub1 = self.add_master_keys("m/", account_id, password) |
|
|
|
xpriv, xpub = bip32_private_derivation(master_xpriv, "m/", account_id) |
|
|
|
xpub2 = self.add_master_keys("cold/", account_id, password) |
|
|
|
self.master_private_keys[account_id] = pw_encode(xpriv, password) |
|
|
|
account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2}) |
|
|
|
self.master_public_keys[account_id] = xpub |
|
|
|
|
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
|
|
|
|
self.storage.put('master_private_keys', self.master_private_keys, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xpub_cold = self.master_public_keys["cold/"] |
|
|
|
|
|
|
|
account = BIP32_Account_2of2({'xpub':xpub, 'xpub2':xpub_cold}) |
|
|
|
|
|
|
|
return account |
|
|
|
return account |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def account_id(self, i): |
|
|
|
|
|
|
|
return "m/%d"%i |
|
|
|
|
|
|
|
|
|
|
|
class Wallet_2of3(Wallet_2of2): |
|
|
|
class Wallet_2of3(Wallet_2of2): |
|
|
|
|
|
|
|
|
|
|
|
@ -1506,21 +1503,14 @@ class Wallet_2of3(Wallet_2of2): |
|
|
|
self.storage.put('wallet_type', '2of3', True) |
|
|
|
self.storage.put('wallet_type', '2of3', True) |
|
|
|
|
|
|
|
|
|
|
|
def make_account(self, account_id, password): |
|
|
|
def make_account(self, account_id, password): |
|
|
|
# if accounts are hardened, we cannot make it symmetric on the other wallet |
|
|
|
xpub1 = self.add_master_keys("m/", account_id, password) |
|
|
|
|
|
|
|
xpub2 = self.add_master_keys("cold/", account_id.replace("m/","cold/"), password) |
|
|
|
"""Creates and saves the master keys, but does not save the account""" |
|
|
|
xpub3 = self.add_master_keys("remote/", account_id.replace("m/","remote/"), password) |
|
|
|
master_xpriv = pw_decode( self.master_private_keys["m/"] , password ) |
|
|
|
account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3}) |
|
|
|
xpriv, xpub = bip32_private_derivation(master_xpriv, "m/", account_id) |
|
|
|
|
|
|
|
self.master_private_keys[account_id] = pw_encode(xpriv, password) |
|
|
|
|
|
|
|
self.master_public_keys[account_id] = xpub |
|
|
|
|
|
|
|
self.storage.put('master_public_keys', self.master_public_keys, True) |
|
|
|
|
|
|
|
self.storage.put('master_private_keys', self.master_private_keys, True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xpub_cold = self.master_public_keys["cold/"] |
|
|
|
|
|
|
|
xpub_remote = self.master_public_keys["remote/"] |
|
|
|
|
|
|
|
account = BIP32_Account_2of3({'xpub':xpub, 'xpub2':xpub_cold, 'xpub3':xpub_remote}) |
|
|
|
|
|
|
|
return account |
|
|
|
return account |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def account_id(self, i): |
|
|
|
|
|
|
|
return "m/%d"%i |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1699,38 +1689,30 @@ class WalletSynchronizer(threading.Thread): |
|
|
|
|
|
|
|
|
|
|
|
class OldWallet(NewWallet): |
|
|
|
class OldWallet(NewWallet): |
|
|
|
|
|
|
|
|
|
|
|
def init_seed(self, seed): |
|
|
|
def make_seed(self): |
|
|
|
import mnemonic |
|
|
|
import mnemonic |
|
|
|
|
|
|
|
seed = random_seed(128) |
|
|
|
if self.seed: |
|
|
|
return ' '.join(mnemonic.mn_encode(seed)) |
|
|
|
raise Exception("a seed exists") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not seed: |
|
|
|
|
|
|
|
seed = random_seed(128) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.seed_version = OLD_SEED_VERSION |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prepare_seed(self, seed): |
|
|
|
|
|
|
|
import mnemonic |
|
|
|
# see if seed was entered as hex |
|
|
|
# see if seed was entered as hex |
|
|
|
seed = seed.strip() |
|
|
|
seed = seed.strip() |
|
|
|
try: |
|
|
|
try: |
|
|
|
assert seed |
|
|
|
assert seed |
|
|
|
seed.decode('hex') |
|
|
|
seed.decode('hex') |
|
|
|
self.seed = str(seed) |
|
|
|
return OLD_SEED_VERSION, str(seed) |
|
|
|
return |
|
|
|
|
|
|
|
except Exception: |
|
|
|
except Exception: |
|
|
|
pass |
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
words = seed.split() |
|
|
|
words = seed.split() |
|
|
|
try: |
|
|
|
seed = mnemonic.mn_decode(words) |
|
|
|
mnemonic.mn_decode(words) |
|
|
|
if not seed: |
|
|
|
except Exception: |
|
|
|
|
|
|
|
raise |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.seed = mnemonic.mn_decode(words) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not self.seed: |
|
|
|
|
|
|
|
raise Exception("Invalid seed") |
|
|
|
raise Exception("Invalid seed") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return OLD_SEED_VERSION, seed |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_master_keys(self, password): |
|
|
|
def create_master_keys(self, password): |
|
|
|
seed = pw_decode(self.seed, password) |
|
|
|
seed = pw_decode(self.seed, password) |
|
|
|
@ -1886,7 +1868,6 @@ class Wallet(object): |
|
|
|
elif is_new_seed(seed): |
|
|
|
elif is_new_seed(seed): |
|
|
|
klass = NewWallet |
|
|
|
klass = NewWallet |
|
|
|
w = klass(storage) |
|
|
|
w = klass(storage) |
|
|
|
w.init_seed(seed) |
|
|
|
|
|
|
|
return w |
|
|
|
return w |
|
|
|
|
|
|
|
|
|
|
|
@classmethod |
|
|
|
@classmethod |
|
|
|
|