mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-31 17:31:36 +00:00
Merge branch 'master' of gitorious.org:electrum/electrum
This commit is contained in:
commit
eb060854a1
6 changed files with 546 additions and 121 deletions
|
@ -16,15 +16,22 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re,sys
|
||||
import re, sys, getpass
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
from wallet import Wallet
|
||||
from interface import Interface
|
||||
from decimal import Decimal
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
from wallet import format_satoshis
|
||||
|
||||
if __name__ == '__main__':
|
||||
known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import']
|
||||
known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','eval']
|
||||
|
||||
usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands))
|
||||
|
||||
|
@ -52,24 +59,30 @@ if __name__ == '__main__':
|
|||
import gui
|
||||
gui.init_wallet(wallet)
|
||||
gui = gui.BitcoinGUI(wallet)
|
||||
|
||||
if re.match('^bitcoin:', cmd):
|
||||
|
||||
o = cmd[8:].split('?')
|
||||
address = o[0]
|
||||
if len(o)>1:
|
||||
params = o[1].split('&')
|
||||
else:
|
||||
params = []
|
||||
cmd = 'gui'
|
||||
amount = ''
|
||||
label = ''
|
||||
|
||||
amount = label = message = signature = identity = ''
|
||||
for p in params:
|
||||
k,v = p.split('=')
|
||||
v = urldecode(v)
|
||||
if k=='amount': amount = v
|
||||
elif k=='label': label = v
|
||||
else: print k,v
|
||||
|
||||
gui.set_send_tab(address, amount, label)
|
||||
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(':')
|
||||
cmd = cmd.replace('&%s=%s'%(k,v),'')
|
||||
else:
|
||||
print k,v
|
||||
|
||||
gui.set_send_tab(address, amount, message, label, identity, signature, cmd)
|
||||
|
||||
gui.main()
|
||||
wallet.save()
|
||||
|
@ -98,7 +111,7 @@ if __name__ == '__main__':
|
|||
|
||||
host = raw_input("server (default:%s):"%wallet.interface.host)
|
||||
port = raw_input("port (default:%d):"%wallet.interface.port)
|
||||
fee = raw_input("fee (default:%f):"%(wallet.fee*1e-8))
|
||||
fee = raw_input("fee (default:%s):"%( str(Decimal(wallet.fee)/100000000)) )
|
||||
if fee: wallet.fee = float(fee)
|
||||
if host: wallet.interface.host = host
|
||||
if port: wallet.interface.port = int(port)
|
||||
|
@ -136,13 +149,13 @@ if __name__ == '__main__':
|
|||
cmd = 'help'
|
||||
|
||||
# open session
|
||||
if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress']:
|
||||
if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'eval']:
|
||||
wallet.interface.new_session(wallet.all_addresses(), wallet.electrum_version)
|
||||
wallet.update()
|
||||
wallet.save()
|
||||
|
||||
# commands needing password
|
||||
if cmd in ['payto', 'password', 'mktx', 'seed', 'import' ] or ( cmd=='addresses' and options.show_keys):
|
||||
if cmd in ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys):
|
||||
password = getpass.getpass('Password:') if wallet.use_encryption else None
|
||||
# check password
|
||||
try:
|
||||
|
@ -193,6 +206,8 @@ if __name__ == '__main__':
|
|||
print "syntax: mktx <recipient> <amount> [label]"
|
||||
elif cmd2 == 'seed':
|
||||
print "show generation seed of your wallet. password protected."
|
||||
elif cmd2 == 'eval':
|
||||
print "run python eval on an object"
|
||||
|
||||
elif cmd == 'seed':
|
||||
import mnemonic
|
||||
|
@ -211,21 +226,25 @@ if __name__ == '__main__':
|
|||
if addrs == []:
|
||||
c, u = wallet.get_balance()
|
||||
if u:
|
||||
print c*1e-8, u*1e-8
|
||||
print Decimal( c ) / 100000000 , Decimal( u ) / 100000000
|
||||
else:
|
||||
print c*1e-8
|
||||
print Decimal( c ) / 100000000
|
||||
else:
|
||||
for addr in addrs:
|
||||
c, u = wallet.get_addr_balance(addr)
|
||||
if u:
|
||||
print "%s %s, %s" % (addr, c*1e-8, u*1e-8)
|
||||
print "%s %s, %s" % (addr, str(Decimal(c)/100000000), str(Decimal(u)/100000000))
|
||||
else:
|
||||
print "%s %s" % (addr, c*1e-8)
|
||||
print "%s %s" % (addr, str(Decimal(c)/100000000))
|
||||
|
||||
elif cmd in [ 'contacts']:
|
||||
for addr in wallet.addressbook:
|
||||
print addr, " ", wallet.labels.get(addr)
|
||||
|
||||
elif cmd == 'eval':
|
||||
print eval(args[1])
|
||||
wallet.save()
|
||||
|
||||
elif cmd in [ 'addresses']:
|
||||
for addr in wallet.all_addresses():
|
||||
if options.show_all or not wallet.is_change(addr):
|
||||
|
@ -240,7 +259,7 @@ if __name__ == '__main__':
|
|||
for item in h:
|
||||
if item['is_in']: ni += 1
|
||||
else: no += 1
|
||||
b = "%d %d %f"%(no, ni, wallet.get_addr_balance(addr)[0]*1e-8)
|
||||
b = "%d %d %s"%(no, ni, str(Decimal(wallet.get_addr_balance(addr)[0])/100000000))
|
||||
else: b=''
|
||||
if options.show_keys:
|
||||
pk = wallet.get_private_key2(addr, password)
|
||||
|
@ -252,9 +271,8 @@ if __name__ == '__main__':
|
|||
b = 0
|
||||
for line in lines:
|
||||
import datetime
|
||||
v = 1.*line['value']/1e8
|
||||
v = line['value']
|
||||
b += v
|
||||
v_str = "%f"%v if v<0 else "+%f"%v
|
||||
try:
|
||||
time_str = datetime.datetime.fromtimestamp( line['nTime'])
|
||||
except:
|
||||
|
@ -264,8 +282,8 @@ if __name__ == '__main__':
|
|||
if not label: label = line['tx_hash']
|
||||
else: label = label + ' '*(64 - len(label) )
|
||||
|
||||
print time_str, " ", label, " ", v_str, " ", "%f"%b
|
||||
print "# balance: ", b
|
||||
print time_str , " " + label + " " + format_satoshis(v)+ " "+ format_satoshis(b)
|
||||
print "# balance: ", format_satoshis(b)
|
||||
|
||||
elif cmd == 'label':
|
||||
try:
|
||||
|
@ -323,3 +341,16 @@ if __name__ == '__main__':
|
|||
else:
|
||||
print "error: mismatch"
|
||||
|
||||
elif cmd == 'signmessage':
|
||||
address, message = args[1:3]
|
||||
print wallet.sign_message(address, message, password)
|
||||
|
||||
elif cmd == 'verifymessage':
|
||||
address, signature, message = args[1:4]
|
||||
try:
|
||||
wallet.verify_message(address, signature, message)
|
||||
print True
|
||||
except:
|
||||
print False
|
||||
|
||||
|
||||
|
|
324
client/gui.py
324
client/gui.py
|
@ -17,7 +17,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import thread, time, ast, sys
|
||||
import thread, time, ast, sys, re
|
||||
import socket, traceback
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
|
@ -28,12 +28,7 @@ from decimal import Decimal
|
|||
gtk.gdk.threads_init()
|
||||
APP_NAME = "Electrum"
|
||||
|
||||
def format_satoshis(x):
|
||||
s = str( Decimal(x) /100000000 )
|
||||
if not '.' in s: s += '.'
|
||||
p = s.find('.')
|
||||
s += " "*( 9 - ( len(s) - p ))
|
||||
return s
|
||||
from wallet import format_satoshis
|
||||
|
||||
def numbify(entry, is_int = False):
|
||||
text = entry.get_text().strip()
|
||||
|
@ -124,6 +119,7 @@ def init_wallet(wallet):
|
|||
|
||||
else:
|
||||
# ask for the server.
|
||||
wallet.interface.get_servers()
|
||||
run_network_dialog( wallet, parent=None )
|
||||
|
||||
# ask for seed and gap.
|
||||
|
@ -370,8 +366,9 @@ def run_network_dialog( wallet, parent ):
|
|||
if host!= wallet.interface.host or port!=wallet.interface.port:
|
||||
wallet.interface.host = host
|
||||
wallet.interface.set_port( port )
|
||||
wallet.save()
|
||||
wallet.interface.is_connected = False
|
||||
if parent:
|
||||
wallet.save()
|
||||
|
||||
|
||||
|
||||
|
@ -399,8 +396,8 @@ def password_line(label):
|
|||
password.show()
|
||||
return password, password_entry
|
||||
|
||||
def password_dialog():
|
||||
dialog = gtk.MessageDialog( None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
def password_dialog(parent):
|
||||
dialog = gtk.MessageDialog( parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, "Please enter your password.")
|
||||
dialog.get_image().set_visible(False)
|
||||
current_pw, current_pw_entry = password_line('Password:')
|
||||
|
@ -529,7 +526,7 @@ class BitcoinGUI:
|
|||
|
||||
def seedb(w, wallet):
|
||||
if wallet.use_encryption:
|
||||
password = password_dialog()
|
||||
password = password_dialog(self.window)
|
||||
if not password: return
|
||||
else: password = None
|
||||
show_seed_dialog(wallet, password, self.window)
|
||||
|
@ -568,7 +565,7 @@ class BitcoinGUI:
|
|||
|
||||
self.window.add(vbox)
|
||||
self.window.show_all()
|
||||
self.fee_box.hide()
|
||||
#self.fee_box.hide()
|
||||
|
||||
self.context_id = self.status_bar.get_context_id("statusbar")
|
||||
self.update_status_bar()
|
||||
|
@ -578,6 +575,27 @@ class BitcoinGUI:
|
|||
gobject.idle_add( self.update_status_bar )
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
def check_recipient_thread():
|
||||
old_r = ''
|
||||
while True:
|
||||
time.sleep(0.5)
|
||||
if self.payto_entry.is_focus():
|
||||
continue
|
||||
r = self.payto_entry.get_text()
|
||||
if r != old_r:
|
||||
old_r = r
|
||||
r = r.strip()
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
|
||||
try:
|
||||
to_address = self.get_alias(r, interactive=False)
|
||||
except:
|
||||
continue
|
||||
if to_address:
|
||||
s = r + ' <' + to_address + '>'
|
||||
gobject.idle_add( lambda: self.payto_entry.set_text(s) )
|
||||
|
||||
|
||||
def update_wallet_thread():
|
||||
while True:
|
||||
try:
|
||||
|
@ -625,6 +643,7 @@ class BitcoinGUI:
|
|||
|
||||
thread.start_new_thread(update_wallet_thread, ())
|
||||
thread.start_new_thread(update_status_bar_thread, ())
|
||||
thread.start_new_thread(check_recipient_thread, ())
|
||||
self.notebook.set_current_page(0)
|
||||
|
||||
|
||||
|
@ -635,60 +654,68 @@ class BitcoinGUI:
|
|||
|
||||
|
||||
def create_send_tab(self):
|
||||
|
||||
|
||||
page = vbox = gtk.VBox()
|
||||
page.show()
|
||||
|
||||
payto = gtk.HBox()
|
||||
payto_label = gtk.Label('Pay to:')
|
||||
payto_label.set_size_request(100,10)
|
||||
payto_label.show()
|
||||
payto_label.set_size_request(100,-1)
|
||||
payto.pack_start(payto_label, False)
|
||||
payto_entry = gtk.Entry()
|
||||
payto_entry.set_size_request(350, 26)
|
||||
payto_entry.show()
|
||||
payto_entry.set_size_request(450, 26)
|
||||
payto.pack_start(payto_entry, False)
|
||||
vbox.pack_start(payto, False, False, 5)
|
||||
|
||||
label = gtk.HBox()
|
||||
label_label = gtk.Label('Label:')
|
||||
label_label.set_size_request(100,10)
|
||||
label_label.show()
|
||||
label.pack_start(label_label, False)
|
||||
label_entry = gtk.Entry()
|
||||
label_entry.set_size_request(350, 26)
|
||||
label_entry.show()
|
||||
label.pack_start(label_entry, False)
|
||||
vbox.pack_start(label, False, False, 5)
|
||||
|
||||
message = gtk.HBox()
|
||||
message_label = gtk.Label('Description:')
|
||||
message_label.set_size_request(100,-1)
|
||||
message.pack_start(message_label, False)
|
||||
message_entry = gtk.Entry()
|
||||
message_entry.set_size_request(450, 26)
|
||||
message.pack_start(message_entry, False)
|
||||
vbox.pack_start(message, False, False, 5)
|
||||
|
||||
amount_box = gtk.HBox()
|
||||
amount_label = gtk.Label('Amount:')
|
||||
amount_label.set_size_request(100,-1)
|
||||
amount_label.show()
|
||||
amount_box.pack_start(amount_label, False)
|
||||
amount_entry = gtk.Entry()
|
||||
amount_entry.set_size_request(120, -1)
|
||||
amount_entry.show()
|
||||
amount_box.pack_start(amount_entry, False)
|
||||
vbox.pack_start(amount_box, False, False, 5)
|
||||
|
||||
send_button = gtk.Button("Send")
|
||||
send_button.show()
|
||||
amount_box.pack_start(send_button, False, False, 5)
|
||||
|
||||
self.fee_box = fee_box = gtk.HBox()
|
||||
fee_label = gtk.Label('Fee:')
|
||||
fee_label.set_size_request(100,10)
|
||||
fee_label.set_size_request(100,-1)
|
||||
fee_box.pack_start(fee_label, False)
|
||||
fee_entry = gtk.Entry()
|
||||
fee_entry.set_size_request(120, 26)
|
||||
fee_entry.set_has_frame(False)
|
||||
fee_entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee"))
|
||||
fee_entry.set_size_request(60, 26)
|
||||
fee_box.pack_start(fee_entry, False)
|
||||
|
||||
send_button.connect("clicked", self.do_send, (payto_entry, label_entry, amount_entry, fee_entry))
|
||||
vbox.pack_start(fee_box, False, False, 5)
|
||||
|
||||
end_box = gtk.HBox()
|
||||
empty_label = gtk.Label('')
|
||||
empty_label.set_size_request(100,-1)
|
||||
end_box.pack_start(empty_label, False)
|
||||
send_button = gtk.Button("Send")
|
||||
send_button.show()
|
||||
end_box.pack_start(send_button, False, False, 0)
|
||||
clear_button = gtk.Button("Clear")
|
||||
clear_button.show()
|
||||
end_box.pack_start(clear_button, False, False, 15)
|
||||
send_button.connect("clicked", self.do_send, (payto_entry, message_entry, amount_entry, fee_entry))
|
||||
clear_button.connect("clicked", self.do_clear, (payto_entry, message_entry, amount_entry, fee_entry))
|
||||
|
||||
vbox.pack_start(end_box, False, False, 5)
|
||||
|
||||
# display this line only if there is a signature
|
||||
payto_sig = gtk.HBox()
|
||||
payto_sig_id = gtk.Label('')
|
||||
payto_sig.pack_start(payto_sig_id, False)
|
||||
vbox.pack_start(payto_sig, True, True, 5)
|
||||
|
||||
|
||||
self.user_fee = False
|
||||
|
||||
def entry_changed( entry, is_fee ):
|
||||
|
@ -696,7 +723,8 @@ class BitcoinGUI:
|
|||
fee = numbify(fee_entry)
|
||||
if not is_fee: fee = None
|
||||
if amount is None:
|
||||
self.fee_box.hide(); return
|
||||
#self.fee_box.hide();
|
||||
return
|
||||
inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
|
||||
if not is_fee:
|
||||
fee_entry.set_text( str( Decimal( fee ) / 100000000 ) )
|
||||
|
@ -713,25 +741,68 @@ class BitcoinGUI:
|
|||
self.error = 'Not enough funds'
|
||||
|
||||
amount_entry.connect('changed', entry_changed, False)
|
||||
fee_entry.connect('changed', entry_changed, True)
|
||||
fee_entry.connect('changed', entry_changed, True)
|
||||
|
||||
self.payto_entry = payto_entry
|
||||
self.payto_amount_entry = amount_entry
|
||||
self.payto_label_entry = label_entry
|
||||
self.payto_fee_entry = fee_entry
|
||||
self.payto_sig_id = payto_sig_id
|
||||
self.payto_sig = payto_sig
|
||||
self.amount_entry = amount_entry
|
||||
self.message_entry = message_entry
|
||||
self.add_tab(page, 'Send')
|
||||
|
||||
def set_send_tab(self, address, amount, label):
|
||||
def set_frozen(self,entry,frozen):
|
||||
if frozen:
|
||||
entry.set_editable(False)
|
||||
entry.set_has_frame(False)
|
||||
entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee"))
|
||||
else:
|
||||
entry.set_editable(True)
|
||||
entry.set_has_frame(True)
|
||||
entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff"))
|
||||
|
||||
|
||||
def set_send_tab(self, payto, amount, message, label, identity, signature, cmd):
|
||||
self.notebook.set_current_page(1)
|
||||
self.payto_entry.set_text(address)
|
||||
self.payto_label_entry.set_text(label)
|
||||
self.payto_amount_entry.set_text(amount)
|
||||
|
||||
if signature:
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
|
||||
signing_address = self.get_alias(identity, interactive = True)
|
||||
elif self.wallet.is_valid(identity):
|
||||
signing_address = identity
|
||||
else:
|
||||
signing_address = None
|
||||
if not signing_address:
|
||||
return
|
||||
try:
|
||||
self.wallet.verify_message(signing_address, signature, cmd )
|
||||
self.wallet.receipt = (signing_address, signature, cmd)
|
||||
except:
|
||||
self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
|
||||
payto = amount = label = identity = message = ''
|
||||
|
||||
# redundant with aliases
|
||||
#if label and payto:
|
||||
# self.labels[payto] = label
|
||||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', payto):
|
||||
payto_address = self.get_alias(payto, interactive=True)
|
||||
if payto_address:
|
||||
payto = payto + ' <' + payto_address + '>'
|
||||
|
||||
self.payto_entry.set_text(payto)
|
||||
self.message_entry.set_text(message)
|
||||
self.amount_entry.set_text(amount)
|
||||
if identity:
|
||||
self.set_frozen(self.payto_entry,True)
|
||||
self.set_frozen(self.amount_entry,True)
|
||||
self.set_frozen(self.message_entry,True)
|
||||
self.payto_sig_id.set_text( ' The bitcoin URI was signed by ' + identity )
|
||||
else:
|
||||
self.payto_sig.set_visible(False)
|
||||
|
||||
def create_about_tab(self):
|
||||
page = gtk.VBox()
|
||||
page.show()
|
||||
#self.info = gtk.Label('')
|
||||
#self.info.set_selectable(True)
|
||||
#page.pack_start(self.info)
|
||||
tv = gtk.TextView()
|
||||
tv.set_editable(False)
|
||||
tv.set_cursor_visible(False)
|
||||
|
@ -739,14 +810,84 @@ class BitcoinGUI:
|
|||
self.info = tv.get_buffer()
|
||||
self.add_tab(page, 'Wall')
|
||||
|
||||
def do_clear(self, w, data):
|
||||
self.payto_sig.set_visible(False)
|
||||
self.payto_fee_entry.set_text('')
|
||||
for entry in [self.payto_entry,self.amount_entry,self.message_entry]:
|
||||
self.set_frozen(entry,False)
|
||||
entry.set_text('')
|
||||
|
||||
|
||||
def question(self,msg):
|
||||
dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg)
|
||||
dialog.show()
|
||||
result = dialog.run()
|
||||
dialog.destroy()
|
||||
return result == gtk.RESPONSE_OK
|
||||
|
||||
def get_alias(self, alias, interactive = False):
|
||||
try:
|
||||
target, signing_address, auth_name = self.wallet.read_alias(alias)
|
||||
except BaseException, e:
|
||||
# raise exception if verify fails (verify the chain)
|
||||
if interactive:
|
||||
self.show_message("Alias error: " + e.message)
|
||||
return
|
||||
|
||||
print target, signing_address, auth_name
|
||||
|
||||
if auth_name is None:
|
||||
a = self.wallet.aliases.get(alias)
|
||||
if not a:
|
||||
msg = "Warning: the alias '%s' is self-signed. The signing address is %s. Do you want to trust this alias?"%(alias,signing_address)
|
||||
if interactive and self.question( msg ):
|
||||
self.wallet.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 self.question( msg ):
|
||||
self.wallet.aliases[alias] = (signing_address, target)
|
||||
else:
|
||||
target = None
|
||||
else:
|
||||
if signing_address not in self.wallet.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 self.question( msg ):
|
||||
self.wallet.authorities[signing_address] = auth_name
|
||||
else:
|
||||
target = None
|
||||
|
||||
if target:
|
||||
self.wallet.aliases[alias] = (signing_address, target)
|
||||
self.update_sending_tab()
|
||||
|
||||
|
||||
return target
|
||||
|
||||
|
||||
|
||||
def do_send(self, w, data):
|
||||
payto_entry, label_entry, amount_entry, fee_entry = data
|
||||
|
||||
label = label_entry.get_text()
|
||||
r = payto_entry.get_text()
|
||||
r = r.strip()
|
||||
|
||||
m1 = re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r)
|
||||
m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
|
||||
|
||||
if m1:
|
||||
to_address = self.get_alias(r, interactive = True)
|
||||
if not to_address:
|
||||
return
|
||||
elif m2:
|
||||
to_address = m2.group(5)
|
||||
else:
|
||||
to_address = r
|
||||
|
||||
to_address = payto_entry.get_text()
|
||||
if not self.wallet.is_valid(to_address):
|
||||
self.show_message( "invalid bitcoin address")
|
||||
self.show_message( "invalid bitcoin address:\n"+to_address)
|
||||
return
|
||||
|
||||
try:
|
||||
|
@ -761,7 +902,7 @@ class BitcoinGUI:
|
|||
return
|
||||
|
||||
if self.wallet.use_encryption:
|
||||
password = password_dialog()
|
||||
password = password_dialog(self.window)
|
||||
if not password:
|
||||
return
|
||||
else:
|
||||
|
@ -782,7 +923,7 @@ class BitcoinGUI:
|
|||
label_entry.set_text("")
|
||||
amount_entry.set_text("")
|
||||
fee_entry.set_text("")
|
||||
self.fee_box.hide()
|
||||
#self.fee_box.hide()
|
||||
self.update_sending_tab()
|
||||
else:
|
||||
self.show_message( msg )
|
||||
|
@ -791,8 +932,20 @@ class BitcoinGUI:
|
|||
def treeview_button_press(self, treeview, event):
|
||||
if event.type == gtk.gdk._2BUTTON_PRESS:
|
||||
c = treeview.get_cursor()[0]
|
||||
tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
|
||||
self.show_message(tx_details)
|
||||
if treeview == self.history_treeview:
|
||||
tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
|
||||
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)
|
||||
|
||||
|
||||
def treeview_key_press(self, treeview, event):
|
||||
c = treeview.get_cursor()[0]
|
||||
|
@ -800,9 +953,21 @@ class BitcoinGUI:
|
|||
if c and c[0] == 0:
|
||||
treeview.parent.grab_focus()
|
||||
treeview.set_cursor((0,))
|
||||
elif event.keyval == gtk.keysyms.Return and treeview == self.history_treeview:
|
||||
tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
|
||||
self.show_message(tx_details)
|
||||
elif event.keyval == gtk.keysyms.Return:
|
||||
if treeview == self.history_treeview:
|
||||
tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
|
||||
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)
|
||||
|
||||
return False
|
||||
|
||||
def create_history_tab(self):
|
||||
|
@ -827,7 +992,7 @@ class BitcoinGUI:
|
|||
tvcolumn.pack_start(cell, False)
|
||||
tvcolumn.add_attribute(cell, 'text', 2)
|
||||
|
||||
tvcolumn = gtk.TreeViewColumn('Label')
|
||||
tvcolumn = gtk.TreeViewColumn('Description')
|
||||
treeview.append_column(tvcolumn)
|
||||
cell = gtk.CellRendererText()
|
||||
cell.set_property('foreground', 'grey')
|
||||
|
@ -892,7 +1057,10 @@ class BitcoinGUI:
|
|||
liststore = self.recv_list if is_recv else self.addressbook_list
|
||||
treeview = gtk.TreeView(model= liststore)
|
||||
treeview.connect('key-press-event', self.treeview_key_press)
|
||||
treeview.connect('button-press-event', self.treeview_button_press)
|
||||
treeview.show()
|
||||
if not is_recv:
|
||||
self.contacts_treeview = treeview
|
||||
|
||||
tvcolumn = gtk.TreeViewColumn('Address')
|
||||
treeview.append_column(tvcolumn)
|
||||
|
@ -993,7 +1161,7 @@ class BitcoinGUI:
|
|||
address = liststore.get_value( liststore.get_iter(path), 0)
|
||||
self.payto_entry.set_text( address )
|
||||
self.notebook.set_current_page(1)
|
||||
self.payto_amount_entry.grab_focus()
|
||||
self.amount_entry.grab_focus()
|
||||
|
||||
button.connect("clicked", payto, treeview, liststore)
|
||||
button.show()
|
||||
|
@ -1013,7 +1181,7 @@ class BitcoinGUI:
|
|||
self.status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
|
||||
self.network_button.set_tooltip_text("Trying to contact %s.\n%d blocks"%(self.wallet.interface.host, self.wallet.interface.blocks))
|
||||
text = "Balance: %s "%( format_satoshis(c) )
|
||||
if u: text += "[+ %s unconfirmed]"%( format_satoshis(u) )
|
||||
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
|
||||
if self.error: text = self.error
|
||||
self.status_bar.pop(self.context_id)
|
||||
self.status_bar.push(self.context_id, text)
|
||||
|
@ -1033,6 +1201,11 @@ class BitcoinGUI:
|
|||
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 address in self.wallet.addressbook:
|
||||
label = self.wallet.labels.get(address)
|
||||
n = 0
|
||||
|
@ -1062,16 +1235,23 @@ class BitcoinGUI:
|
|||
if is_default_label: label = tx['default_label']
|
||||
tooltip = tx_hash + "\n%d confirmations"%conf
|
||||
|
||||
tx = self.wallet.tx_history.get(tx_hash)
|
||||
details = "Transaction Details:\n\n"
|
||||
details+= "Transaction ID:\n" + tx_hash + "\n\n"
|
||||
details+= "Status: %d confirmations\n\n"%conf
|
||||
details+= "Date: %s\n\n"%time_str
|
||||
details+= "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n"
|
||||
details+= "Outputs:\n-"+ '\n-'.join(tx['outputs'])
|
||||
# tx = self.wallet.tx_history.get(tx_hash)
|
||||
details = "Transaction Details:\n\n" \
|
||||
+ "Transaction ID:\n" + tx_hash + "\n\n" \
|
||||
+ "Status: %d confirmations\n\n"%conf \
|
||||
+ "Date: %s\n\n"%time_str \
|
||||
+ "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
|
||||
+ "Outputs:\n-"+ '\n-'.join(tx['outputs'])
|
||||
r = self.wallet.receipts.get(tx_hash)
|
||||
if r:
|
||||
details += "\n_______________________________________" \
|
||||
+ '\n\nSigned URI: ' + r[2] \
|
||||
+ "\n\nSigned by: " + r[0] \
|
||||
+ '\n\nSignature: ' + r[1]
|
||||
|
||||
|
||||
self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label,
|
||||
('+' if v>0 else '') + format_satoshis(v), format_satoshis(balance), tooltip, details] )
|
||||
format_satoshis(v,True), format_satoshis(balance), tooltip, details] )
|
||||
if cursor: self.history_treeview.set_cursor( cursor )
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
import random, socket, ast
|
||||
|
||||
|
||||
|
||||
|
||||
class Interface:
|
||||
def __init__(self):
|
||||
self.servers = ['ecdsa.org','electrum.novit.ro'] # list of default servers
|
||||
|
|
94
client/msqr.py
Normal file
94
client/msqr.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
# from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/
|
||||
|
||||
def modular_sqrt(a, p):
|
||||
""" Find a quadratic residue (mod p) of 'a'. p
|
||||
must be an odd prime.
|
||||
|
||||
Solve the congruence of the form:
|
||||
x^2 = a (mod p)
|
||||
And returns x. Note that p - x is also a root.
|
||||
|
||||
0 is returned is no square root exists for
|
||||
these a and p.
|
||||
|
||||
The Tonelli-Shanks algorithm is used (except
|
||||
for some simple cases in which the solution
|
||||
is known from an identity). This algorithm
|
||||
runs in polynomial time (unless the
|
||||
generalized Riemann hypothesis is false).
|
||||
"""
|
||||
# Simple cases
|
||||
#
|
||||
if legendre_symbol(a, p) != 1:
|
||||
return 0
|
||||
elif a == 0:
|
||||
return 0
|
||||
elif p == 2:
|
||||
return p
|
||||
elif p % 4 == 3:
|
||||
return pow(a, (p + 1) / 4, p)
|
||||
|
||||
# Partition p-1 to s * 2^e for an odd s (i.e.
|
||||
# reduce all the powers of 2 from p-1)
|
||||
#
|
||||
s = p - 1
|
||||
e = 0
|
||||
while s % 2 == 0:
|
||||
s /= 2
|
||||
e += 1
|
||||
|
||||
# Find some 'n' with a legendre symbol n|p = -1.
|
||||
# Shouldn't take long.
|
||||
#
|
||||
n = 2
|
||||
while legendre_symbol(n, p) != -1:
|
||||
n += 1
|
||||
|
||||
# Here be dragons!
|
||||
# Read the paper "Square roots from 1; 24, 51,
|
||||
# 10 to Dan Shanks" by Ezra Brown for more
|
||||
# information
|
||||
#
|
||||
|
||||
# x is a guess of the square root that gets better
|
||||
# with each iteration.
|
||||
# b is the "fudge factor" - by how much we're off
|
||||
# with the guess. The invariant x^2 = ab (mod p)
|
||||
# is maintained throughout the loop.
|
||||
# g is used for successive powers of n to update
|
||||
# both a and b
|
||||
# r is the exponent - decreases with each update
|
||||
#
|
||||
x = pow(a, (s + 1) / 2, p)
|
||||
b = pow(a, s, p)
|
||||
g = pow(n, s, p)
|
||||
r = e
|
||||
|
||||
while True:
|
||||
t = b
|
||||
m = 0
|
||||
for m in xrange(r):
|
||||
if t == 1:
|
||||
break
|
||||
t = pow(t, 2, p)
|
||||
|
||||
if m == 0:
|
||||
return x
|
||||
|
||||
gs = pow(g, 2 ** (r - m - 1), p)
|
||||
g = (gs * gs) % p
|
||||
x = (x * gs) % p
|
||||
b = (b * g) % p
|
||||
r = m
|
||||
|
||||
def legendre_symbol(a, p):
|
||||
""" Compute the Legendre symbol a|p using
|
||||
Euler's criterion. p is a prime, a is
|
||||
relatively prime to p (if p divides
|
||||
a, then a|p = 0)
|
||||
|
||||
Returns 1 if a has a square root modulo
|
||||
p, -1 otherwise.
|
||||
"""
|
||||
ls = pow(a, (p - 1) / 2, p)
|
||||
return -1 if ls == p - 1 else ls
|
|
@ -1,2 +1,2 @@
|
|||
ELECTRUM_VERSION = "0.37"
|
||||
ELECTRUM_VERSION = "0.38"
|
||||
SEED_VERSION = 4 # bump this everytime the seed generation is modified
|
||||
|
|
165
client/wallet.py
165
client/wallet.py
|
@ -17,8 +17,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import sys, base64, os, re, hashlib, socket, getpass, copy, operator, ast, random
|
||||
from decimal import Decimal
|
||||
import sys, base64, os, re, hashlib, copy, operator, ast
|
||||
|
||||
try:
|
||||
import ecdsa
|
||||
|
@ -151,10 +150,6 @@ def int_to_hex(i, length=1):
|
|||
return s.decode('hex')[::-1].encode('hex')
|
||||
|
||||
|
||||
# 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
|
||||
EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
|
||||
DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
|
||||
|
@ -218,12 +213,22 @@ def raw_tx( inputs, outputs, for_sig = None ):
|
|||
|
||||
|
||||
|
||||
def format_satoshis(x, is_diff=False):
|
||||
from decimal import Decimal
|
||||
s = str( Decimal(x) /100000000 )
|
||||
if is_diff and x>0:
|
||||
s = "+" + s
|
||||
if not '.' in s: s += '.'
|
||||
p = s.find('.')
|
||||
s += " "*( 9 - ( len(s) - p ))
|
||||
s = " "*( 5 - ( p )) + s
|
||||
return s
|
||||
|
||||
|
||||
from version import ELECTRUM_VERSION, SEED_VERSION
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Wallet:
|
||||
def __init__(self, interface):
|
||||
|
||||
|
@ -242,6 +247,11 @@ class Wallet:
|
|||
self.status = {} # current status of addresses
|
||||
self.history = {}
|
||||
self.labels = {} # labels for addresses and transactions
|
||||
self.aliases = {} # aliases for addresses
|
||||
self.authorities = {} # trusted addresses
|
||||
|
||||
self.receipts = {} # signed URIs
|
||||
self.receipt = None # next receipt
|
||||
self.addressbook = [] # outgoing addresses, for payments
|
||||
|
||||
# not saved
|
||||
|
@ -287,7 +297,7 @@ class Wallet:
|
|||
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
|
||||
self.init_mpk(seed)
|
||||
# encrypt
|
||||
self.seed = wallet.pw_encode( seed, password )
|
||||
self.seed = self.pw_encode( seed, password )
|
||||
|
||||
def init_mpk(self,seed):
|
||||
# public key
|
||||
|
@ -323,7 +333,7 @@ class Wallet:
|
|||
def get_sequence(self,n,for_change):
|
||||
return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key ) )
|
||||
|
||||
def get_private_key2(self, address, password):
|
||||
def get_private_key(self, address, password):
|
||||
""" Privatekey(type,n) = Master_private_key + H(n|S|type) """
|
||||
order = generator_secp256k1.order()
|
||||
|
||||
|
@ -346,10 +356,65 @@ class Wallet:
|
|||
|
||||
pk = number_to_string(secexp,order)
|
||||
return pk
|
||||
|
||||
|
||||
|
||||
def create_new_address2(self, for_change):
|
||||
def msg_magic(self, message):
|
||||
return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message
|
||||
|
||||
def sign_message(self, address, message, password):
|
||||
private_key = ecdsa.SigningKey.from_string( self.get_private_key(address, password), curve = SECP256k1 )
|
||||
public_key = private_key.get_verifying_key()
|
||||
signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string )
|
||||
assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string)
|
||||
for i in range(4):
|
||||
sig = base64.b64encode( chr(27+i) + signature )
|
||||
try:
|
||||
self.verify_message( address, sig, message)
|
||||
return sig
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
raise BaseException("error: cannot sign message")
|
||||
|
||||
|
||||
def verify_message(self, address, signature, message):
|
||||
""" See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """
|
||||
from ecdsa import numbertheory, ellipticcurve, util
|
||||
import msqr
|
||||
curve = curve_secp256k1
|
||||
G = generator_secp256k1
|
||||
order = G.order()
|
||||
# extract r,s from signature
|
||||
sig = base64.b64decode(signature)
|
||||
if len(sig) != 65: raise BaseException("Wrong encoding")
|
||||
r,s = util.sigdecode_string(sig[1:], order)
|
||||
recid = ord(sig[0]) - 27
|
||||
# 1.1
|
||||
x = r + (recid/2) * order
|
||||
# 1.3
|
||||
alpha = ( x * x * x + curve.a() * x + curve.b() ) % curve.p()
|
||||
beta = msqr.modular_sqrt(alpha, curve.p())
|
||||
y = beta if (beta - recid) % 2 == 0 else curve.p() - beta
|
||||
# 1.4 the constructor checks that nR is at infinity
|
||||
R = ellipticcurve.Point(curve, x, y, order)
|
||||
# 1.5 compute e from message:
|
||||
h = Hash( self.msg_magic( message ) )
|
||||
e = string_to_number(h)
|
||||
minus_e = -e % order
|
||||
# 1.6 compute Q = r^-1 (sR - eG)
|
||||
inv_r = numbertheory.inverse_mod(r,order)
|
||||
Q = inv_r * ( s * R + minus_e * G )
|
||||
public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 )
|
||||
# check that Q is the public key
|
||||
public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
|
||||
# check that we get the original signing address
|
||||
addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() )
|
||||
# print addr
|
||||
if address != addr:
|
||||
print "bad signature"
|
||||
raise BaseException("Bad signature")
|
||||
|
||||
|
||||
def create_new_address(self, for_change):
|
||||
""" Publickey(type,n) = Master_public_key + H(n|S|type)*point """
|
||||
curve = SECP256k1
|
||||
n = len(self.change_addresses) if for_change else len(self.addresses)
|
||||
|
@ -374,12 +439,12 @@ class Wallet:
|
|||
is_new = False
|
||||
while True:
|
||||
if self.change_addresses == []:
|
||||
self.create_new_address2(True)
|
||||
self.create_new_address(True)
|
||||
is_new = True
|
||||
continue
|
||||
a = self.change_addresses[-1]
|
||||
if self.history.get(a):
|
||||
self.create_new_address2(True)
|
||||
self.create_new_address(True)
|
||||
is_new = True
|
||||
else:
|
||||
break
|
||||
|
@ -387,13 +452,13 @@ class Wallet:
|
|||
n = self.gap_limit
|
||||
while True:
|
||||
if len(self.addresses) < n:
|
||||
self.create_new_address2(False)
|
||||
self.create_new_address(False)
|
||||
is_new = True
|
||||
continue
|
||||
if map( lambda a: self.history.get(a), self.addresses[-n:] ) == n*[[]]:
|
||||
break
|
||||
else:
|
||||
self.create_new_address2(False)
|
||||
self.create_new_address(False)
|
||||
is_new = True
|
||||
|
||||
|
||||
|
@ -427,6 +492,9 @@ class Wallet:
|
|||
'labels':self.labels,
|
||||
'contacts':self.addressbook,
|
||||
'imported_keys':self.imported_keys,
|
||||
'aliases':self.aliases,
|
||||
'authorities':self.authorities,
|
||||
'receipts':self.receipts,
|
||||
}
|
||||
f = open(self.path,"w")
|
||||
f.write( repr(s) )
|
||||
|
@ -457,6 +525,9 @@ class Wallet:
|
|||
self.labels = d.get('labels')
|
||||
self.addressbook = d.get('contacts')
|
||||
self.imported_keys = d.get('imported_keys',{})
|
||||
self.aliases = d.get('aliases',{})
|
||||
self.authorities = d.get('authorities',{})
|
||||
self.receipts = d.get('receipts',{})
|
||||
except:
|
||||
raise BaseException(upgrade_msg)
|
||||
|
||||
|
@ -473,7 +544,7 @@ class Wallet:
|
|||
if not self.history.get(addr):
|
||||
n = n + 1
|
||||
if n < self.gap_limit:
|
||||
new_address = self.create_new_address2(False)
|
||||
new_address = self.create_new_address(False)
|
||||
self.history[new_address] = [] #get from server
|
||||
return True, new_address
|
||||
else:
|
||||
|
@ -559,7 +630,7 @@ class Wallet:
|
|||
s_inputs = []
|
||||
for i in range(len(inputs)):
|
||||
addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
|
||||
private_key = ecdsa.SigningKey.from_string( self.get_private_key2(addr, password), curve = SECP256k1 )
|
||||
private_key = ecdsa.SigningKey.from_string( self.get_private_key(addr, password), curve = SECP256k1 )
|
||||
public_key = private_key.get_verifying_key()
|
||||
pubkey = public_key.to_string()
|
||||
tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
|
||||
|
@ -634,19 +705,19 @@ class Wallet:
|
|||
def mktx(self, to_address, amount, label, password, fee=None):
|
||||
if not self.is_valid(to_address):
|
||||
raise BaseException("Invalid address")
|
||||
inputs, total, fee = wallet.choose_tx_inputs( amount, fee )
|
||||
inputs, total, fee = self.choose_tx_inputs( amount, fee )
|
||||
if not inputs:
|
||||
raise BaseException("Not enough funds")
|
||||
outputs = wallet.choose_tx_outputs( to_address, amount, fee, total )
|
||||
s_inputs = wallet.sign_inputs( inputs, outputs, password )
|
||||
outputs = self.choose_tx_outputs( to_address, amount, fee, total )
|
||||
s_inputs = self.sign_inputs( inputs, outputs, password )
|
||||
|
||||
tx = filter( raw_tx( s_inputs, outputs ) )
|
||||
if to_address not in self.addressbook:
|
||||
self.addressbook.append(to_address)
|
||||
if label:
|
||||
tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex')
|
||||
wallet.labels[tx_hash] = label
|
||||
wallet.save()
|
||||
self.labels[tx_hash] = label
|
||||
self.save()
|
||||
return tx
|
||||
|
||||
def sendtx(self, tx):
|
||||
|
@ -654,5 +725,51 @@ class Wallet:
|
|||
out = self.interface.send_tx(tx)
|
||||
if out != tx_hash:
|
||||
return False, "error: " + out
|
||||
if self.receipt:
|
||||
self.receipts[tx_hash] = self.receipt
|
||||
self.receipt = None
|
||||
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 = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1)
|
||||
elif m2:
|
||||
url = 'http://' + 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
|
||||
self.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
|
||||
self.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
|
||||
|
||||
if not self.is_valid(target):
|
||||
raise BaseException("Invalid bitcoin address")
|
||||
|
||||
return target, signing_addr, auth_name
|
||||
|
|
Loading…
Add table
Reference in a new issue