3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2011 thomasv@gitorious
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
28 from util import print_msg, print_error
34 from transaction import Transaction
35 from plugins import run_hook
37 from synchronizer import WalletSynchronizer
39 COINBASE_MATURITY = 100
42 # internal ID for imported account
43 IMPORTED_ACCOUNT = '/x'
49 def __init__(self, config):
50 self.lock = threading.Lock()
53 self.file_exists = False
54 self.path = self.init_path(config)
55 print_error( "wallet path", self.path )
60 def init_path(self, config):
61 """Set the path of the wallet."""
63 # command line -w option
64 path = config.get('wallet_path')
69 path = config.get('default_wallet_path')
74 dirpath = os.path.join(config.path, "wallets")
75 if not os.path.exists(dirpath):
78 new_path = os.path.join(config.path, "wallets", "default_wallet")
80 # default path in pre 1.9 versions
81 old_path = os.path.join(config.path, "electrum.dat")
82 if os.path.exists(old_path) and not os.path.exists(new_path):
83 os.rename(old_path, new_path)
89 """Read the contents of the wallet file."""
91 with open(self.path, "r") as f:
96 d = ast.literal_eval( data ) #parse raw data from reading wallet file
98 raise IOError("Cannot read wallet file.")
101 self.file_exists = True
104 def get(self, key, default=None):
105 v = self.data.get(key)
110 def put(self, key, value, save = True):
113 if value is not None:
114 self.data[key] = value
115 elif key in self.data:
122 f = open(self.path,"w")
125 if 'ANDROID_DATA' not in os.environ:
127 os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
130 class Abstract_Wallet:
132 def __init__(self, storage):
133 self.storage = storage
134 self.electrum_version = ELECTRUM_VERSION
135 self.gap_limit_for_change = 3 # constant
137 self.seed_version = storage.get('seed_version', NEW_SEED_VERSION)
138 self.gap_limit = storage.get('gap_limit', 5)
139 self.use_change = storage.get('use_change',True)
140 self.use_encryption = storage.get('use_encryption', False)
141 self.seed = storage.get('seed', '') # encrypted
142 self.labels = storage.get('labels', {})
143 self.frozen_addresses = storage.get('frozen_addresses',[])
144 self.addressbook = storage.get('contacts', [])
146 self.history = storage.get('addr_history',{}) # address -> list(txid, height)
148 self.fee = int(storage.get('fee_per_kb', 10000))
150 self.master_public_keys = storage.get('master_public_keys',{})
151 self.master_private_keys = storage.get('master_private_keys', {})
153 self.next_addresses = storage.get('next_addresses',{})
156 # This attribute is set when wallet.start_threads is called.
157 self.synchronizer = None
161 self.transactions = {}
162 tx_list = self.storage.get('transactions',{})
163 for k,v in tx_list.items():
167 print_msg("Warning: Cannot deserialize transactions. skipping")
170 self.add_extra_addresses(tx)
171 self.transactions[k] = tx
173 for h,tx in self.transactions.items():
174 if not self.check_new_tx(h, tx):
175 print_error("removing unreferenced tx", h)
176 self.transactions.pop(h)
180 self.prevout_values = {} # my own transaction outputs
181 self.spent_outputs = []
186 # there is a difference between wallet.up_to_date and interface.is_up_to_date()
187 # interface.is_up_to_date() returns true when all requests have been answered and processed
188 # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
190 self.up_to_date = False
191 self.lock = threading.Lock()
192 self.transaction_lock = threading.Lock()
193 self.tx_event = threading.Event()
194 for tx_hash, tx in self.transactions.items():
195 self.update_tx_outputs(tx_hash)
197 def add_extra_addresses(self, tx):
199 # find the address corresponding to pay-to-pubkey inputs
200 tx.add_extra_addresses(self.transactions)
201 for o in tx.d.get('outputs'):
202 if o.get('is_pubkey'):
203 for tx2 in self.transactions.values():
204 tx2.add_extra_addresses({h:tx})
206 def get_action(self):
209 def convert_imported_keys(self, password):
210 for k, v in self.imported_keys.items():
211 sec = pw_decode(v, password)
212 pubkey = public_key_from_private_key(sec)
213 address = public_key_to_bc_address(pubkey.decode('hex'))
215 self.import_key(sec, password)
216 self.imported_keys.pop(k)
217 self.storage.put('imported_keys', self.imported_keys)
219 def load_accounts(self):
221 self.imported_keys = self.storage.get('imported_keys',{})
223 d = self.storage.get('accounts', {})
224 for k, v in d.items():
226 v['mpk'] = self.storage.get('master_public_key')
227 self.accounts[k] = OldAccount(v)
228 elif v.get('imported'):
229 self.accounts[k] = ImportedAccount(v)
231 self.accounts[k] = BIP32_Account_2of3(v)
233 self.accounts[k] = BIP32_Account_2of2(v)
235 self.accounts[k] = BIP32_Account(v)
236 elif v.get('pending'):
237 self.accounts[k] = PendingAccount(v)
239 print_error("cannot load account", v)
241 def synchronize(self):
244 def can_create_accounts(self):
247 def set_up_to_date(self,b):
248 with self.lock: self.up_to_date = b
250 def is_up_to_date(self):
251 with self.lock: return self.up_to_date
254 self.up_to_date = False
255 while not self.is_up_to_date():
258 def is_imported(self, addr):
259 account = self.accounts.get(IMPORTED_ACCOUNT)
261 return addr in account.get_addresses(0)
265 def has_imported_keys(self):
266 account = self.accounts.get(IMPORTED_ACCOUNT)
267 return account is not None
269 def import_key(self, sec, password):
271 pubkey = public_key_from_private_key(sec)
272 address = public_key_to_bc_address(pubkey.decode('hex'))
274 raise Exception('Invalid private key')
276 if self.is_mine(address):
277 raise Exception('Address already in wallet')
279 if self.accounts.get(IMPORTED_ACCOUNT) is None:
280 self.accounts[IMPORTED_ACCOUNT] = ImportedAccount({'imported':{}})
281 self.accounts[IMPORTED_ACCOUNT].add(address, pubkey, sec, password)
284 if self.synchronizer:
285 self.synchronizer.subscribe_to_addresses([address])
288 def delete_imported_key(self, addr):
289 account = self.accounts[IMPORTED_ACCOUNT]
291 if not account.get_addresses(0):
292 self.accounts.pop(IMPORTED_ACCOUNT)
295 def set_label(self, name, text = None):
297 old_text = self.labels.get(name)
300 self.labels[name] = text
304 self.labels.pop(name)
308 self.storage.put('labels', self.labels, True)
310 run_hook('set_label', name, text, changed)
313 def addresses(self, include_change = True, _next=True):
315 for a in self.accounts.keys():
316 o += self.get_account_addresses(a, include_change)
319 for addr in self.next_addresses.values():
324 def is_mine(self, address):
325 return address in self.addresses(True)
327 def is_change(self, address):
328 if not self.is_mine(address): return False
329 acct, s = self.get_address_index(address)
330 if s is None: return False
333 def get_address_index(self, address):
335 for account in self.accounts.keys():
336 for for_change in [0,1]:
337 addresses = self.accounts[account].get_addresses(for_change)
338 for addr in addresses:
340 return account, (for_change, addresses.index(addr))
342 for k,v in self.next_addresses.items():
346 raise Exception("Address not found", address)
348 def getpubkeys(self, addr):
349 assert is_valid(addr) and self.is_mine(addr)
350 account, sequence = self.get_address_index(addr)
351 a = self.accounts[account]
352 return a.get_pubkeys( sequence )
354 def get_private_key(self, address, password):
355 if self.is_watching_only():
357 account_id, sequence = self.get_address_index(address)
358 return self.accounts[account_id].get_private_key(sequence, self, password)
360 def get_public_keys(self, address):
361 account_id, sequence = self.get_address_index(address)
362 return self.accounts[account_id].get_pubkeys(sequence)
364 def can_sign(self, tx):
366 if self.is_watching_only():
372 addr_list, xpub_list = tx.inputs_to_sign()
373 for addr in addr_list:
374 if self.is_mine(addr):
377 mpk = [ self.master_public_keys[k] for k in self.master_private_keys.keys() ]
378 for xpub, sequence in xpub_list:
384 def add_keypairs(self, tx, keypairs, password):
385 # first check the provided password. This will raise if invalid.
386 self.check_password(password)
388 addr_list, xpub_list = tx.inputs_to_sign()
389 for addr in addr_list:
390 if self.is_mine(addr):
391 private_keys = self.get_private_key(addr, password)
392 for sec in private_keys:
393 pubkey = public_key_from_private_key(sec)
394 keypairs[ pubkey ] = sec
396 for xpub, sequence in xpub_list:
397 # look for account that can sign
398 for k, account in self.accounts.items():
399 if xpub in account.get_master_pubkeys():
404 addr = account.get_address(*sequence)
405 pk = self.get_private_key(addr, password)
407 pubkey = public_key_from_private_key(sec)
408 keypairs[pubkey] = sec
410 def signrawtransaction(self, tx, private_keys, password):
411 # check that the password is correct. This will raise if it's not.
412 self.get_seed(password)
414 # build a list of public/private keys
417 # add private keys from parameter
418 for sec in private_keys:
419 pubkey = public_key_from_private_key(sec)
420 keypairs[ pubkey ] = sec
423 self.add_keypairs(tx, keypairs, password)
425 # sign the transaction
426 self.sign_transaction(tx, keypairs, password)
428 def sign_message(self, address, message, password):
429 keys = self.get_private_key(address, password)
430 assert len(keys) == 1
432 key = regenerate_key(sec)
433 compressed = is_compressed(sec)
434 return key.sign_message(message, compressed, address)
436 def decrypt_message(self, pubkey, message, password):
437 address = public_key_to_bc_address(pubkey.decode('hex'))
438 keys = self.get_private_key(address, password)
440 ec = regenerate_key(secret)
441 decrypted = ec.decrypt_message(message)
445 return self.history.values() != [[]] * len(self.history)
447 def add_contact(self, address, label=None):
448 self.addressbook.append(address)
449 self.storage.put('contacts', self.addressbook, True)
451 self.set_label(address, label)
453 def delete_contact(self, addr):
454 if addr in self.addressbook:
455 self.addressbook.remove(addr)
456 self.storage.put('addressbook', self.addressbook, True)
458 def fill_addressbook(self):
459 for tx_hash, tx in self.transactions.items():
460 is_relevant, is_send, _, _ = self.get_tx_value(tx)
462 for addr, v in tx.outputs:
463 if not self.is_mine(addr) and addr not in self.addressbook:
464 self.addressbook.append(addr)
466 # self.update_tx_labels()
468 def get_num_tx(self, address):
470 for tx in self.transactions.values():
471 if address in map(lambda x:x[0], tx.outputs): n += 1
474 def get_address_flags(self, addr):
475 flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-"
476 flags += "F" if addr in self.frozen_addresses else "-"
479 def get_tx_value(self, tx, account=None):
480 domain = self.get_account_addresses(account)
481 return tx.get_value(domain, self.prevout_values)
483 def update_tx_outputs(self, tx_hash):
484 tx = self.transactions.get(tx_hash)
486 for i, (addr, value) in enumerate(tx.outputs):
487 key = tx_hash+ ':%d'%i
488 self.prevout_values[key] = value
490 for item in tx.inputs:
491 if self.is_mine(item.get('address')):
492 key = item['prevout_hash'] + ':%d'%item['prevout_n']
493 self.spent_outputs.append(key)
495 def get_addr_balance(self, address):
496 #assert self.is_mine(address)
497 h = self.history.get(address,[])
498 if h == ['*']: return 0,0
500 received_coins = [] # list of coins received at address
502 for tx_hash, tx_height in h:
503 tx = self.transactions.get(tx_hash)
506 for i, (addr, value) in enumerate(tx.outputs):
508 key = tx_hash + ':%d'%i
509 received_coins.append(key)
511 for tx_hash, tx_height in h:
512 tx = self.transactions.get(tx_hash)
516 for item in tx.inputs:
517 addr = item.get('address')
519 key = item['prevout_hash'] + ':%d'%item['prevout_n']
520 value = self.prevout_values.get( key )
521 if key in received_coins:
524 for i, (addr, value) in enumerate(tx.outputs):
525 key = tx_hash + ':%d'%i
535 def get_account_name(self, k):
536 return self.labels.get(k, self.accounts[k].get_name(k))
538 def get_account_names(self):
540 for k in self.accounts.keys():
541 account_names[k] = self.get_account_name(k)
544 def get_account_addresses(self, a, include_change=True):
546 o = self.addresses(True)
547 elif a in self.accounts:
548 ac = self.accounts[a]
549 o = ac.get_addresses(0)
550 if include_change: o += ac.get_addresses(1)
553 def get_account_balance(self, account):
554 return self.get_balance(self.get_account_addresses(account))
556 def get_frozen_balance(self):
557 return self.get_balance(self.frozen_addresses)
559 def get_balance(self, domain=None):
560 if domain is None: domain = self.addresses(True)
563 c, u = self.get_addr_balance(addr)
568 def get_unspent_coins(self, domain=None):
570 if domain is None: domain = self.addresses(True)
572 h = self.history.get(addr, [])
573 if h == ['*']: continue
574 for tx_hash, tx_height in h:
575 tx = self.transactions.get(tx_hash)
576 if tx is None: raise Exception("Wallet not synchronized")
577 is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
578 for o in tx.d.get('outputs'):
580 if output.get('address') != addr: continue
581 key = tx_hash + ":%d" % output.get('prevout_n')
582 if key in self.spent_outputs: continue
583 output['prevout_hash'] = tx_hash
584 output['height'] = tx_height
585 output['coinbase'] = is_coinbase
586 coins.append((tx_height, output))
590 coins = sorted(coins)
591 if coins[-1][0] != 0:
592 while coins[0][0] == 0:
593 coins = coins[1:] + [ coins[0] ]
594 return [x[1] for x in coins]
596 def choose_tx_inputs( self, amount, fixed_fee, num_outputs, domain = None, coins = None ):
597 """ todo: minimize tx size """
599 fee = self.fee if fixed_fee is None else fixed_fee
603 domain = self.addresses(True)
604 for i in self.frozen_addresses:
605 if i in domain: domain.remove(i)
606 coins = self.get_unspent_coins(domain)
611 if item.get('coinbase') and item.get('height') + COINBASE_MATURITY > self.network.get_local_height():
613 v = item.get('value')
616 fee = self.estimated_fee(inputs, num_outputs) if fixed_fee is None else fixed_fee
617 if total >= amount + fee: break
621 return inputs, total, fee
623 def set_fee(self, fee):
626 self.storage.put('fee_per_kb', self.fee, True)
628 def estimated_fee(self, inputs, num_outputs):
629 estimated_size = len(inputs) * 180 + num_outputs * 34 # this assumes non-compressed keys
630 fee = self.fee * int(math.ceil(estimated_size/1000.))
633 def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None):
634 "add change to a transaction"
635 change_amount = total - ( amount + fee )
636 if change_amount > DUST_THRESHOLD:
639 # send change to one of the accounts involved in the tx
640 address = inputs[0].get('address')
641 account, _ = self.get_address_index(address)
643 if not self.use_change or account == IMPORTED_ACCOUNT:
644 change_addr = inputs[-1]['address']
646 change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
648 # Insert the change output at a random position in the outputs
649 posn = random.randint(0, len(outputs))
650 outputs[posn:posn] = [( change_addr, change_amount)]
653 def get_history(self, address):
655 return self.history.get(address)
657 def get_status(self, h):
658 if not h: return None
659 if h == ['*']: return '*'
661 for tx_hash, height in h:
662 status += tx_hash + ':%d:' % height
663 return hashlib.sha256( status ).digest().encode('hex')
665 def receive_tx_callback(self, tx_hash, tx, tx_height):
667 with self.transaction_lock:
668 self.add_extra_addresses(tx)
669 if not self.check_new_tx(tx_hash, tx):
670 # may happen due to pruning
671 print_error("received transaction that is no longer referenced in history", tx_hash)
673 self.transactions[tx_hash] = tx
674 self.network.pending_transactions_for_notifications.append(tx)
675 self.save_transactions()
676 if self.verifier and tx_height>0:
677 self.verifier.add(tx_hash, tx_height)
678 self.update_tx_outputs(tx_hash)
680 def save_transactions(self):
682 for k,v in self.transactions.items():
684 self.storage.put('transactions', tx, True)
686 def receive_history_callback(self, addr, hist):
688 if not self.check_new_history(addr, hist):
689 raise Exception("error: received history for %s is not consistent with known transactions"%addr)
692 self.history[addr] = hist
693 self.storage.put('addr_history', self.history, True)
696 for tx_hash, tx_height in hist:
698 # add it in case it was previously unconfirmed
699 if self.verifier: self.verifier.add(tx_hash, tx_height)
701 def get_tx_history(self, account=None):
702 if not self.verifier:
705 with self.transaction_lock:
706 history = self.transactions.items()
707 history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
711 for tx_hash, tx in history:
712 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
713 if v is not None: balance += v
715 c, u = self.get_account_balance(account)
718 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
720 balance = c + u - balance
721 for tx_hash, tx in history:
722 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
725 if value is not None:
728 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
729 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
733 def get_label(self, tx_hash):
734 label = self.labels.get(tx_hash)
735 is_default = (label == '') or (label is None)
736 if is_default: label = self.get_default_label(tx_hash)
737 return label, is_default
739 def get_default_label(self, tx_hash):
740 tx = self.transactions.get(tx_hash)
743 is_relevant, is_mine, _, _ = self.get_tx_value(tx)
747 if not self.is_mine(o_addr):
749 default_label = self.labels[o_addr]
751 default_label = '>' + o_addr
754 default_label = '(internal)'
758 if self.is_mine(o_addr) and not self.is_change(o_addr):
763 if self.is_mine(o_addr):
770 default_label = self.labels[o_addr]
772 default_label = '<' + o_addr
776 def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, domain=None, coins=None ):
777 for address, x in outputs:
778 assert is_valid(address), "Address " + address + " is invalid!"
779 amount = sum( map(lambda x:x[1], outputs) )
780 inputs, total, fee = self.choose_tx_inputs( amount, fee, len(outputs), domain, coins )
782 raise ValueError("Not enough funds")
784 self.add_input_info(txin)
785 outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr)
786 return Transaction.from_io(inputs, outputs)
788 def mktx(self, outputs, password, fee=None, change_addr=None, domain= None, coins = None ):
789 tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain, coins)
791 self.add_keypairs(tx, keypairs, password)
793 self.sign_transaction(tx, keypairs, password)
796 def add_input_info(self, txin):
797 address = txin['address']
798 account_id, sequence = self.get_address_index(address)
799 account = self.accounts[account_id]
800 redeemScript = account.redeem_script(sequence)
801 txin['x_pubkeys'] = account.get_xpubkeys(sequence)
802 txin['pubkeys'] = pubkeys = account.get_pubkeys(sequence)
803 txin['signatures'] = [None] * len(pubkeys)
806 txin['redeemScript'] = redeemScript
809 txin['redeemPubkey'] = account.get_pubkey(*sequence)
812 def sign_transaction(self, tx, keypairs, password):
814 run_hook('sign_transaction', tx, password)
816 def sendtx(self, tx):
820 return self.receive_tx(h, tx)
822 def send_tx(self, tx):
824 self.tx_event.clear()
825 self.network.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast)
828 def on_broadcast(self, i, r):
829 self.tx_result = r.get('result')
832 def receive_tx(self, tx_hash, tx):
835 return False, "error: " + out
836 run_hook('receive_tx', tx, self)
839 def update_password(self, old_password, new_password):
840 if new_password == '':
844 decoded = self.get_seed(old_password)
845 self.seed = pw_encode( decoded, new_password)
846 self.storage.put('seed', self.seed, True)
848 imported_account = self.accounts.get(IMPORTED_ACCOUNT)
850 imported_account.update_password(old_password, new_password)
853 for k, v in self.master_private_keys.items():
854 b = pw_decode(v, old_password)
855 c = pw_encode(b, new_password)
856 self.master_private_keys[k] = c
857 self.storage.put('master_private_keys', self.master_private_keys, True)
859 self.use_encryption = (new_password != None)
860 self.storage.put('use_encryption', self.use_encryption,True)
862 def freeze(self,addr):
863 if self.is_mine(addr) and addr not in self.frozen_addresses:
864 self.frozen_addresses.append(addr)
865 self.storage.put('frozen_addresses', self.frozen_addresses, True)
870 def unfreeze(self,addr):
871 if self.is_mine(addr) and addr in self.frozen_addresses:
872 self.frozen_addresses.remove(addr)
873 self.storage.put('frozen_addresses', self.frozen_addresses, True)
878 def set_verifier(self, verifier):
879 self.verifier = verifier
881 # review transactions that are in the history
882 for addr, hist in self.history.items():
883 if hist == ['*']: continue
884 for tx_hash, tx_height in hist:
886 # add it in case it was previously unconfirmed
887 self.verifier.add(tx_hash, tx_height)
889 # if we are on a pruning server, remove unverified transactions
890 vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
891 for tx_hash in self.transactions.keys():
892 if tx_hash not in vr:
893 self.transactions.pop(tx_hash)
895 def check_new_history(self, addr, hist):
896 # check that all tx in hist are relevant
898 for tx_hash, height in hist:
899 tx = self.transactions.get(tx_hash)
901 if not tx.has_address(addr):
904 # check that we are not "orphaning" a transaction
905 old_hist = self.history.get(addr,[])
906 if old_hist == ['*']: return True
908 for tx_hash, height in old_hist:
909 if tx_hash in map(lambda x:x[0], hist): continue
911 for _addr, _hist in self.history.items():
912 if _addr == addr: continue
913 if _hist == ['*']: continue
914 _tx_hist = map(lambda x:x[0], _hist)
915 if tx_hash in _tx_hist:
920 tx = self.transactions.get(tx_hash)
921 # tx might not be there
925 if self.verifier.get_height(tx_hash):
928 print_error("new history is orphaning transaction:", tx_hash)
929 # check that all outputs are not mine, request histories
931 for _addr, _v in tx.outputs:
932 # assert not self.is_mine(_addr)
933 ext_requests.append( ('blockchain.address.get_history', [_addr]) )
935 ext_h = self.network.synchronous_get(ext_requests)
936 print_error("sync:", ext_requests, ext_h)
939 if h == ['*']: continue
941 if item.get('tx_hash') == tx_hash:
942 height = item.get('height')
944 print_error("found height for", tx_hash, height)
945 self.verifier.add(tx_hash, height)
947 print_error("removing orphaned tx from history", tx_hash)
948 self.transactions.pop(tx_hash)
952 def check_new_tx(self, tx_hash, tx):
953 # 1 check that tx is referenced in addr_history.
955 for addr, hist in self.history.items():
956 if hist == ['*']:continue
957 for txh, height in hist:
959 addresses.append(addr)
964 # 2 check that referencing addresses are in the tx
965 for addr in addresses:
966 if not tx.has_address(addr):
971 def start_threads(self, network):
972 from verifier import TxVerifier
973 self.network = network
974 if self.network is not None:
975 self.verifier = TxVerifier(self.network, self.storage)
976 self.verifier.start()
977 self.set_verifier(self.verifier)
978 self.synchronizer = WalletSynchronizer(self, network)
979 self.synchronizer.start()
982 self.synchronizer =None
984 def stop_threads(self):
987 self.synchronizer.stop()
989 def restore(self, cb):
992 def get_accounts(self):
995 def save_accounts(self):
997 for k, v in self.accounts.items():
999 self.storage.put('accounts', d, True)
1001 def can_import(self):
1002 return not self.is_watching_only()
1004 def is_used(self, address):
1005 h = self.history.get(address,[])
1006 c, u = self.get_addr_balance(address)
1007 return len(h), len(h) > 0 and c == -u
1009 def address_is_old(self, address, age_limit=2):
1011 h = self.history.get(address, [])
1014 for tx_hash, tx_height in h:
1018 tx_age = self.network.get_local_height() - tx_height + 1
1021 return age > age_limit
1024 class Imported_Wallet(Abstract_Wallet):
1026 def __init__(self, storage):
1027 Abstract_Wallet.__init__(self, storage)
1028 a = self.accounts.get(IMPORTED_ACCOUNT)
1030 self.accounts[IMPORTED_ACCOUNT] = ImportedAccount({'imported':{}})
1031 self.storage.put('wallet_type', 'imported', True)
1033 def is_watching_only(self):
1034 acc = self.accounts[IMPORTED_ACCOUNT]
1035 n = acc.keypairs.values()
1036 return n == [(None, None)] * len(n)
1041 def is_deterministic(self):
1044 def check_password(self, password):
1045 self.accounts[IMPORTED_ACCOUNT].get_private_key((0,0), self, password)
1047 def is_used(self, address):
1048 h = self.history.get(address,[])
1049 return len(h), False
1051 def get_master_public_keys(self):
1055 class Deterministic_Wallet(Abstract_Wallet):
1057 def __init__(self, storage):
1058 Abstract_Wallet.__init__(self, storage)
1061 return self.seed != ''
1063 def is_deterministic(self):
1066 def is_watching_only(self):
1067 return not self.has_seed()
1069 def add_seed(self, seed, password):
1071 raise Exception("a seed exists")
1073 self.seed_version, self.seed = self.prepare_seed(seed)
1075 self.seed = pw_encode( self.seed, password)
1076 self.use_encryption = True
1078 self.use_encryption = False
1080 self.storage.put('seed', self.seed, True)
1081 self.storage.put('seed_version', self.seed_version, True)
1082 self.storage.put('use_encryption', self.use_encryption,True)
1083 self.create_master_keys(password)
1085 def get_seed(self, password):
1086 return pw_decode(self.seed, password)
1088 def get_mnemonic(self, password):
1089 return self.get_seed(password)
1091 def change_gap_limit(self, value):
1092 if value >= self.gap_limit:
1093 self.gap_limit = value
1094 self.storage.put('gap_limit', self.gap_limit, True)
1095 #self.interface.poke('synchronizer')
1098 elif value >= self.min_acceptable_gap():
1099 for key, account in self.accounts.items():
1100 addresses = account[0]
1101 k = self.num_unused_trailing_addresses(addresses)
1102 n = len(addresses) - k + value
1103 addresses = addresses[0:n]
1104 self.accounts[key][0] = addresses
1106 self.gap_limit = value
1107 self.storage.put('gap_limit', self.gap_limit, True)
1108 self.save_accounts()
1113 def num_unused_trailing_addresses(self, addresses):
1115 for a in addresses[::-1]:
1116 if self.history.get(a):break
1120 def min_acceptable_gap(self):
1121 # fixme: this assumes wallet is synchronized
1125 for account in self.accounts.values():
1126 addresses = account.get_addresses(0)
1127 k = self.num_unused_trailing_addresses(addresses)
1128 for a in addresses[0:-k]:
1129 if self.history.get(a):
1133 if n > nmax: nmax = n
1136 def synchronize_sequence(self, account, for_change):
1137 limit = self.gap_limit_for_change if for_change else self.gap_limit
1140 addresses = account.get_addresses(for_change)
1141 if len(addresses) < limit:
1142 address = account.create_new_address(for_change)
1143 self.history[address] = []
1144 new_addresses.append( address )
1147 if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
1150 address = account.create_new_address(for_change)
1151 self.history[address] = []
1152 new_addresses.append( address )
1154 return new_addresses
1156 def check_pending_accounts(self):
1157 for account_id, addr in self.next_addresses.items():
1158 if self.address_is_old(addr):
1159 print_error( "creating account", account_id )
1160 xpub = self.master_public_keys[account_id]
1161 account = BIP32_Account({'xpub':xpub})
1162 self.add_account(account_id, account)
1163 self.next_addresses.pop(account_id)
1165 def synchronize_account(self, account):
1167 new += self.synchronize_sequence(account, 0)
1168 new += self.synchronize_sequence(account, 1)
1171 def synchronize(self):
1172 self.check_pending_accounts()
1174 for account in self.accounts.values():
1175 if type(account) in [ImportedAccount, PendingAccount]:
1177 new += self.synchronize_account(account)
1179 self.save_accounts()
1180 self.storage.put('addr_history', self.history, True)
1183 def restore(self, callback):
1185 def wait_for_wallet():
1186 self.set_up_to_date(False)
1187 while not self.is_up_to_date():
1188 msg = "%s\n%s %d\n%s %.1f"%(
1189 _("Please wait..."),
1190 _("Addresses generated:"),
1191 len(self.addresses(True)),
1192 _("Kilobytes received:"),
1193 self.network.interface.bytes_received/1024.)
1195 apply(callback, (msg,))
1198 def wait_for_network():
1199 while not self.network.is_connected():
1200 msg = "%s \n" % (_("Connecting..."))
1201 apply(callback, (msg,))
1204 # wait until we are connected, because the user might have selected another server
1210 self.fill_addressbook()
1212 def create_account(self, name, password):
1213 i = self.num_accounts()
1214 account_id = self.account_id(i)
1215 account = self.make_account(account_id, password)
1216 self.add_account(account_id, account)
1218 self.set_label(account_id, name)
1220 # add address of the next account
1221 _, _ = self.next_account_address(password)
1224 def add_account(self, account_id, account):
1225 self.accounts[account_id] = account
1226 self.save_accounts()
1228 def account_is_pending(self, k):
1229 return type(self.accounts.get(k)) == PendingAccount
1231 def delete_pending_account(self, k):
1232 assert self.account_is_pending(k)
1233 self.accounts.pop(k)
1234 self.save_accounts()
1236 def create_pending_account(self, name, password):
1237 account_id, addr = self.next_account_address(password)
1238 self.set_label(account_id, name)
1239 self.accounts[account_id] = PendingAccount({'pending':addr})
1240 self.save_accounts()
1243 class NewWallet(Deterministic_Wallet):
1245 def __init__(self, storage):
1246 Deterministic_Wallet.__init__(self, storage)
1248 def can_create_accounts(self):
1249 return not self.is_watching_only()
1251 def get_master_public_key(self):
1252 return self.master_public_keys["m/"]
1254 def get_master_public_keys(self):
1256 for k, account in self.accounts.items():
1257 name = self.get_account_name(k)
1258 mpk_text = '\n\n'.join( account.get_master_pubkeys() )
1259 out[name] = mpk_text
1262 def get_master_private_key(self, account, password):
1263 k = self.master_private_keys.get(account)
1265 xpriv = pw_decode( k, password)
1268 def check_password(self, password):
1269 xpriv = self.get_master_private_key( "m/", password )
1270 xpub = self.master_public_keys["m/"]
1271 assert deserialize_xkey(xpriv)[3] == deserialize_xkey(xpub)[3]
1273 def create_watching_only_wallet(self, xpub):
1274 self.storage.put('seed_version', self.seed_version, True)
1275 self.add_master_public_key("m/", xpub)
1276 account = BIP32_Account({'xpub':xpub})
1277 self.add_account("m/", account)
1279 def create_accounts(self, password):
1280 # First check the password is valid (this raises if it isn't).
1281 self.check_password(password)
1282 self.create_account('Main account', password)
1284 def add_master_public_key(self, name, mpk):
1285 self.master_public_keys[name] = mpk
1286 self.storage.put('master_public_keys', self.master_public_keys, True)
1288 def add_master_private_key(self, name, xpriv, password):
1289 self.master_private_keys[name] = pw_encode(xpriv, password)
1290 self.storage.put('master_private_keys', self.master_private_keys, True)
1292 def add_master_keys(self, root, account_id, password):
1293 x = self.master_private_keys.get(root)
1295 master_xpriv = pw_decode(x, password )
1296 xpriv, xpub = bip32_private_derivation(master_xpriv, root, account_id)
1297 self.add_master_public_key(account_id, xpub)
1298 self.add_master_private_key(account_id, xpriv, password)
1300 master_xpub = self.master_public_keys[root]
1301 xpub = bip32_public_derivation(master_xpub, root, account_id)
1302 self.add_master_public_key(account_id, xpub)
1305 def create_master_keys(self, password):
1306 xpriv, xpub = bip32_root(mnemonic_to_seed(self.get_seed(password),'').encode('hex'))
1307 self.add_master_public_key("m/", xpub)
1308 self.add_master_private_key("m/", xpriv, password)
1310 def find_root_by_master_key(self, xpub):
1311 for key, xpub2 in self.master_public_keys.items():
1312 if key == "m/":continue
1316 def num_accounts(self):
1318 for k, v in self.accounts.items():
1319 if type(v) != BIP32_Account:
1325 account_id = self.account_id(i)
1326 if account_id not in keys: break
1330 def next_account_address(self, password):
1331 i = self.num_accounts()
1332 account_id = self.account_id(i)
1334 addr = self.next_addresses.get(account_id)
1336 account = self.make_account(account_id, password)
1337 addr = account.first_address()
1338 self.next_addresses[account_id] = addr
1339 self.storage.put('next_addresses', self.next_addresses)
1341 return account_id, addr
1343 def account_id(self, i):
1346 def make_account(self, account_id, password):
1347 """Creates and saves the master keys, but does not save the account"""
1348 xpub = self.add_master_keys("m/", account_id, password)
1349 account = BIP32_Account({'xpub':xpub})
1352 def make_seed(self):
1353 import mnemonic, ecdsa
1354 entropy = ecdsa.util.randrange( pow(2,160) )
1357 ss = "%040x"%(entropy+nonce)
1358 s = hashlib.sha256(ss.decode('hex')).digest().encode('hex')
1359 # we keep only 13 words, that's approximately 139 bits of entropy
1360 words = mnemonic.mn_encode(s)[0:13]
1361 seed = ' '.join(words)
1362 if is_new_seed(seed):
1363 break # this will remove 8 bits of entropy
1367 def prepare_seed(self, seed):
1369 return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip()))
1372 class Wallet_2of2(NewWallet):
1373 """ This class is used for multisignature addresses"""
1375 def __init__(self, storage):
1376 NewWallet.__init__(self, storage)
1377 self.storage.put('wallet_type', '2of2', True)
1379 def can_create_accounts(self):
1382 def can_import(self):
1385 def create_account(self):
1386 xpub1 = self.master_public_keys.get("m/")
1387 xpub2 = self.master_public_keys.get("cold/")
1388 account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2})
1389 self.add_account('m/', account)
1391 def get_master_public_keys(self):
1392 xpub1 = self.master_public_keys.get("m/")
1393 xpub2 = self.master_public_keys.get("cold/")
1394 return {'hot':xpub1, 'cold':xpub2}
1396 def get_action(self):
1397 xpub1 = self.master_public_keys.get("m/")
1398 xpub2 = self.master_public_keys.get("cold/")
1400 return 'create_2of2_1'
1402 return 'create_2of2_2'
1405 class Wallet_2of3(Wallet_2of2):
1406 """ This class is used for multisignature addresses"""
1408 def __init__(self, storage):
1409 Wallet_2of2.__init__(self, storage)
1410 self.storage.put('wallet_type', '2of3', True)
1412 def create_account(self):
1413 xpub1 = self.master_public_keys.get("m/")
1414 xpub2 = self.master_public_keys.get("cold/")
1415 xpub3 = self.master_public_keys.get("remote/")
1416 account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3})
1417 self.add_account('m/', account)
1419 def get_master_public_keys(self):
1420 xpub1 = self.master_public_keys.get("m/")
1421 xpub2 = self.master_public_keys.get("cold/")
1422 xpub3 = self.master_public_keys.get("remote/")
1423 return {'hot':xpub1, 'cold':xpub2, 'remote':xpub3}
1425 def get_action(self):
1426 xpub1 = self.master_public_keys.get("m/")
1427 xpub2 = self.master_public_keys.get("cold/")
1428 xpub3 = self.master_public_keys.get("remote/")
1429 # fixme: we use order of creation
1430 if xpub2 and xpub1 is None:
1431 return 'create_2fa_2'
1433 return 'create_2of3_1'
1434 if xpub2 is None or xpub3 is None:
1435 return 'create_2of3_2'
1438 class OldWallet(Deterministic_Wallet):
1440 def make_seed(self):
1442 seed = random_seed(128)
1443 return ' '.join(mnemonic.mn_encode(seed))
1445 def prepare_seed(self, seed):
1447 # see if seed was entered as hex
1452 return OLD_SEED_VERSION, str(seed)
1456 words = seed.split()
1457 seed = mnemonic.mn_decode(words)
1459 raise Exception("Invalid seed")
1461 return OLD_SEED_VERSION, seed
1463 def create_master_keys(self, password):
1464 seed = self.get_seed(password)
1465 mpk = OldAccount.mpk_from_seed(seed)
1466 self.storage.put('master_public_key', mpk, True)
1468 def get_master_public_key(self):
1469 return self.storage.get("master_public_key")
1471 def get_master_public_keys(self):
1472 return {'Main Account':self.get_master_public_key()}
1474 def create_accounts(self, password):
1475 mpk = self.storage.get("master_public_key")
1476 self.create_account(mpk)
1478 def create_account(self, mpk):
1479 self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
1480 self.save_accounts()
1482 def create_watching_only_wallet(self, mpk):
1483 self.seed_version = OLD_SEED_VERSION
1484 self.storage.put('seed_version', self.seed_version, True)
1485 self.storage.put('master_public_key', mpk, True)
1486 self.create_account(mpk)
1488 def get_seed(self, password):
1489 seed = pw_decode(self.seed, password).encode('utf8')
1492 def check_password(self, password):
1493 seed = self.get_seed(password)
1494 self.accounts[0].check_seed(seed)
1496 def get_mnemonic(self, password):
1498 s = self.get_seed(password)
1499 return ' '.join(mnemonic.mn_encode(s))
1501 def check_pending_accounts(self):
1505 # former WalletFactory
1506 class Wallet(object):
1507 """The main wallet "entry point".
1508 This class is actually a factory that will return a wallet of the correct
1509 type when passed a WalletStorage instance."""
1511 def __new__(self, storage):
1512 config = storage.config
1513 if config.get('bitkey', False):
1514 # if user requested support for Bitkey device,
1515 # import Bitkey driver
1516 from wallet_bitkey import WalletBitkey
1517 return WalletBitkey(config)
1519 if storage.get('wallet_type') == '2of2':
1520 return Wallet_2of2(storage)
1522 if storage.get('wallet_type') == '2of3':
1523 return Wallet_2of3(storage)
1525 if storage.get('wallet_type') == 'imported':
1526 return Imported_Wallet(storage)
1528 if not storage.file_exists:
1529 seed_version = NEW_SEED_VERSION if config.get('bip32') is True else OLD_SEED_VERSION
1531 seed_version = storage.get('seed_version')
1532 if not seed_version:
1533 seed_version = OLD_SEED_VERSION if len(storage.get('master_public_key')) == 128 else NEW_SEED_VERSION
1535 if seed_version == OLD_SEED_VERSION:
1536 return OldWallet(storage)
1537 elif seed_version == NEW_SEED_VERSION:
1538 return NewWallet(storage)
1540 msg = "This wallet seed is not supported."
1541 if seed_version in [5]:
1542 msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%seed_version
1547 def is_seed(self, seed):
1550 elif is_old_seed(seed):
1552 elif is_new_seed(seed):
1558 def is_mpk(self, mpk):
1566 return len(mpk) == 128
1569 deserialize_xkey(mpk)
1575 def is_address(self, text):
1578 for x in text.split():
1579 if not bitcoin.is_address(x):
1584 def is_private_key(self, text):
1587 for x in text.split():
1588 if not bitcoin.is_private_key(x):
1593 def from_seed(self, seed, storage):
1594 if is_old_seed(seed):
1596 elif is_new_seed(seed):
1602 def from_address(self, text, storage):
1603 w = Imported_Wallet(storage)
1604 for x in text.split():
1605 w.accounts[IMPORTED_ACCOUNT].add(x, None, None, None)
1610 def from_private_key(self, text, storage):
1611 w = Imported_Wallet(storage)
1612 for x in text.split():
1613 w.import_key(x, None)
1617 def from_mpk(self, mpk, storage):
1625 w = OldWallet(storage)
1627 w.create_watching_only_wallet(mpk)
1629 w = NewWallet(storage)
1630 w.create_watching_only_wallet(mpk)