move http aliases to separate plugin
This commit is contained in:
parent
b6afa2455c
commit
93b98e1176
|
@ -61,8 +61,6 @@ elif platform.system() == 'Darwin':
|
|||
else:
|
||||
MONOSPACE_FONT = 'monospace'
|
||||
|
||||
ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
|
||||
|
||||
from electrum import ELECTRUM_VERSION
|
||||
import re
|
||||
|
||||
|
@ -426,23 +424,6 @@ class ElectrumWindow(QMainWindow):
|
|||
def timer_actions(self):
|
||||
self.run_hook('timer_actions')
|
||||
|
||||
if self.payto_e.hasFocus():
|
||||
return
|
||||
r = unicode( self.payto_e.text() )
|
||||
if r != self.previous_payto_e:
|
||||
self.previous_payto_e = r
|
||||
r = r.strip()
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
|
||||
try:
|
||||
to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
|
||||
except:
|
||||
return
|
||||
if to_address:
|
||||
s = r + ' <' + to_address + '>'
|
||||
self.payto_e.setText(s)
|
||||
|
||||
|
||||
|
||||
def update_status(self):
|
||||
if self.wallet.interface and self.wallet.interface.is_connected:
|
||||
if not self.wallet.up_to_date:
|
||||
|
@ -591,10 +572,11 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
def address_label_clicked(self, item, column, l, column_addr, column_label):
|
||||
if column == column_label and item.isSelected():
|
||||
is_editable = item.data(0, 32).toBool()
|
||||
if not is_editable:
|
||||
return
|
||||
addr = unicode( item.text(column_addr) )
|
||||
label = unicode( item.text(column_label) )
|
||||
if label in self.wallet.aliases.keys():
|
||||
return
|
||||
item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||||
l.editItem( item, column )
|
||||
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||||
|
@ -605,17 +587,14 @@ class ElectrumWindow(QMainWindow):
|
|||
if column == column_label:
|
||||
addr = unicode( item.text(column_addr) )
|
||||
text = unicode( item.text(column_label) )
|
||||
changed = False
|
||||
is_editable = item.data(0, 32).toBool()
|
||||
if not is_editable:
|
||||
return
|
||||
|
||||
if text in self.wallet.aliases.keys():
|
||||
print_error("Error: This is one of your aliases")
|
||||
label = self.wallet.labels.get(addr,'')
|
||||
item.setText(column_label, QString(label))
|
||||
else:
|
||||
changed = self.set_label(addr, text)
|
||||
if changed:
|
||||
self.update_history_tab()
|
||||
self.update_completions()
|
||||
changed = self.set_label(addr, text)
|
||||
if changed:
|
||||
self.update_history_tab()
|
||||
self.update_completions()
|
||||
|
||||
self.current_item_changed(item)
|
||||
|
||||
|
@ -778,8 +757,8 @@ class ElectrumWindow(QMainWindow):
|
|||
for addr,label in self.wallet.labels.items():
|
||||
if addr in self.wallet.addressbook:
|
||||
l.append( label + ' <' + addr + '>')
|
||||
l = l + self.wallet.aliases.keys()
|
||||
|
||||
self.run_hook('update_completions', l)
|
||||
self.completions.setStringList(l)
|
||||
|
||||
|
||||
|
@ -794,19 +773,9 @@ class ElectrumWindow(QMainWindow):
|
|||
r = unicode( self.payto_e.text() )
|
||||
r = r.strip()
|
||||
|
||||
# alias
|
||||
m1 = re.match(ALIAS_REGEXP, r)
|
||||
# label or alias, with address in brackets
|
||||
m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
|
||||
|
||||
if m1:
|
||||
to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
|
||||
if not to_address:
|
||||
return
|
||||
elif m2:
|
||||
to_address = m2.group(2)
|
||||
else:
|
||||
to_address = r
|
||||
m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
|
||||
to_address = m.group(2) if m else r
|
||||
|
||||
if not is_valid(to_address):
|
||||
QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
|
||||
|
@ -858,10 +827,19 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
|
||||
def set_url(self, url):
|
||||
payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
|
||||
address, amount, label, message, signature, identity, url = util.parse_url(url)
|
||||
|
||||
if label and self.wallet.labels.get(address) != label:
|
||||
if self.question('Give label "%s" to address %s ?'%(label,address)):
|
||||
if address not in self.wallet.addressbook and not self.wallet.is_mine(address):
|
||||
self.wallet.addressbook.append(address)
|
||||
self.set_label(address, label)
|
||||
|
||||
self.run_hook('set_url', url, self.show_message, self.question)
|
||||
|
||||
self.tabs.setCurrentIndex(1)
|
||||
label = self.wallet.labels.get(payto)
|
||||
m_addr = label + ' <'+ payto+'>' if label else payto
|
||||
label = self.wallet.labels.get(address)
|
||||
m_addr = label + ' <'+ address +'>' if label else address
|
||||
self.payto_e.setText(m_addr)
|
||||
|
||||
self.message_e.setText(message)
|
||||
|
@ -1029,50 +1007,41 @@ class ElectrumWindow(QMainWindow):
|
|||
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
|
||||
|
||||
|
||||
def payto(self, x, is_alias):
|
||||
if not x: return
|
||||
if is_alias:
|
||||
label = x
|
||||
m_addr = label
|
||||
else:
|
||||
addr = x
|
||||
label = self.wallet.labels.get(addr)
|
||||
m_addr = label + ' <' + addr + '>' if label else addr
|
||||
def payto(self, addr):
|
||||
if not addr: return
|
||||
label = self.wallet.labels.get(addr)
|
||||
m_addr = label + ' <' + addr + '>' if label else addr
|
||||
self.tabs.setCurrentIndex(1)
|
||||
self.payto_e.setText(m_addr)
|
||||
self.amount_e.setFocus()
|
||||
|
||||
def delete_contact(self, x, is_alias):
|
||||
|
||||
def delete_contact(self, x):
|
||||
if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
|
||||
if not is_alias and x in self.wallet.addressbook:
|
||||
if x in self.wallet.addressbook:
|
||||
self.wallet.addressbook.remove(x)
|
||||
self.set_label(x, None)
|
||||
elif is_alias and x in self.wallet.aliases:
|
||||
self.wallet.aliases.pop(x)
|
||||
self.update_history_tab()
|
||||
self.update_contacts_tab()
|
||||
self.update_completions()
|
||||
self.update_history_tab()
|
||||
self.update_contacts_tab()
|
||||
self.update_completions()
|
||||
|
||||
|
||||
def create_contact_menu(self, position):
|
||||
# fixme: this function apparently has a side effect.
|
||||
# if it is not called the menu pops up several times
|
||||
#self.contacts_list.selectedIndexes()
|
||||
|
||||
item = self.contacts_list.itemAt(position)
|
||||
if not item: return
|
||||
addr = unicode(item.text(0))
|
||||
label = unicode(item.text(1))
|
||||
is_alias = label in self.wallet.aliases.keys()
|
||||
x = label if is_alias else addr
|
||||
is_editable = item.data(0,32).toBool()
|
||||
payto_addr = item.data(0,33).toString()
|
||||
menu = QMenu()
|
||||
menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
|
||||
menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
|
||||
menu.addAction(_("Pay to"), lambda: self.payto(payto_addr))
|
||||
menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address")))
|
||||
if not is_alias:
|
||||
if is_editable:
|
||||
menu.addAction(_("Edit label"), lambda: self.edit_label(False))
|
||||
else:
|
||||
menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
|
||||
menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
|
||||
menu.addAction(_("Delete"), lambda: self.delete_contact(addr))
|
||||
|
||||
self.run_hook('create_contact_menu', menu, item)
|
||||
menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
|
||||
|
||||
|
||||
|
@ -1157,31 +1126,13 @@ class ElectrumWindow(QMainWindow):
|
|||
# we use column 1 because column 0 may be hidden
|
||||
l.setCurrentItem(l.topLevelItem(0),1)
|
||||
|
||||
def show_contact_details(self, m):
|
||||
a = self.wallet.aliases.get(m)
|
||||
if a:
|
||||
if a[0] in self.wallet.authorities.keys():
|
||||
s = self.wallet.authorities.get(a[0])
|
||||
else:
|
||||
s = "self-signed"
|
||||
msg = _('Alias:')+' '+ m + '\n'+_('Target address:')+' '+ a[1] + '\n\n'+_('Signed by:')+' ' + s + '\n'+_('Signing address:')+' ' + a[0]
|
||||
QMessageBox.information(self, 'Alias', msg, 'OK')
|
||||
|
||||
def update_contacts_tab(self):
|
||||
|
||||
l = self.contacts_list
|
||||
l.clear()
|
||||
|
||||
alias_targets = []
|
||||
for alias, v in self.wallet.aliases.items():
|
||||
s, target = v
|
||||
alias_targets.append(target)
|
||||
item = QTreeWidgetItem( [ target, alias, '-'] )
|
||||
item.setBackgroundColor(0, QColor('lightgray'))
|
||||
l.addTopLevelItem(item)
|
||||
|
||||
for address in self.wallet.addressbook:
|
||||
if address in alias_targets: continue
|
||||
label = self.wallet.labels.get(address,'')
|
||||
n = 0
|
||||
for tx in self.wallet.transactions.values():
|
||||
|
@ -1189,11 +1140,17 @@ class ElectrumWindow(QMainWindow):
|
|||
|
||||
item = QTreeWidgetItem( [ address, label, "%d"%n] )
|
||||
item.setFont(0, QFont(MONOSPACE_FONT))
|
||||
# 32 = label can be edited (bool)
|
||||
item.setData(0,32, True)
|
||||
# 33 = payto string
|
||||
item.setData(0,33, address)
|
||||
l.addTopLevelItem(item)
|
||||
|
||||
self.run_hook('update_contacts_tab', l)
|
||||
l.setCurrentItem(l.topLevelItem(0))
|
||||
|
||||
|
||||
|
||||
def create_console_tab(self):
|
||||
from qt_console import Console
|
||||
self.console = console = Console()
|
||||
|
|
|
@ -868,14 +868,14 @@ class ElectrumWindow:
|
|||
self.show_message(tx_details)
|
||||
elif treeview == self.contacts_treeview:
|
||||
m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0)
|
||||
a = self.wallet.aliases.get(m)
|
||||
if a:
|
||||
if a[0] in self.wallet.authorities.keys():
|
||||
s = self.wallet.authorities.get(a[0])
|
||||
else:
|
||||
s = "self-signed"
|
||||
msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
|
||||
self.show_message(msg)
|
||||
#a = self.wallet.aliases.get(m)
|
||||
#if a:
|
||||
# if a[0] in self.wallet.authorities.keys():
|
||||
# s = self.wallet.authorities.get(a[0])
|
||||
# else:
|
||||
# s = "self-signed"
|
||||
# msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
|
||||
# self.show_message(msg)
|
||||
|
||||
|
||||
def treeview_key_press(self, treeview, event):
|
||||
|
@ -890,14 +890,14 @@ class ElectrumWindow:
|
|||
self.show_message(tx_details)
|
||||
elif treeview == self.contacts_treeview:
|
||||
m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0)
|
||||
a = self.wallet.aliases.get(m)
|
||||
if a:
|
||||
if a[0] in self.wallet.authorities.keys():
|
||||
s = self.wallet.authorities.get(a[0])
|
||||
else:
|
||||
s = "self"
|
||||
msg = 'Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + s + '\nSigning address:' + a[0]
|
||||
self.show_message(msg)
|
||||
#a = self.wallet.aliases.get(m)
|
||||
#if a:
|
||||
# if a[0] in self.wallet.authorities.keys():
|
||||
# s = self.wallet.authorities.get(a[0])
|
||||
# else:
|
||||
# s = "self"
|
||||
# msg = 'Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + s + '\nSigning address:' + a[0]
|
||||
# self.show_message(msg)
|
||||
|
||||
return False
|
||||
|
||||
|
@ -1145,10 +1145,10 @@ class ElectrumWindow:
|
|||
def update_sending_tab(self):
|
||||
# detect addresses that are not mine in history, add them here...
|
||||
self.addressbook_list.clear()
|
||||
for alias, v in self.wallet.aliases.items():
|
||||
s, target = v
|
||||
label = self.wallet.labels.get(alias)
|
||||
self.addressbook_list.append((alias, label, '-'))
|
||||
#for alias, v in self.wallet.aliases.items():
|
||||
# s, target = v
|
||||
# label = self.wallet.labels.get(alias)
|
||||
# self.addressbook_list.append((alias, label, '-'))
|
||||
|
||||
for address in self.wallet.addressbook:
|
||||
label = self.wallet.labels.get(address)
|
||||
|
@ -1176,7 +1176,7 @@ class ElectrumWindow:
|
|||
|
||||
label, is_default_label = self.wallet.get_label(tx_hash)
|
||||
tooltip = tx_hash + "\n%d confirmations"%conf if tx_hash else ''
|
||||
details = self.wallet.get_tx_details(tx_hash)
|
||||
details = self.get_tx_details(tx_hash)
|
||||
|
||||
self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label,
|
||||
format_satoshis(value,True,self.wallet.num_zeros),
|
||||
|
@ -1184,6 +1184,40 @@ class ElectrumWindow:
|
|||
if cursor: self.history_treeview.set_cursor( cursor )
|
||||
|
||||
|
||||
def get_tx_details(self, tx_hash):
|
||||
import datetime
|
||||
if not tx_hash: return ''
|
||||
tx = self.wallet.transactions.get(tx_hash)
|
||||
is_mine, v, fee = self.wallet.get_tx_value(tx)
|
||||
conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
|
||||
|
||||
if timestamp:
|
||||
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
|
||||
else:
|
||||
time_str = 'pending'
|
||||
|
||||
inputs = map(lambda x: x.get('address'), tx.inputs)
|
||||
outputs = map(lambda x: x.get('address'), tx.d['outputs'])
|
||||
tx_details = "Transaction Details" +"\n\n" \
|
||||
+ "Transaction ID:\n" + tx_hash + "\n\n" \
|
||||
+ "Status: %d confirmations\n"%conf
|
||||
if is_mine:
|
||||
if fee:
|
||||
tx_details += "Amount sent: %s\n"% format_satoshis(v-fee, False) \
|
||||
+ "Transaction fee: %s\n"% format_satoshis(fee, False)
|
||||
else:
|
||||
tx_details += "Amount sent: %s\n"% format_satoshis(v, False) \
|
||||
+ "Transaction fee: unknown\n"
|
||||
else:
|
||||
tx_details += "Amount received: %s\n"% format_satoshis(v, False) \
|
||||
|
||||
tx_details += "Date: %s\n\n"%time_str \
|
||||
+ "Inputs:\n-"+ '\n-'.join(inputs) + "\n\n" \
|
||||
+ "Outputs:\n-"+ '\n-'.join(outputs)
|
||||
|
||||
return tx_details
|
||||
|
||||
|
||||
|
||||
def newaddress_dialog(self, w):
|
||||
|
||||
|
|
35
lib/util.py
35
lib/util.py
|
@ -1,4 +1,4 @@
|
|||
import os, sys
|
||||
import os, sys, re
|
||||
import platform
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
|
@ -147,3 +147,36 @@ def age(from_date, since_date = None, target_tz=None, include_seconds=False):
|
|||
return "about 1 year ago"
|
||||
else:
|
||||
return "over %d years ago" % (round(distance_in_minutes / 525600))
|
||||
|
||||
|
||||
|
||||
|
||||
# URL decode
|
||||
_ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE)
|
||||
urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x)
|
||||
|
||||
def parse_url(url):
|
||||
o = url[8:].split('?')
|
||||
address = o[0]
|
||||
if len(o)>1:
|
||||
params = o[1].split('&')
|
||||
else:
|
||||
params = []
|
||||
|
||||
amount = label = message = signature = identity = ''
|
||||
for p in params:
|
||||
k,v = p.split('=')
|
||||
uv = urldecode(v)
|
||||
if k == 'amount': amount = uv
|
||||
elif k == 'message': message = uv
|
||||
elif k == 'label': label = uv
|
||||
elif k == 'signature':
|
||||
identity, signature = uv.split(':')
|
||||
url = url.replace('&%s=%s'%(k,v),'')
|
||||
else:
|
||||
print k,v
|
||||
|
||||
return address, amount, label, message, signature, identity, url
|
||||
|
||||
|
||||
|
||||
|
|
181
lib/wallet.py
181
lib/wallet.py
|
@ -33,9 +33,6 @@ import time
|
|||
from util import print_msg, print_error, user_dir, format_satoshis
|
||||
from bitcoin import *
|
||||
|
||||
# URL decode
|
||||
_ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE)
|
||||
urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x)
|
||||
|
||||
# AES encryption
|
||||
EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
|
||||
|
@ -82,11 +79,8 @@ class Wallet:
|
|||
self.use_encryption = config.get('use_encryption', False)
|
||||
self.seed = config.get('seed', '') # encrypted
|
||||
self.labels = config.get('labels', {})
|
||||
self.aliases = config.get('aliases', {}) # aliases for addresses
|
||||
self.authorities = config.get('authorities', {}) # trusted addresses
|
||||
self.frozen_addresses = config.get('frozen_addresses',[])
|
||||
self.prioritized_addresses = config.get('prioritized_addresses',[])
|
||||
self.receipts = config.get('receipts',{}) # signed URIs
|
||||
self.addressbook = config.get('contacts', [])
|
||||
self.imported_keys = config.get('imported_keys',{})
|
||||
self.history = config.get('addr_history',{}) # address -> list(txid, height)
|
||||
|
@ -425,46 +419,6 @@ class Wallet:
|
|||
return tx.get_value(addresses, self.prevout_values)
|
||||
|
||||
|
||||
def get_tx_details(self, tx_hash):
|
||||
import datetime
|
||||
if not tx_hash: return ''
|
||||
tx = self.transactions.get(tx_hash)
|
||||
is_mine, v, fee = self.get_tx_value(tx)
|
||||
conf, timestamp = self.verifier.get_confirmations(tx_hash)
|
||||
|
||||
if timestamp:
|
||||
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
|
||||
else:
|
||||
time_str = 'pending'
|
||||
|
||||
inputs = map(lambda x: x.get('address'), tx.inputs)
|
||||
outputs = map(lambda x: x.get('address'), tx.d['outputs'])
|
||||
tx_details = "Transaction Details" +"\n\n" \
|
||||
+ "Transaction ID:\n" + tx_hash + "\n\n" \
|
||||
+ "Status: %d confirmations\n"%conf
|
||||
if is_mine:
|
||||
if fee:
|
||||
tx_details += "Amount sent: %s\n"% format_satoshis(v-fee, False) \
|
||||
+ "Transaction fee: %s\n"% format_satoshis(fee, False)
|
||||
else:
|
||||
tx_details += "Amount sent: %s\n"% format_satoshis(v, False) \
|
||||
+ "Transaction fee: unknown\n"
|
||||
else:
|
||||
tx_details += "Amount received: %s\n"% format_satoshis(v, False) \
|
||||
|
||||
tx_details += "Date: %s\n\n"%time_str \
|
||||
+ "Inputs:\n-"+ '\n-'.join(inputs) + "\n\n" \
|
||||
+ "Outputs:\n-"+ '\n-'.join(outputs)
|
||||
|
||||
r = self.receipts.get(tx_hash)
|
||||
if r:
|
||||
tx_details += "\n_______________________________________" \
|
||||
+ '\n\nSigned URI: ' + r[2] \
|
||||
+ "\n\nSigned by: " + r[0] \
|
||||
+ '\n\nSignature: ' + r[1]
|
||||
|
||||
return tx_details
|
||||
|
||||
|
||||
def update_tx_outputs(self, tx_hash):
|
||||
tx = self.transactions.get(tx_hash)
|
||||
|
@ -813,48 +767,6 @@ class Wallet:
|
|||
return True, out
|
||||
|
||||
|
||||
def read_alias(self, alias):
|
||||
# this might not be the right place for this function.
|
||||
import urllib
|
||||
|
||||
m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias)
|
||||
m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias)
|
||||
if m1:
|
||||
url = 'https://' + m1.group(2) + '/bitcoin.id/' + m1.group(1)
|
||||
elif m2:
|
||||
url = 'https://' + alias + '/bitcoin.id'
|
||||
else:
|
||||
return ''
|
||||
try:
|
||||
lines = urllib.urlopen(url).readlines()
|
||||
except:
|
||||
return ''
|
||||
|
||||
# line 0
|
||||
line = lines[0].strip().split(':')
|
||||
if len(line) == 1:
|
||||
auth_name = None
|
||||
target = signing_addr = line[0]
|
||||
else:
|
||||
target, auth_name, signing_addr, signature = line
|
||||
msg = "alias:%s:%s:%s"%(alias,target,auth_name)
|
||||
print msg, signature
|
||||
EC_KEY.verify_message(signing_addr, signature, msg)
|
||||
|
||||
# other lines are signed updates
|
||||
for line in lines[1:]:
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
line = line.split(':')
|
||||
previous = target
|
||||
print repr(line)
|
||||
target, signature = line
|
||||
EC_KEY.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
|
||||
|
||||
if not is_valid(target):
|
||||
raise ValueError("Invalid bitcoin address")
|
||||
|
||||
return target, signing_addr, auth_name
|
||||
|
||||
def update_password(self, seed, old_password, new_password):
|
||||
if new_password == '': new_password = None
|
||||
|
@ -868,96 +780,6 @@ class Wallet:
|
|||
self.imported_keys[k] = c
|
||||
self.save()
|
||||
|
||||
def get_alias(self, alias, interactive = False, show_message=None, question = None):
|
||||
try:
|
||||
target, signing_address, auth_name = self.read_alias(alias)
|
||||
except BaseException, e:
|
||||
# raise exception if verify fails (verify the chain)
|
||||
if interactive:
|
||||
show_message("Alias error: " + str(e))
|
||||
return
|
||||
|
||||
print target, signing_address, auth_name
|
||||
|
||||
if auth_name is None:
|
||||
a = self.aliases.get(alias)
|
||||
if not a:
|
||||
msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
|
||||
if interactive and question( msg ):
|
||||
self.aliases[alias] = (signing_address, target)
|
||||
else:
|
||||
target = None
|
||||
else:
|
||||
if signing_address != a[0]:
|
||||
msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias
|
||||
if interactive and question( msg ):
|
||||
self.aliases[alias] = (signing_address, target)
|
||||
else:
|
||||
target = None
|
||||
else:
|
||||
if signing_address not in self.authorities.keys():
|
||||
msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address)
|
||||
if interactive and question( msg ):
|
||||
self.authorities[signing_address] = auth_name
|
||||
else:
|
||||
target = None
|
||||
|
||||
if target:
|
||||
self.aliases[alias] = (signing_address, target)
|
||||
|
||||
return target
|
||||
|
||||
|
||||
def parse_url(self, url, show_message, question):
|
||||
o = url[8:].split('?')
|
||||
address = o[0]
|
||||
if len(o)>1:
|
||||
params = o[1].split('&')
|
||||
else:
|
||||
params = []
|
||||
|
||||
amount = label = message = signature = identity = ''
|
||||
for p in params:
|
||||
k,v = p.split('=')
|
||||
uv = urldecode(v)
|
||||
if k == 'amount': amount = uv
|
||||
elif k == 'message': message = uv
|
||||
elif k == 'label': label = uv
|
||||
elif k == 'signature':
|
||||
identity, signature = uv.split(':')
|
||||
url = url.replace('&%s=%s'%(k,v),'')
|
||||
else:
|
||||
print k,v
|
||||
|
||||
if label and self.labels.get(address) != label:
|
||||
if question('Give label "%s" to address %s ?'%(label,address)):
|
||||
if address not in self.addressbook and not self.is_mine(address):
|
||||
self.addressbook.append(address)
|
||||
self.labels[address] = label
|
||||
|
||||
if signature:
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
|
||||
signing_address = self.get_alias(identity, True, show_message, question)
|
||||
elif is_valid(identity):
|
||||
signing_address = identity
|
||||
else:
|
||||
signing_address = None
|
||||
if not signing_address:
|
||||
return
|
||||
try:
|
||||
EC_KEY.verify_message(signing_address, signature, url )
|
||||
self.receipt = (signing_address, signature, url)
|
||||
except:
|
||||
show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
|
||||
address = amount = label = identity = message = ''
|
||||
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', address):
|
||||
payto_address = self.get_alias(address, True, show_message, question)
|
||||
if payto_address:
|
||||
address = address + ' <' + payto_address + '>'
|
||||
|
||||
return address, amount, label, message, signature, identity, url
|
||||
|
||||
|
||||
|
||||
def freeze(self,addr):
|
||||
|
@ -1008,9 +830,6 @@ class Wallet:
|
|||
'labels': self.labels,
|
||||
'contacts': self.addressbook,
|
||||
'imported_keys': self.imported_keys,
|
||||
'aliases': self.aliases,
|
||||
'authorities': self.authorities,
|
||||
'receipts': self.receipts,
|
||||
'num_zeros': self.num_zeros,
|
||||
'frozen_addresses': self.frozen_addresses,
|
||||
'prioritized_addresses': self.prioritized_addresses,
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
import re
|
||||
import platform
|
||||
from decimal import Decimal
|
||||
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
import PyQt4.QtCore as QtCore
|
||||
import PyQt4.QtGui as QtGui
|
||||
|
||||
from electrum_gui.qrcodewidget import QRCodeWidget
|
||||
from electrum_gui import bmp, pyqrnative
|
||||
from electrum_gui.i18n import _
|
||||
|
||||
|
||||
ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
|
||||
|
||||
|
||||
config = {}
|
||||
|
||||
def get_info():
|
||||
return 'Aliases', _('Retrieve aliases using http.')
|
||||
|
||||
def init(self):
|
||||
global config
|
||||
config = self.config
|
||||
self.aliases = config.get('aliases', {}) # aliases for addresses
|
||||
self.authorities = config.get('authorities', {}) # trusted addresses
|
||||
self.receipts = config.get('receipts',{}) # signed URIs
|
||||
do_enable(self, is_enabled())
|
||||
|
||||
def is_enabled():
|
||||
return config.get('use_aliases') is True
|
||||
|
||||
def is_available():
|
||||
return True
|
||||
|
||||
|
||||
def toggle(gui):
|
||||
enabled = not is_enabled()
|
||||
config.set_key('use_aliases', enabled, True)
|
||||
do_enable(gui, enabled)
|
||||
return enabled
|
||||
|
||||
|
||||
def do_enable(gui, enabled):
|
||||
if enabled:
|
||||
gui.set_hook('timer_actions', timer_actions)
|
||||
gui.set_hook('set_url', set_url_hook)
|
||||
gui.set_hook('update_contacts_tab', update_contacts_tab_hook)
|
||||
gui.set_hook('update_completions', update_completions_hook)
|
||||
gui.set_hook('create_contact_menu', create_contact_menu_hook)
|
||||
else:
|
||||
gui.unset_hook('timer_actions', timer_actions)
|
||||
gui.unset_hook('set_url', set_url_hook)
|
||||
gui.unset_hook('update_contacts_tab', update_contacts_tab_hook)
|
||||
gui.unset_hook('update_completions', update_completions_hook)
|
||||
gui.unset_hook('create_contact_menu', create_contact_menu_hook)
|
||||
|
||||
|
||||
def timer_actions(self):
|
||||
if self.payto_e.hasFocus():
|
||||
return
|
||||
r = unicode( self.payto_e.text() )
|
||||
if r != self.previous_payto_e:
|
||||
self.previous_payto_e = r
|
||||
r = r.strip()
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
|
||||
try:
|
||||
to_address = get_alias(self, r, True, self.show_message, self.question)
|
||||
except:
|
||||
return
|
||||
if to_address:
|
||||
s = r + ' <' + to_address + '>'
|
||||
self.payto_e.setText(s)
|
||||
|
||||
|
||||
def get_alias(self, alias, interactive = False, show_message=None, question = None):
|
||||
try:
|
||||
target, signing_address, auth_name = read_alias(self, alias)
|
||||
except BaseException, e:
|
||||
# raise exception if verify fails (verify the chain)
|
||||
if interactive:
|
||||
show_message("Alias error: " + str(e))
|
||||
return
|
||||
|
||||
print target, signing_address, auth_name
|
||||
|
||||
if auth_name is None:
|
||||
a = self.aliases.get(alias)
|
||||
if not a:
|
||||
msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
|
||||
if interactive and question( msg ):
|
||||
self.aliases[alias] = (signing_address, target)
|
||||
else:
|
||||
target = None
|
||||
else:
|
||||
if signing_address != a[0]:
|
||||
msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias
|
||||
if interactive and question( msg ):
|
||||
self.aliases[alias] = (signing_address, target)
|
||||
else:
|
||||
target = None
|
||||
else:
|
||||
if signing_address not in self.authorities.keys():
|
||||
msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address)
|
||||
if interactive and question( msg ):
|
||||
self.authorities[signing_address] = auth_name
|
||||
else:
|
||||
target = None
|
||||
|
||||
if target:
|
||||
self.aliases[alias] = (signing_address, target)
|
||||
|
||||
return target
|
||||
|
||||
|
||||
|
||||
def read_alias(self, alias):
|
||||
import urllib
|
||||
|
||||
m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias)
|
||||
m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias)
|
||||
if m1:
|
||||
url = 'https://' + m1.group(2) + '/bitcoin.id/' + m1.group(1)
|
||||
elif m2:
|
||||
url = 'https://' + alias + '/bitcoin.id'
|
||||
else:
|
||||
return ''
|
||||
try:
|
||||
lines = urllib.urlopen(url).readlines()
|
||||
except:
|
||||
return ''
|
||||
|
||||
# line 0
|
||||
line = lines[0].strip().split(':')
|
||||
if len(line) == 1:
|
||||
auth_name = None
|
||||
target = signing_addr = line[0]
|
||||
else:
|
||||
target, auth_name, signing_addr, signature = line
|
||||
msg = "alias:%s:%s:%s"%(alias,target,auth_name)
|
||||
print msg, signature
|
||||
EC_KEY.verify_message(signing_addr, signature, msg)
|
||||
|
||||
# other lines are signed updates
|
||||
for line in lines[1:]:
|
||||
line = line.strip()
|
||||
if not line: continue
|
||||
line = line.split(':')
|
||||
previous = target
|
||||
print repr(line)
|
||||
target, signature = line
|
||||
EC_KEY.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
|
||||
|
||||
if not is_valid(target):
|
||||
raise ValueError("Invalid bitcoin address")
|
||||
|
||||
return target, signing_addr, auth_name
|
||||
|
||||
|
||||
def set_url_hook(self, url, show_message, question):
|
||||
payto, amount, label, message, signature, identity, url = util.parse_url(url)
|
||||
if signature:
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
|
||||
signing_address = get_alias(identity, True, show_message, question)
|
||||
elif is_valid(identity):
|
||||
signing_address = identity
|
||||
else:
|
||||
signing_address = None
|
||||
if not signing_address:
|
||||
return
|
||||
try:
|
||||
EC_KEY.verify_message(signing_address, signature, url )
|
||||
self.receipt = (signing_address, signature, url)
|
||||
except:
|
||||
show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
|
||||
address = amount = label = identity = message = ''
|
||||
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', address):
|
||||
payto_address = get_alias(address, True, show_message, question)
|
||||
if payto_address:
|
||||
address = address + ' <' + payto_address + '>'
|
||||
|
||||
return address, amount, label, message, signature, identity, url
|
||||
|
||||
|
||||
|
||||
def update_contacts_tab_hook(self, l):
|
||||
alias_targets = []
|
||||
for alias, v in self.aliases.items():
|
||||
s, target = v
|
||||
alias_targets.append(target)
|
||||
item = QTreeWidgetItem( [ target, alias, '-'] )
|
||||
item.setBackgroundColor(0, QColor('lightgray'))
|
||||
l.insertTopLevelItem(0,item)
|
||||
item.setData(0,32,False)
|
||||
item.setData(0,33,alias + ' <' + target + '>')
|
||||
|
||||
|
||||
|
||||
def update_completions_hook(self, l):
|
||||
l[:] = l + self.aliases.keys()
|
||||
|
||||
|
||||
def create_contact_menu_hook(self, menu, item):
|
||||
label = unicode(item.text(1))
|
||||
if label in self.aliases.keys():
|
||||
addr = unicode(item.text(0))
|
||||
label = unicode(item.text(1))
|
||||
menu.addAction(_("View alias details"), lambda: show_contact_details(self, label))
|
||||
menu.addAction(_("Delete alias"), lambda: delete_alias(self, label))
|
||||
|
||||
|
||||
def show_contact_details(self, m):
|
||||
a = self.aliases.get(m)
|
||||
if a:
|
||||
if a[0] in self.authorities.keys():
|
||||
s = self.authorities.get(a[0])
|
||||
else:
|
||||
s = "self-signed"
|
||||
msg = _('Alias:')+' '+ m + '\n'+_('Target address:')+' '+ a[1] + '\n\n'+_('Signed by:')+' ' + s + '\n'+_('Signing address:')+' ' + a[0]
|
||||
QMessageBox.information(self, 'Alias', msg, 'OK')
|
||||
|
||||
|
||||
def delete_alias(self, x):
|
||||
if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
|
||||
if x in self.aliases:
|
||||
self.aliases.pop(x)
|
||||
self.update_history_tab()
|
||||
self.update_contacts_tab()
|
||||
self.update_completions()
|
Loading…
Reference in New Issue