electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit e84d087a640bbcc878b3c235c1a3e0186562c34d
parent c871a795825c62b57008ac8f82170f5da9650ed0
Author: thomasv <thomasv@gitorious>
Date:   Wed, 27 Feb 2013 09:04:22 +0100

accounts

Diffstat:
Melectrum | 2+-
Mlib/bitcoin.py | 63++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mlib/gui_qt.py | 125+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mlib/wallet.py | 218+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
4 files changed, 256 insertions(+), 152 deletions(-)

diff --git a/electrum b/electrum @@ -284,7 +284,7 @@ if __name__ == '__main__': sys.exit(0) - + # important warning if cmd in ['dumpprivkey', 'dumpprivkeys']: print_msg("WARNING: ALL your private keys are secret.") diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -415,8 +415,13 @@ def CKD_prime(K, c, n): class DeterministicSequence: """ Privatekey(type,n) = Master_private_key + H(n|S|type) """ - def __init__(self, master_public_key): + def __init__(self, master_public_key, mpk2 = None): self.master_public_key = master_public_key + if mpk2: + self.mpk2 = mpk2 + self.is_p2sh = True + else: + self.is_p2sh = False @classmethod def from_seed(klass, seed): @@ -434,9 +439,30 @@ class DeterministicSequence: seed = hashlib.sha256(seed + oldseed).digest() return string_to_number( seed ) - def get_sequence(self,n,for_change): + def get_sequence(self, n, for_change): return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key.decode('hex') ) ) + def get_address(self, for_change, n): + if not self.is_p2sh: + pubkey = self.get_pubkey(n, for_change) + address = public_key_to_bc_address( pubkey.decode('hex') ) + else: + pubkey1 = self.get_pubkey(n, for_change) + pubkey2 = self.get_pubkey2(n, for_change) + address = Transaction.multisig_script([pubkey1, pubkey2], 2)["address"] + return address + + #sec = self.p2sh_sequence.get_private_key(n, for_change, seed) + #addr = hash_160_to_bc_address(hash_160(txin["redeemScript"].decode('hex')), 5) + + def get_pubkey2(self, n, for_change): + curve = SECP256k1 + z = string_to_number( Hash( "%d:%d:"%(n, for_change) + self.mpk2.decode('hex') ) ) + master_public_key = ecdsa.VerifyingKey.from_string( self.mpk2.decode('hex'), curve = SECP256k1 ) + pubkey_point = master_public_key.pubkey.point + z*curve.generator + public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 ) + return '04' + public_key2.to_string().encode('hex') + def get_pubkey(self, n, for_change): curve = SECP256k1 z = self.get_sequence(n, for_change) @@ -467,13 +493,24 @@ class DeterministicSequence: return True -################################## transactions + def add_input_info(self, txin, account, is_change, n): + txin['electrumKeyID'] = (account, is_change, n) # used by the server to find the key + if not self.p2sh: + txin['pubkeysig'] = [(None, None)] + pk_addr = txin['address'] + else: + pubkey1 = self.get_pubkey(n, is_change) + pubkey2 = self.get_pubkey2(n, is_change) + pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key + txin['redeemScript'] = Transaction.multisig_script([pubkey1, pubkey2], 2)['redeemScript'] + return pk_addr +################################## transactions class Transaction: @@ -745,8 +782,24 @@ class Transaction: "hex":self.raw, "complete":self.is_complete } - if not self.is_complete and self.input_info: - out['input_info'] = json.dumps(self.input_info).replace(' ','') + if not self.is_complete: + extras = [] + for i in self.inputs: + e = { 'txid':i['tx_hash'], 'vout':i['index'], + 'scriptPubKey':i.get('raw_output_script'), + 'electrumKeyID':i.get('electrumKeyID'), + 'redeemScript':i.get('redeemScript'), + 'signatures':i.get('signatures'), + 'pubkeys':i.get('pubkeys'), + } + extras.append(e) + self.input_info = extras + + if self.input_info: + out['input_info'] = json.dumps(self.input_info).replace(' ','') + + + print "out", out return out diff --git a/lib/gui_qt.py b/lib/gui_qt.py @@ -387,7 +387,7 @@ def ok_cancel_buttons(dialog): default_column_widths = { "history":[40,140,350,140], "contacts":[350,330], - "receive":[[310],[50,310,200,130,130],[50,310,200,130,130]] } + "receive":[[310],[310,200,130,130],[310,200,130,130]] } class ElectrumWindow(QMainWindow): @@ -674,7 +674,7 @@ class ElectrumWindow(QMainWindow): self.recv_changed(item) - if column == 3: + if column == 2: address = str( item.text(column_addr) ) text = str( item.text(3) ) try: @@ -945,7 +945,7 @@ class ElectrumWindow(QMainWindow): if label: self.wallet.labels[tx.hash()] = label - if self.wallet.seed: + if tx.is_complete: h = self.wallet.send_tx(tx) waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait...")) status, msg = self.wallet.receive_tx( h ) @@ -966,6 +966,8 @@ class ElectrumWindow(QMainWindow): QMessageBox.warning(self, _('Error'), _('Could not write transaction to file'), _('OK')) + + def set_url(self, url): payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question) self.tabs.setCurrentIndex(1) @@ -1046,11 +1048,11 @@ class ElectrumWindow(QMainWindow): def create_receive_tab(self): - l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Requested'), _('Balance'), _('Tx')]) + l,w,hbox = self.create_list_tab([ _('Address'), _('Label'), _('Requested'), _('Balance'), _('Tx')]) l.setContextMenuPolicy(Qt.CustomContextMenu) l.customContextMenuRequested.connect(self.create_receive_menu) - self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2)) - self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2)) + self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1)) + self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1)) self.connect(l, SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'), lambda a,b: self.recv_changed(a)) self.receive_list = l self.receive_buttons_hbox = hbox @@ -1069,7 +1071,7 @@ class ElectrumWindow(QMainWindow): def save_column_widths(self): if self.receive_tab_mode == 0: - widths = [ self.receive_list.columnWidth(1) ] + widths = [ self.receive_list.columnWidth(0) ] else: widths = [] for i in range(self.receive_list.columnCount() -1): @@ -1116,7 +1118,7 @@ class ElectrumWindow(QMainWindow): item = self.receive_list.itemAt(position) if not item: return - addr = unicode(item.text(1)) + addr = unicode(item.text(0)) menu = QMenu() menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr)) if self.receive_tab_mode == 2: @@ -1186,13 +1188,9 @@ class ElectrumWindow(QMainWindow): def update_receive_item(self, item): - address = str( item.data(1,0).toString() ) - - flags = self.wallet.get_address_flags(address) - item.setData(0,0,flags) - + address = str(item.data(0,0).toString()) label = self.wallet.labels.get(address,'') - item.setData(2,0,label) + item.setData(1,0,label) try: amount, currency = self.wallet.requested_amounts.get(address, (None, None)) @@ -1200,60 +1198,81 @@ class ElectrumWindow(QMainWindow): amount, currency = None, None amount_str = amount + (' ' + currency if currency else '') if amount is not None else '' - item.setData(3,0,amount_str) + item.setData(2,0,amount_str) c, u = self.wallet.get_addr_balance(address) balance = format_satoshis( c + u, False, self.wallet.num_zeros ) - item.setData(4,0,balance) + item.setData(3,0,balance) if self.receive_tab_mode == 1: if address in self.wallet.frozen_addresses: - item.setBackgroundColor(1, QColor('lightblue')) + item.setBackgroundColor(0, QColor('lightblue')) elif address in self.wallet.prioritized_addresses: - item.setBackgroundColor(1, QColor('lightgreen')) + item.setBackgroundColor(0, QColor('lightgreen')) def update_receive_tab(self): l = self.receive_list l.clear() - l.setColumnHidden(0, not self.receive_tab_mode == 1) - l.setColumnHidden(3, not self.receive_tab_mode == 2) - l.setColumnHidden(4, self.receive_tab_mode == 0) - l.setColumnHidden(5, not self.receive_tab_mode == 1) - if self.receive_tab_mode ==0: + l.setColumnHidden(2, not self.receive_tab_mode == 2) + l.setColumnHidden(3, self.receive_tab_mode == 0) + l.setColumnHidden(4, not self.receive_tab_mode == 1) + if self.receive_tab_mode == 0: width = self.column_widths['receive'][0][0] - l.setColumnWidth(1, width) + l.setColumnWidth(0, width) else: for i,width in enumerate(self.column_widths['receive'][self.receive_tab_mode]): l.setColumnWidth(i, width) - gap = 0 - is_red = False - for address in self.wallet.all_addresses(): - if self.wallet.is_change(address) and self.receive_tab_mode != 1: - continue + for k, account in self.wallet.accounts.items(): + name = account.get('name',str(k)) + account_item = QTreeWidgetItem( [ name, '', '', '', ''] ) + l.addTopLevelItem(account_item) + account_item.setExpanded(True) - h = self.wallet.history.get(address,[]) - - if address in self.wallet.addresses: - if h == []: - gap += 1 - if gap > self.wallet.gap_limit: - is_red = True - else: - gap = 0 + for is_change in [0,1]: + name = "Receiving" if not is_change else "Change" + seq_item = QTreeWidgetItem( [ name, '', '', '', ''] ) + account_item.addChild(seq_item) + if not is_change: seq_item.setExpanded(True) + is_red = False + gap = 0 - num_tx = '*' if h == ['*'] else "%d"%len(h) - item = QTreeWidgetItem( [ '', address, '', '', '', num_tx] ) - item.setFont(0, QFont(MONOSPACE_FONT)) - item.setFont(1, QFont(MONOSPACE_FONT)) - item.setFont(3, QFont(MONOSPACE_FONT)) - self.update_receive_item(item) - if is_red and address in self.wallet.addresses: - item.setBackgroundColor(1, QColor('red')) - l.addTopLevelItem(item) + for address in account[is_change]: + h = self.wallet.history.get(address,[]) + + if not is_change: + if h == []: + gap += 1 + if gap > self.wallet.gap_limit: + is_red = True + else: + gap = 0 + + num_tx = '*' if h == ['*'] else "%d"%len(h) + item = QTreeWidgetItem( [ address, '', '', '', num_tx] ) + item.setFont(0, QFont(MONOSPACE_FONT)) + item.setFont(2, QFont(MONOSPACE_FONT)) + self.update_receive_item(item) + if is_red and not is_change: + item.setBackgroundColor(1, QColor('red')) + seq_item.addChild(item) + + if self.wallet.imported_keys: + account_item = QTreeWidgetItem( [ "Imported", '', '', '', ''] ) + l.addTopLevelItem(account_item) + account_item.setExpanded(True) + for address in self.wallet.imported_keys.keys(): + item = QTreeWidgetItem( [ address, '', '', '', ''] ) + item.setFont(0, QFont(MONOSPACE_FONT)) + item.setFont(2, QFont(MONOSPACE_FONT)) + self.update_receive_item(item) + if is_red and not is_change: + item.setBackgroundColor(1, QColor('red')) + account_item.addChild(item) + # we use column 1 because column 0 may be hidden l.setCurrentItem(l.topLevelItem(0),1) @@ -1391,8 +1410,8 @@ class ElectrumWindow(QMainWindow): dialog.exec_() - @staticmethod - def show_seed_dialog(wallet, parent=None): + @classmethod + def show_seed_dialog(self, wallet, parent=None): if not wallet.seed: QMessageBox.information(parent, _('Message'), _('No seed'), _('OK')) return @@ -1410,6 +1429,10 @@ class ElectrumWindow(QMainWindow): QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK')) return + self.show_seed(seed) + + @classmethod + def show_seed(self, seed): dialog = QDialog(None) dialog.setModal(1) dialog.setWindowTitle('Electrum' + ' - ' + _('Seed')) @@ -1625,8 +1648,8 @@ class ElectrumWindow(QMainWindow): self.qr_window.setVisible(False) #self.print_button.setHidden(self.qr_window is None or not self.qr_window.isVisible()) - self.receive_list.setColumnHidden(3, self.qr_window is None or not self.qr_window.isVisible()) - self.receive_list.setColumnWidth(2, 200) + self.receive_list.setColumnHidden(2, self.qr_window is None or not self.qr_window.isVisible()) + self.receive_list.setColumnWidth(1, 200) def question(self, msg): diff --git a/lib/wallet.py b/lib/wallet.py @@ -80,8 +80,6 @@ class Wallet: self.fee = int(config.get('fee',100000)) self.num_zeros = int(config.get('num_zeros',0)) self.use_encryption = config.get('use_encryption', False) - self.addresses = config.get('addresses', []) # receiving addresses visible for user - self.change_addresses = config.get('change_addresses', []) # addresses used as change self.seed = config.get('seed', '') # encrypted self.labels = config.get('labels', {}) self.aliases = config.get('aliases', {}) # aliases for addresses @@ -93,9 +91,15 @@ class Wallet: self.imported_keys = config.get('imported_keys',{}) self.history = config.get('addr_history',{}) # address -> list(txid, height) self.tx_height = config.get('tx_height',{}) + self.requested_amounts = config.get('requested_amounts',{}) + self.accounts = config.get('accounts', {}) # this should not include public keys + self.sequences = {} + + mpk1 = self.config.get('master_public_key') + self.sequences[0] = DeterministicSequence(mpk1) + if self.accounts.get(0) is None: + self.accounts[0] = { 0:[], 1:[], 'name':'Main account' } - master_public_key = config.get('master_public_key','') - self.sequence = DeterministicSequence(master_public_key) self.transactions = {} tx = config.get('transactions',{}) @@ -105,7 +109,6 @@ class Wallet: print_msg("Warning: Cannot deserialize transactions. skipping") - self.requested_amounts = config.get('requested_amounts',{}) # not saved self.prevout_values = {} # my own transaction outputs @@ -162,21 +165,33 @@ class Wallet: self.seed = seed self.config.set_key('seed', self.seed, True) self.config.set_key('seed_version', self.seed_version, True) - self.init_mpk(self.seed) - def init_mpk(self,seed): + self.init_main_account(self.seed) + + + def init_main_account(self, seed): # public key - self.sequence = DeterministicSequence.from_seed(seed) - self.config.set_key('master_public_key', self.sequence.master_public_key, True) + sequence = DeterministicSequence.from_seed(seed) + self.accounts[0] = { 0:[], 1:[], 'name':'Main account' } + self.sequences[0] = sequence + self.config.set_key('accounts', self.accounts, True) + + def all_addresses(self): - return self.addresses + self.change_addresses + self.imported_keys.keys() + o = self.imported_keys.keys() + for a in self.accounts.values(): + o += a[0] + o += a[1] + return o + def is_mine(self, address): return address in self.all_addresses() def is_change(self, address): - return address in self.change_addresses + #return address in self.change_addresses + return False def get_master_public_key(self): return self.sequence.master_public_key @@ -184,47 +199,40 @@ class Wallet: def get_address_index(self, address): if address in self.imported_keys.keys(): raise BaseException("imported key") - - 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 - return n,for_change + for account in self.accounts.keys(): + for for_change in [0,1]: + addresses = self.accounts[account][for_change] + for addr in addresses: + if address == addr: + return account, for_change, addresses.index(addr) + raise BaseException("not found") + def get_public_key(self, address): - n, for_change = self.get_address_index(address) - return self.sequence.get_pubkey(n, for_change) + account, n, for_change = self.get_address_index(address) + return self.sequences[account].get_pubkey(n, for_change) def decode_seed(self, password): seed = pw_decode(self.seed, password) - self.sequence.check_seed(seed) + self.sequences[0].check_seed(seed) return seed def get_private_key(self, address, password): - return self.get_private_keys([address], password)[address] + return self.get_private_keys([address], password).get(address) def get_private_keys(self, addresses, password): # decode seed in any case, in order to test the password seed = self.decode_seed(password) - secexp = self.sequence.stretch_key(seed) + secexp = self.sequences[0].stretch_key(seed) out = {} for address in addresses: if address in self.imported_keys.keys(): - pk = pw_decode( self.imported_keys[address], password ) + out[address] = pw_decode( self.imported_keys[address], password ) else: - 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", address) - pk = self.sequence.get_private_key_from_stretched_exponent(n, for_change, secexp) - out[address] = pk + account, for_change, n = self.get_address_index(address) + if account == 0: + out[address] = self.sequences[0].get_private_key_from_stretched_exponent(n, for_change, secexp) return out @@ -261,11 +269,11 @@ class Wallet: # find the address: if txin.get('electrumKeyID'): - n, for_change = txin.get('electrumKeyID') - sec = self.sequence.get_private_key(n, for_change, seed) - address = address_from_private_key(sec) - txin['address'] = address - private_keys[address] = sec + account, for_change, n = txin.get('electrumKeyID') + sec = self.sequences[account].get_private_key(n, for_change, seed) + addr = self.sequences[account].get_address(n, for_change) + txin['address'] = addr + private_keys[addr] = sec elif txin.get("redeemScript"): txin['address'] = hash_160_to_bc_address(hash_160(txin.get("redeemScript").decode('hex')), 5) @@ -280,26 +288,24 @@ class Wallet: tx.sign( private_keys ) - def sign_message(self, address, message, password): sec = self.get_private_key(address, password) key = regenerate_key(sec) compressed = is_compressed(sec) return key.sign_message(message, compressed, address) - - def create_new_address(self, for_change): - n = len(self.change_addresses) if for_change else len(self.addresses) - address = self.get_new_address(n, for_change) - if for_change: - self.change_addresses.append(address) - else: - self.addresses.append(address) + + + def create_new_address(self, account, for_change): + addresses = self.accounts[account][for_change] + n = len(addresses) + address = self.get_new_address( account, for_change, n) + self.accounts[account][for_change].append(address) self.history[address] = [] return address - def get_new_address(self, n, for_change): - pubkey = self.sequence.get_pubkey(n, for_change) - address = public_key_to_bc_address( pubkey.decode('hex') ) + + def get_new_address(self, account, for_change, n): + return self.sequences[account].get_address(for_change, n) print_msg( address ) return address @@ -311,18 +317,22 @@ class Wallet: return True elif value >= self.min_acceptable_gap(): - k = self.num_unused_trailing_addresses() - n = len(self.addresses) - k + value - self.addresses = self.addresses[0:n] + for key, account in self.accounts.items(): + addresses = account[0] + k = self.num_unused_trailing_addresses(addresses) + n = len(addresses) - k + value + addresses = addresses[0:n] + self.accounts[key][0] = addresses + self.gap_limit = value self.save() return True else: return False - def num_unused_trailing_addresses(self): + def num_unused_trailing_addresses(self, addresses): k = 0 - for a in self.addresses[::-1]: + for a in addresses[::-1]: if self.history.get(a):break k = k + 1 return k @@ -331,13 +341,16 @@ class Wallet: # fixme: this assumes wallet is synchronized n = 0 nmax = 0 - k = self.num_unused_trailing_addresses() - for a in self.addresses[0:-k]: - if self.history.get(a): - n = 0 - else: - n += 1 - if n > nmax: nmax = n + + for account in self.accounts.values(): + addresses = account[0] + k = self.num_unused_trailing_addresses(addresses) + for a in addresses[0:-k]: + if self.history.get(a): + n = 0 + else: + n += 1 + if n > nmax: nmax = n return nmax + 1 @@ -356,26 +369,32 @@ class Wallet: return age > 2 - def synchronize_sequence(self, addresses, n, for_change): + def synchronize_sequence(self, account, for_change): + limit = self.gap_limit_for_change if for_change else self.gap_limit + addresses = self.accounts[account][for_change] new_addresses = [] while True: - if len(self.addresses) < n: - new_addresses.append( self.create_new_address(for_change) ) + if len(addresses) < limit: + new_addresses.append( self.create_new_address(account, for_change) ) continue - if map( lambda a: self.address_is_old(a), addresses[-n:] ) == n*[False]: + if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]: break else: - new_addresses.append( self.create_new_address(for_change) ) + new_addresses.append( self.create_new_address(account, for_change) ) return new_addresses + def synchronize_account(self, account): + new = [] + new += self.synchronize_sequence(account, 0) + new += self.synchronize_sequence(account, 1) + return new + def synchronize(self): - if not self.sequence.master_public_key: - return [] - new_addresses = [] - new_addresses += self.synchronize_sequence(self.addresses, self.gap_limit, False) - new_addresses += self.synchronize_sequence(self.change_addresses, self.gap_limit_for_change, True) - return new_addresses + new = [] + for account in self.accounts.keys(): + new += self.synchronize_account(account) + return new def is_found(self): @@ -506,14 +525,27 @@ class Wallet: u += v return c, u - def get_balance(self): + def get_account_addresses(self, a): + ac = self.accounts[a] + return ac[0] + ac[1] + + + def get_account_balance(self, account): conf = unconf = 0 - for addr in self.all_addresses(): + for addr in self.get_account_addresses(account): c, u = self.get_addr_balance(addr) conf += c unconf += u return conf, unconf + def get_balance(self): + cc = uu = 0 + for a in self.accounts.keys(): + c, u = self.get_account_balance(a) + cc += c + uu += u + return cc, uu + def get_unspent_coins(self, domain=None): coins = [] @@ -557,7 +589,7 @@ class Wallet: addr = item.get('address') v = item.get('value') total += v - item['pubkeysig'] = [(None, None)] + inputs.append( item ) fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee if total >= amount + fee: break @@ -718,15 +750,18 @@ class Wallet: outputs = self.add_tx_change(outputs, amount, fee, total, change_addr) tx = Transaction.from_io(inputs, outputs) - for i in range(len(tx.inputs)): - addr = tx.inputs[i]['address'] - n, is_change = self.get_address_index(addr) - tx.input_info[i]['electrumKeyID'] = (n, is_change) - if not self.seed: - return tx - - self.sign_tx(tx, password) + pk_addresses = [] + for i in range(len(tx.inputs)): + txin = tx.inputs[i] + account, is_change, n = self.get_address_index(txin['address']) + pk_addr = self.sequences[account].add_input_info(txin, account, is_change, n) + pk_addresses.append(pk_addr) + + # get all private keys at once. + private_keys = self.get_private_keys(pk_addresses, password) + print "private keys", private_keys + tx.sign(private_keys) for address, x in outputs: if address not in self.addressbook and not self.is_mine(address): @@ -734,13 +769,7 @@ class Wallet: return tx - def sign_tx(self, tx, password): - private_keys = {} - for txin in tx.inputs: - addr = txin['address'] - sec = self.get_private_key(addr, password) - private_keys[addr] = sec - tx.sign(private_keys) + def sendtx(self, tx): # synchronous @@ -954,8 +983,7 @@ class Wallet: 'use_encryption': self.use_encryption, 'use_change': self.use_change, 'fee': self.fee, - 'addresses': self.addresses, - 'change_addresses': self.change_addresses, + 'accounts': self.accounts, 'addr_history': self.history, 'labels': self.labels, 'contacts': self.addressbook,