Browse Source

store contacts in a separate file, shared between wallets

283
ThomasV 10 years ago
parent
commit
e8189490e9
  1. 87
      gui/qt/main_window.py
  2. 30
      lib/util.py
  3. 23
      lib/wallet.py
  4. 2
      plugins/openalias.py

87
gui/qt/main_window.py

@ -34,7 +34,7 @@ from electrum.plugins import run_hook
import icons_rc 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 Transaction
from electrum import mnemonic from electrum import mnemonic
from electrum import util, bitcoin, commands, Interface, Wallet from electrum import util, bitcoin, commands, Interface, Wallet
@ -117,6 +117,7 @@ class ElectrumWindow(QMainWindow):
self.app = gui_object.app self.app = gui_object.app
self.invoices = InvoiceStore(self.config) self.invoices = InvoiceStore(self.config)
self.contacts = StoreDict(self.config, 'contacts')
self.create_status_bar() self.create_status_bar()
self.need_update = threading.Event() self.need_update = threading.Event()
@ -201,11 +202,12 @@ class ElectrumWindow(QMainWindow):
def load_wallet(self, wallet): def load_wallet(self, wallet):
import electrum import electrum
self.wallet = wallet self.wallet = wallet
# backward compatibility
self.update_wallet_format() self.update_wallet_format()
self.import_old_contacts()
# address used to create a dummy transaction and estimate transaction fee # address used to create a dummy transaction and estimate transaction fee
a = self.wallet.addresses(False) a = self.wallet.addresses(False)
self.dummy_address = a[0] if a else None self.dummy_address = a[0] if a else None
self.accounts_expanded = self.wallet.storage.get('accounts_expanded',{}) self.accounts_expanded = self.wallet.storage.get('accounts_expanded',{})
self.current_account = self.wallet.storage.get("current_account", None) self.current_account = self.wallet.storage.get("current_account", None)
title = 'Electrum ' + self.wallet.electrum_version + ' - ' + os.path.basename(self.wallet.storage.path) 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.mpk_menu.setEnabled(self.wallet.is_deterministic())
self.import_menu.setVisible(self.wallet.can_import()) self.import_menu.setVisible(self.wallet.can_import())
self.export_menu.setEnabled(self.wallet.can_export()) self.export_menu.setEnabled(self.wallet.can_export())
self.update_lock_icon() self.update_lock_icon()
self.update_buttons_on_seed() self.update_buttons_on_seed()
self.update_console() self.update_console()
self.clear_receive_tab() self.clear_receive_tab()
self.update_receive_tab() self.update_receive_tab()
self.show() self.show()
run_hook('load_wallet', wallet) 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): def update_wallet_format(self):
# convert old-format imported keys # convert old-format imported keys
@ -248,7 +256,6 @@ class ElectrumWindow(QMainWindow):
if self.wallet.get_master_public_keys() and self.wallet.addresses() == []: if self.wallet.get_master_public_keys() and self.wallet.addresses() == []:
self.wallet.synchronize() self.wallet.synchronize()
def open_wallet(self): def open_wallet(self):
wallet_folder = self.wallet.storage.path wallet_folder = self.wallet.storage.path
filename = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) ) filename = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) )
@ -1032,21 +1039,21 @@ class ElectrumWindow(QMainWindow):
for item in self.pay_from: for item in self.pay_from:
self.from_list.addTopLevelItem(QTreeWidgetItem( [format(item), self.format_amount(item['value']) ])) 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): 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) self.completions.setStringList(l)
def protected(func): def protected(func):
return lambda s, *args: s.do_protect(func, args) return lambda s, *args: s.do_protect(func, args)
def read_send_tab(self): def read_send_tab(self):
if self.payment_request and self.payment_request.has_expired(): if self.payment_request and self.payment_request.has_expired():
QMessageBox.warning(self, _('Error'), _('Payment request has expired'), _('OK')) QMessageBox.warning(self, _('Error'), _('Payment request has expired'), _('OK'))
return return
label = unicode( self.message_e.text() ) label = unicode( self.message_e.text() )
if self.payment_request: if self.payment_request:
@ -1304,7 +1311,7 @@ class ElectrumWindow(QMainWindow):
return self.create_list_tab(l) return self.create_list_tab(l)
def create_contacts_tab(self): 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 self.contacts_list = l
return self.create_list_tab(l) return self.create_list_tab(l)
@ -1430,22 +1437,19 @@ class ElectrumWindow(QMainWindow):
def payto(self, addr): def payto(self, addr):
if not addr: return if not addr:
label = self.wallet.labels.get(addr) return
m_addr = label + ' <' + addr + '>' if label else addr
self.tabs.setCurrentIndex(1) self.tabs.setCurrentIndex(1)
self.payto_e.setText(m_addr) self.payto_e.setText(addr)
self.amount_e.setFocus() self.amount_e.setFocus()
def delete_contact(self, x): def delete_contact(self, x):
if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")): if not self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
self.wallet.delete_contact(x) return
self.wallet.set_label(x, None) self.contacts.pop(x)
self.update_history_tab() self.update_history_tab()
self.update_contacts_tab() self.update_contacts_tab()
self.update_completions() self.update_completions()
def create_contact_menu(self, position): def create_contact_menu(self, position):
item = self.contacts_list.itemAt(position) item = self.contacts_list.itemAt(position)
@ -1453,16 +1457,10 @@ class ElectrumWindow(QMainWindow):
if not item: if not item:
menu.addAction(_("New contact"), lambda: self.new_contact_dialog()) menu.addAction(_("New contact"), lambda: self.new_contact_dialog())
else: else:
addr = unicode(item.text(0)) key = unicode(item.text(0))
label = unicode(item.text(1)) menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(key))
is_editable = item.data(0,32).toBool() menu.addAction(_("Pay to"), lambda: self.payto(self.get_contact_payto(key)))
payto_addr = item.data(0,33).toString() menu.addAction(_("Delete"), lambda: self.delete_contact(key))
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))
run_hook('create_contact_menu', menu, item) run_hook('create_contact_menu', menu, item)
menu.exec_(self.contacts_list.viewport().mapToGlobal(position)) menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
@ -1590,17 +1588,14 @@ class ElectrumWindow(QMainWindow):
def update_contacts_tab(self): def update_contacts_tab(self):
l = self.contacts_list l = self.contacts_list
item = l.currentItem() 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() l.clear()
for address in self.wallet.addressbook: for key, v in self.contacts.items():
label = self.wallet.labels.get(address,'') _type, value = v
n = self.wallet.get_num_tx(address) item = QTreeWidgetItem([key, value, _type])
item = QTreeWidgetItem( [ address, label, "%d"%n] ) item.setData(0, Qt.UserRole, key)
item.setFont(0, QFont(MONOSPACE_FONT))
item.setData(0, Qt.UserRole, address)
item.setData(0, Qt.UserRole+1, True)
l.addTopLevelItem(item) l.addTopLevelItem(item)
if address == current_address: if key == current_key:
l.setCurrentItem(item) l.setCurrentItem(item)
run_hook('update_contacts_tab', l) run_hook('update_contacts_tab', l)
@ -1697,12 +1692,10 @@ class ElectrumWindow(QMainWindow):
def new_contact_dialog(self): def new_contact_dialog(self):
d = QDialog(self) d = QDialog(self)
d.setWindowTitle(_("New Contact")) d.setWindowTitle(_("New Contact"))
vbox = QVBoxLayout(d) vbox = QVBoxLayout(d)
vbox.addWidget(QLabel(_('New Contact')+':')) vbox.addWidget(QLabel(_('New Contact') + ':'))
grid = QGridLayout() grid = QGridLayout()
line1 = QLineEdit() line1 = QLineEdit()
line2 = QLineEdit() line2 = QLineEdit()
@ -1724,9 +1717,7 @@ class ElectrumWindow(QMainWindow):
QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK')) QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
return return
self.wallet.add_contact(address) self.contacts[label] = ('address', address)
if label:
self.wallet.set_label(address, label)
self.update_contacts_tab() self.update_contacts_tab()
self.update_history_tab() self.update_history_tab()

30
lib/util.py

@ -397,3 +397,33 @@ class QueuePipe:
def send_all(self, requests): def send_all(self, requests):
for request in requests: for request in requests:
self.send(request) 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()

23
lib/wallet.py

@ -159,7 +159,6 @@ class Abstract_Wallet(object):
self.seed = storage.get('seed', '') # encrypted self.seed = storage.get('seed', '') # encrypted
self.labels = storage.get('labels', {}) self.labels = storage.get('labels', {})
self.frozen_addresses = storage.get('frozen_addresses',[]) self.frozen_addresses = storage.get('frozen_addresses',[])
self.addressbook = set(storage.get('contacts', []))
self.history = storage.get('addr_history',{}) # address -> list(txid, height) self.history = storage.get('addr_history',{}) # address -> list(txid, height)
self.fee_per_kb = int(storage.get('fee_per_kb', RECOMMENDED_FEE)) self.fee_per_kb = int(storage.get('fee_per_kb', RECOMMENDED_FEE))
@ -380,28 +379,6 @@ class Abstract_Wallet(object):
def is_found(self): def is_found(self):
return self.history.values() != [[]] * len(self.history) 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): def get_num_tx(self, address):
""" return number of transactions where address is involved """ """ return number of transactions where address is involved """
return len(self.history.get(address, [])) return len(self.history.get(address, []))

2
plugins/openalias.py

@ -110,7 +110,7 @@ class Plugin(BasePlugin):
self.win.previous_payto_e = new_url self.win.previous_payto_e = new_url
if self.config.get('openalias_autoadd') == 'checked': 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.update_contacts_tab()
self.win.payto_e.setFrozen(True) self.win.payto_e.setFrozen(True)

Loading…
Cancel
Save