Browse Source

adding new protocol (draft)

master
thomasv 14 years ago
parent
commit
0fc0bdd667
  1. 13
      client/electrum
  2. 211
      client/interface.py
  3. 4
      client/remote_wallet.py
  4. 29
      client/wallet.py

13
client/electrum

@ -20,7 +20,6 @@ import re, sys, getpass
from optparse import OptionParser from optparse import OptionParser
from wallet import Wallet, SecretToASecret from wallet import Wallet, SecretToASecret
from interface import Interface
from decimal import Decimal from decimal import Decimal
@ -43,11 +42,13 @@ if __name__ == '__main__':
parser.add_option("-r", "--remote", dest="remote_url", default=None, help="URL of a remote wallet") parser.add_option("-r", "--remote", dest="remote_url", default=None, help="URL of a remote wallet")
options, args = parser.parse_args() options, args = parser.parse_args()
interface = Interface() wallet = Wallet()
wallet = Wallet(interface)
wallet.set_path(options.wallet_path) wallet.set_path(options.wallet_path)
wallet.read()
wallet.remote_url = options.remote_url wallet.remote_url = options.remote_url
interface = wallet.interface
if len(args)==0: if len(args)==0:
url = None url = None
cmd = 'gui' cmd = 'gui'
@ -71,7 +72,7 @@ if __name__ == '__main__':
gui = gui.ElectrumGui(wallet) gui = gui.ElectrumGui(wallet)
try: try:
found = wallet.read() found = wallet.file_exists
if not found: if not found:
found = gui.restore_or_create() found = gui.restore_or_create()
except BaseException, e: except BaseException, e:
@ -90,14 +91,14 @@ if __name__ == '__main__':
if cmd not in known_commands: if cmd not in known_commands:
cmd = 'help' cmd = 'help'
if not wallet.read() and cmd not in ['help','create','restore']: if not wallet.file_exists and cmd not in ['help','create','restore']:
print "Wallet file not found." print "Wallet file not found."
print "Type 'electrum.py create' to create a new wallet, or provide a path to a wallet with the -d option" print "Type 'electrum.py create' to create a new wallet, or provide a path to a wallet with the -d option"
sys.exit(0) sys.exit(0)
if cmd in ['create', 'restore']: if cmd in ['create', 'restore']:
import mnemonic import mnemonic
if wallet.read(): if wallet.file_exists:
print "remove the existing wallet first!" print "remove the existing wallet first!"
sys.exit(0) sys.exit(0)
password = getpass.getpass("Password (hit return if you do not wish to encrypt your wallet):") password = getpass.getpass("Password (hit return if you do not wish to encrypt your wallet):")

211
client/interface.py

@ -20,7 +20,7 @@
import random, socket, ast import random, socket, ast
import thread, traceback, sys, time import thread, traceback, sys, time, json
DEFAULT_TIMEOUT=5 DEFAULT_TIMEOUT=5
@ -32,22 +32,49 @@ class Interface:
self.rtime = 0 self.rtime = 0
self.blocks = 0 self.blocks = 0
self.message = '' self.message = ''
self.set_port(50000)
self.is_connected = False self.is_connected = False
self.was_updated = True # fixme: use a semaphore self.was_updated = True # fixme: use a semaphore
self.was_polled = False # True after the first poll self.was_polled = False # True after the first poll
def set_port(self, port_number): def send_tx(self, data):
self.port = port_number out = self.handler('blockchain.transaction.broadcast', data )
if self.use_http(): return out
self.handler = self.http_json_handler
else: def retrieve_history(self, address):
self.handler = self.native_handler out = self.handler('blockchain.address.get_history', address )
return out
def get_servers(self):
thread.start_new_thread(self.update_servers_thread, ())
def set_server(self, host, port):
if host!= self.host or port!=self.port:
self.host = host
self.port = port
self.is_connected = False
def update_servers_thread(self):
pass
class NativeInterface(Interface):
"""This is the original Electrum protocol. It uses polling, and a non-persistent tcp connection"""
def use_http(self): def __init__(self, host=None, port=50000):
return self.port in [80,81,8080,8081] Interface.__init__(self)
if host: self.host = host
self.port = port
def native_handler(self, method, params = ''): def new_session(self, addresses, version):
self.was_polled = False
out = self.handler('session.new', [ version, addresses ] )
self.session_id, self.message = ast.literal_eval( out )
def update_session(self, addresses):
out = self.handler('session.update', [ self.session_id, addresses ] )
return out
def handler(self, method, params = ''):
import time import time
cmds = {'session.new':'new_session', cmds = {'session.new':'new_session',
'peers':'peers', 'peers':'peers',
@ -76,69 +103,8 @@ class Interface:
out = ast.literal_eval( out ) out = ast.literal_eval( out )
return out return out
def http_json_handler(self, method, params = []):
import urllib2, json, time
if type(params) != type([]): params = [ params ]
t1 = time.time()
data = { 'method':method, 'id':'jsonrpc', 'params':params }
data_json = json.dumps(data)
host = 'http://%s:%d'%( self.host if cmd!='peers' else self.peers_server, self.port )
req = urllib2.Request(host, data_json, {'content-type': 'application/json'})
response_stream = urllib2.urlopen(req)
response = json.loads( response_stream.read() )
out = response.get('result')
if not out:
print response
self.rtime = time.time() - t1
self.is_connected = True
return out
def send_tx(self, data):
out = self.handler('blockchain.transaction.broadcast', data )
return out
def retrieve_history(self, address):
out = self.handler('blockchain.address.get_history', address )
return out
def poll(self):
out = self.handler('session.poll', self.session_id )
blocks, changed_addr = ast.literal_eval( out )
if blocks == -1: raise BaseException("session not found")
self.blocks = int(blocks)
if changed_addr: self.was_updated = True
self.was_polled = True
return changed_addr
def new_session(self, addresses, version):
self.was_polled = False
out = self.handler('session.new', [ version, addresses ] )
self.session_id, self.message = ast.literal_eval( out )
def update_session(self, addresses):
out = self.handler('session.update', [ self.session_id, addresses ] )
return out
def update_servers_thread(self):
# if my server is not reachable, I should get the list from one of the default servers
# requesting servers could be an independent process
while True:
for server in self.default_servers:
try:
self.peers_server = server
out = self.handler('peers')
self.servers = map( lambda x:x[1], out )
# print "Received server list from %s" % self.peers_server, out
break
except socket.timeout:
continue
except socket.error:
continue
time.sleep(5*60)
def poll_interval(self): def poll_interval(self):
return 15 if self.use_http() else 5 return 5
def update_wallet(self, wallet): def update_wallet(self, wallet):
is_new = False is_new = False
@ -158,6 +124,15 @@ class Interface:
else: else:
return False return False
def poll(self):
out = self.handler('session.poll', self.session_id )
blocks, changed_addr = ast.literal_eval( out )
if blocks == -1: raise BaseException("session not found")
self.blocks = int(blocks)
if changed_addr: self.was_updated = True
self.was_polled = True
return changed_addr
def update_wallet_thread(self, wallet): def update_wallet_thread(self, wallet):
while True: while True:
try: try:
@ -195,15 +170,89 @@ class Interface:
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
break break
def start(self, wallet): def start(self, wallet):
thread.start_new_thread(self.update_wallet_thread, (wallet,)) thread.start_new_thread(self.update_wallet_thread, (wallet,))
def get_servers(self): def update_servers_thread(self):
thread.start_new_thread(self.update_servers_thread, ()) # if my server is not reachable, I should get the list from one of the default servers
# requesting servers could be an independent process
while True:
for server in self.default_servers:
try:
self.peers_server = server
out = self.handler('peers')
self.servers = map( lambda x:x[1], out )
# print "Received server list from %s" % self.peers_server, out
break
except socket.timeout:
continue
except socket.error:
continue
except:
traceback.print_exc(file=sys.stdout)
def set_server(self, host, port): time.sleep(5*60)
if host!= self.host or port!=self.port:
self.host = host
self.set_port( port )
self.is_connected = False
class TCPInterface(Interface):
"""json-rpc over TCP"""
def __init__(self, host=None, port=50001):
Interface.__init__(self)
if host: self.host = host
self.port = 50001
self.s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
self.s.connect(( self.host, self.port))
def send(self, cmd, params = []):
request = json.dumps( { 'method':cmd, 'params':params } )
self.s.send( request + '\n' )
def listen_thread(self, wallet):
out = ''
while True:
msg = self.s.recv(1024)
out += msg
s = out.find('\n')
if s!=-1:
c = out[0:s]
out = out[s+1:]
c = json.loads(c)
cmd = c.get('method')
if cmd == 'server.banner':
self.message = c.get('result')
else:
print "received message:", c
def start(self, wallet):
thread.start_new_thread(self.listen_thread, (wallet,))
self.send('client.version', wallet.electrum_version)
self.send('server.banner', None)
for address in wallet.all_addresses():
self.send('blockchain.address.subscribe', address)
class HttpInterface(Interface):
def __init__(self):
self.port = 8081
def handler(self, method, params = []):
import urllib2, json, time
if type(params) != type([]): params = [ params ]
t1 = time.time()
data = { 'method':method, 'id':'jsonrpc', 'params':params }
data_json = json.dumps(data)
host = 'http://%s:%d'%( self.host if cmd!='peers' else self.peers_server, self.port )
req = urllib2.Request(host, data_json, {'content-type': 'application/json'})
response_stream = urllib2.urlopen(req)
response = json.loads( response_stream.read() )
out = response.get('result')
if not out:
print response
self.rtime = time.time() - t1
self.is_connected = True
return out

4
client/remote_wallet.py

@ -21,7 +21,6 @@ import time, thread, sys, socket
# see http://code.google.com/p/jsonrpclib/ # see http://code.google.com/p/jsonrpclib/
import jsonrpclib import jsonrpclib
from wallet import Wallet from wallet import Wallet
from interface import Interface
""" """
Simple wallet daemon for webservers. Simple wallet daemon for webservers.
@ -41,8 +40,7 @@ port = 8444
wallet_path = 'wallet_path' wallet_path = 'wallet_path'
username = 'foo' username = 'foo'
password = 'bar' password = 'bar'
interface = Interface() wallet = Wallet()
wallet = Wallet(interface)
stopping = False stopping = False

29
client/wallet.py

@ -234,7 +234,7 @@ from version import ELECTRUM_VERSION, SEED_VERSION
class Wallet: class Wallet:
def __init__(self, interface): def __init__(self):
self.electrum_version = ELECTRUM_VERSION self.electrum_version = ELECTRUM_VERSION
self.seed_version = SEED_VERSION self.seed_version = SEED_VERSION
@ -262,7 +262,6 @@ class Wallet:
self.tx_history = {} self.tx_history = {}
self.imported_keys = {} self.imported_keys = {}
self.interface = interface
self.remote_url = None self.remote_url = None
@ -541,23 +540,27 @@ class Wallet:
f.close() f.close()
def read(self): def read(self):
from interface import NativeInterface, HttpInterface,TCPInterface
upgrade_msg = """This wallet seed is deprecated. Please run upgrade.py for a diagnostic.""" upgrade_msg = """This wallet seed is deprecated. Please run upgrade.py for a diagnostic."""
self.file_exists = False
try: try:
f = open(self.path,"r") f = open(self.path,"r")
data = f.read() data = f.read()
f.close() f.close()
except: except:
return False self.interface = NativeInterface()
return
try: try:
d = ast.literal_eval( data ) d = ast.literal_eval( data )
self.seed_version = d.get('seed_version') self.seed_version = d.get('seed_version')
self.master_public_key = d.get('master_public_key').decode('hex') self.master_public_key = d.get('master_public_key').decode('hex')
self.use_encryption = d.get('use_encryption') self.use_encryption = d.get('use_encryption')
self.fee = int( d.get('fee') ) self.fee = int( d.get('fee') )
self.interface.host = d.get('host')
self.interface.set_port( d.get('port') )
self.interface.blocks = d.get('blocks')
self.seed = d.get('seed') self.seed = d.get('seed')
host = d.get('host')
port = d.get('port')
blocks = d.get('blocks')
self.addresses = d.get('addresses') self.addresses = d.get('addresses')
self.change_addresses = d.get('change_addresses') self.change_addresses = d.get('change_addresses')
self.status = d.get('status') self.status = d.get('status')
@ -569,7 +572,7 @@ class Wallet:
self.authorities = d.get('authorities',{}) self.authorities = d.get('authorities',{})
self.receipts = d.get('receipts',{}) self.receipts = d.get('receipts',{})
except: except:
raise BaseException(upgrade_msg) raise BaseException("cannot read wallet file")
self.update_tx_history() self.update_tx_history()
@ -578,7 +581,17 @@ class Wallet:
if self.remote_url: assert self.master_public_key.encode('hex') == self.get_remote_mpk() if self.remote_url: assert self.master_public_key.encode('hex') == self.get_remote_mpk()
return True self.file_exists = True
if port == 50000:
self.interface = NativeInterface(host,port)
elif port == 50001:
self.interface = TCPInterface(host,port)
elif port in [80,8080,81,8181]:
self.interface = HttpInterface(host,port)
else:
raise BaseException("unknown protocol: %d"%port)
def get_new_address(self): def get_new_address(self):
n = 0 n = 0

Loading…
Cancel
Save