From e8189490e9a2ac6805c5800526bd1ea582e97daa Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 23 Apr 2015 12:16:46 +0200 Subject: [PATCH] store contacts in a separate file, shared between wallets --- gui/qt/main_window.py | 87 +++++++++++++++++++------------------------ lib/util.py | 30 +++++++++++++++ lib/wallet.py | 23 ------------ plugins/openalias.py | 2 +- 4 files changed, 70 insertions(+), 72 deletions(-) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index 4500bd984..fcf258c02 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -34,7 +34,7 @@ from electrum.plugins import run_hook import icons_rc -from electrum.util import format_satoshis, format_time, NotEnoughFunds +from electrum.util import format_satoshis, format_time, NotEnoughFunds, StoreDict from electrum import Transaction from electrum import mnemonic from electrum import util, bitcoin, commands, Interface, Wallet @@ -117,6 +117,7 @@ class ElectrumWindow(QMainWindow): self.app = gui_object.app self.invoices = InvoiceStore(self.config) + self.contacts = StoreDict(self.config, 'contacts') self.create_status_bar() self.need_update = threading.Event() @@ -201,11 +202,12 @@ class ElectrumWindow(QMainWindow): def load_wallet(self, wallet): import electrum self.wallet = wallet + # backward compatibility self.update_wallet_format() + self.import_old_contacts() # address used to create a dummy transaction and estimate transaction fee a = self.wallet.addresses(False) self.dummy_address = a[0] if a else None - self.accounts_expanded = self.wallet.storage.get('accounts_expanded',{}) self.current_account = self.wallet.storage.get("current_account", None) title = 'Electrum ' + self.wallet.electrum_version + ' - ' + os.path.basename(self.wallet.storage.path) @@ -224,16 +226,22 @@ class ElectrumWindow(QMainWindow): self.mpk_menu.setEnabled(self.wallet.is_deterministic()) self.import_menu.setVisible(self.wallet.can_import()) self.export_menu.setEnabled(self.wallet.can_export()) - self.update_lock_icon() self.update_buttons_on_seed() self.update_console() - self.clear_receive_tab() self.update_receive_tab() self.show() run_hook('load_wallet', wallet) + def import_old_contacts(self): + # backward compatibility: import contacts + addressbook = set(self.wallet.storage.get('contacts', [])) + for k in addressbook: + l = self.wallet.labels.get(k) + if bitcoin.is_address(k) and l: + self.contacts[l] = ('address', k) + self.wallet.storage.put('contacts', None) def update_wallet_format(self): # convert old-format imported keys @@ -248,7 +256,6 @@ class ElectrumWindow(QMainWindow): if self.wallet.get_master_public_keys() and self.wallet.addresses() == []: self.wallet.synchronize() - def open_wallet(self): wallet_folder = self.wallet.storage.path filename = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) ) @@ -1032,21 +1039,21 @@ class ElectrumWindow(QMainWindow): for item in self.pay_from: self.from_list.addTopLevelItem(QTreeWidgetItem( [format(item), self.format_amount(item['value']) ])) + def get_contact_payto(self, key): + _type, value = self.contacts.get(key) + return key + ' <' + value + '>' if _type == 'address' else key + def update_completions(self): - l = self.wallet.get_completions() + l = [self.get_contact_payto(key) for key in self.contacts.keys()] self.completions.setStringList(l) - def protected(func): return lambda s, *args: s.do_protect(func, args) - def read_send_tab(self): - if self.payment_request and self.payment_request.has_expired(): QMessageBox.warning(self, _('Error'), _('Payment request has expired'), _('OK')) return - label = unicode( self.message_e.text() ) if self.payment_request: @@ -1304,7 +1311,7 @@ class ElectrumWindow(QMainWindow): return self.create_list_tab(l) def create_contacts_tab(self): - l = MyTreeWidget(self, self.create_contact_menu, [_('Address'), _('Label'), _('Tx')], [350, None]) + l = MyTreeWidget(self, self.create_contact_menu, [_('Key'), _('Value'), _('Type')], [350, None, 130]) self.contacts_list = l return self.create_list_tab(l) @@ -1430,22 +1437,19 @@ class ElectrumWindow(QMainWindow): def payto(self, addr): - if not addr: return - label = self.wallet.labels.get(addr) - m_addr = label + ' <' + addr + '>' if label else addr + if not addr: + return self.tabs.setCurrentIndex(1) - self.payto_e.setText(m_addr) + self.payto_e.setText(addr) self.amount_e.setFocus() - def delete_contact(self, x): - if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")): - self.wallet.delete_contact(x) - self.wallet.set_label(x, None) - self.update_history_tab() - self.update_contacts_tab() - self.update_completions() - + if not self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")): + return + self.contacts.pop(x) + self.update_history_tab() + self.update_contacts_tab() + self.update_completions() def create_contact_menu(self, position): item = self.contacts_list.itemAt(position) @@ -1453,16 +1457,10 @@ class ElectrumWindow(QMainWindow): if not item: menu.addAction(_("New contact"), lambda: self.new_contact_dialog()) else: - addr = unicode(item.text(0)) - label = unicode(item.text(1)) - is_editable = item.data(0,32).toBool() - payto_addr = item.data(0,33).toString() - menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr)) - menu.addAction(_("Pay to"), lambda: self.payto(payto_addr)) - menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address"))) - if is_editable: - menu.addAction(_("Edit label"), lambda: self.contacts_list.edit_label(item)) - menu.addAction(_("Delete"), lambda: self.delete_contact(addr)) + key = unicode(item.text(0)) + menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(key)) + menu.addAction(_("Pay to"), lambda: self.payto(self.get_contact_payto(key))) + menu.addAction(_("Delete"), lambda: self.delete_contact(key)) run_hook('create_contact_menu', menu, item) menu.exec_(self.contacts_list.viewport().mapToGlobal(position)) @@ -1590,17 +1588,14 @@ class ElectrumWindow(QMainWindow): def update_contacts_tab(self): l = self.contacts_list item = l.currentItem() - current_address = item.data(0, Qt.UserRole).toString() if item else None + current_key = item.data(0, Qt.UserRole).toString() if item else None l.clear() - for address in self.wallet.addressbook: - label = self.wallet.labels.get(address,'') - n = self.wallet.get_num_tx(address) - item = QTreeWidgetItem( [ address, label, "%d"%n] ) - item.setFont(0, QFont(MONOSPACE_FONT)) - item.setData(0, Qt.UserRole, address) - item.setData(0, Qt.UserRole+1, True) + for key, v in self.contacts.items(): + _type, value = v + item = QTreeWidgetItem([key, value, _type]) + item.setData(0, Qt.UserRole, key) l.addTopLevelItem(item) - if address == current_address: + if key == current_key: l.setCurrentItem(item) run_hook('update_contacts_tab', l) @@ -1697,12 +1692,10 @@ class ElectrumWindow(QMainWindow): def new_contact_dialog(self): - d = QDialog(self) d.setWindowTitle(_("New Contact")) vbox = QVBoxLayout(d) - vbox.addWidget(QLabel(_('New Contact')+':')) - + vbox.addWidget(QLabel(_('New Contact') + ':')) grid = QGridLayout() line1 = QLineEdit() line2 = QLineEdit() @@ -1724,9 +1717,7 @@ class ElectrumWindow(QMainWindow): QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK')) return - self.wallet.add_contact(address) - if label: - self.wallet.set_label(address, label) + self.contacts[label] = ('address', address) self.update_contacts_tab() self.update_history_tab() diff --git a/lib/util.py b/lib/util.py index 8d164a2e4..c362aad35 100644 --- a/lib/util.py +++ b/lib/util.py @@ -397,3 +397,33 @@ class QueuePipe: def send_all(self, requests): for request in requests: self.send(request) + + + +class StoreDict(dict): + + def __init__(self, config, name): + self.config = config + self.path = os.path.join(self.config.path, name) + self.load() + + def load(self): + try: + with open(self.path, 'r') as f: + self.update(json.loads(f.read())) + except: + pass + + def save(self): + with open(self.path, 'w') as f: + s = json.dumps(self, indent=4, sort_keys=True) + r = f.write(s) + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + self.save() + + def pop(self, key): + if key in self.keys(): + dict.pop(self, key) + self.save() diff --git a/lib/wallet.py b/lib/wallet.py index 46c0f0979..2c344efd4 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -159,7 +159,6 @@ class Abstract_Wallet(object): self.seed = storage.get('seed', '') # encrypted self.labels = storage.get('labels', {}) self.frozen_addresses = storage.get('frozen_addresses',[]) - self.addressbook = set(storage.get('contacts', [])) self.history = storage.get('addr_history',{}) # address -> list(txid, height) self.fee_per_kb = int(storage.get('fee_per_kb', RECOMMENDED_FEE)) @@ -380,28 +379,6 @@ class Abstract_Wallet(object): def is_found(self): return self.history.values() != [[]] * len(self.history) - def add_contact(self, address, label=None): - self.addressbook.add(address) - self.storage.put('contacts', list(self.addressbook), True) - if label: - self.set_label(address, label) - - def delete_contact(self, addr): - if addr in self.addressbook: - self.addressbook.remove(addr) - self.storage.put('contacts', list(self.addressbook), True) - - def get_completions(self): - l = [] - for x in self.addressbook: - if bitcoin.is_address(x): - label = self.labels.get(x) - if label: - l.append( label + ' <' + x + '>') - else: - l.append(x) - return l - def get_num_tx(self, address): """ return number of transactions where address is involved """ return len(self.history.get(address, [])) diff --git a/plugins/openalias.py b/plugins/openalias.py index da403cb99..37be783e5 100644 --- a/plugins/openalias.py +++ b/plugins/openalias.py @@ -110,7 +110,7 @@ class Plugin(BasePlugin): self.win.previous_payto_e = new_url if self.config.get('openalias_autoadd') == 'checked': - self.win.wallet.add_contact(url, name) + self.win.contacts[url] = ('openalias', name) self.win.update_contacts_tab() self.win.payto_e.setFrozen(True)