Merge the network and network_proxy

This commit is contained in:
Neil Booth 2015-08-30 21:18:10 +09:00
parent 4d6a0f29ee
commit 2d05e7d891
14 changed files with 158 additions and 319 deletions

View File

@ -77,7 +77,7 @@ if is_bundle or is_local or is_android:
from electrum import util from electrum import util
from electrum import SimpleConfig, Network, Wallet, WalletStorage, NetworkProxy from electrum import SimpleConfig, Network, Wallet, WalletStorage
from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword
from electrum.plugins import init_plugins, run_hook, always_hook from electrum.plugins import init_plugins, run_hook, always_hook
from electrum.commands import get_parser, known_commands, Commands, config_variables from electrum.commands import get_parser, known_commands, Commands, config_variables
@ -97,12 +97,12 @@ def prompt_password(prompt, confirm=True):
def init_gui(config, network_proxy): def init_gui(config, network):
gui_name = config.get('gui', 'qt') gui_name = config.get('gui', 'qt')
if gui_name in ['lite', 'classic']: if gui_name in ['lite', 'classic']:
gui_name = 'qt' gui_name = 'qt'
gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui']) gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
gui = gui.ElectrumGui(config, network_proxy) gui = gui.ElectrumGui(config, network)
return gui return gui
@ -157,8 +157,7 @@ def init_cmdline(config):
wallet = Wallet.from_seed(seed, password, storage) wallet = Wallet.from_seed(seed, password, storage)
if not config.get('offline'): if not config.get('offline'):
s = get_daemon(config, False) network = Network(config)
network = NetworkProxy(s, config)
network.start() network.start()
wallet.start_threads(network) wallet.start_threads(network)
print_msg("Recovering wallet...") print_msg("Recovering wallet...")
@ -332,13 +331,12 @@ class ClientThread(util.DaemonThread):
class NetworkServer(util.DaemonThread): class NetworkServer(util.DaemonThread):
def __init__(self, config, network_proxy): def __init__(self, config, network):
util.DaemonThread.__init__(self) util.DaemonThread.__init__(self)
self.debug = False self.debug = False
self.config = config self.config = config
self.pipe = util.QueuePipe() self.pipe = util.QueuePipe()
self.network_proxy = network_proxy self.network = network
self.network = self.network_proxy.network
self.lock = threading.RLock() self.lock = threading.RLock()
# each GUI is a client of the daemon # each GUI is a client of the daemon
self.clients = [] self.clients = []
@ -516,11 +514,11 @@ if __name__ == '__main__':
# daemon is not running # daemon is not running
if cmd_name == 'gui': if cmd_name == 'gui':
network_proxy = NetworkProxy(None, config) network = Network(config)
network_proxy.start() network.start()
server = NetworkServer(config, network_proxy) server = NetworkServer(config, network)
server.start() server.start()
server.gui = init_gui(config, network_proxy) server.gui = init_gui(config, network)
server.gui.main() server.gui.main()
elif cmd_name == 'daemon': elif cmd_name == 'daemon':
subcommand = config.get('subcommand') subcommand = config.get('subcommand')
@ -530,9 +528,9 @@ if __name__ == '__main__':
elif subcommand == 'start': elif subcommand == 'start':
p = os.fork() p = os.fork()
if p == 0: if p == 0:
network_proxy = NetworkProxy(None, config) network = Network(config)
network_proxy.start() network.start()
server = NetworkServer(config, network_proxy) server = NetworkServer(config, network)
if config.get('websocket_server'): if config.get('websocket_server'):
import websockets import websockets
websockets.WebSocketServer(config, server).start() websockets.WebSocketServer(config, server).start()

View File

@ -2357,7 +2357,7 @@ class ElectrumWindow(QMainWindow):
txid, ok = QInputDialog.getText(self, _('Lookup transaction'), _('Transaction ID') + ':') txid, ok = QInputDialog.getText(self, _('Lookup transaction'), _('Transaction ID') + ':')
if ok and txid: if ok and txid:
try: try:
r = self.network.synchronous_get([('blockchain.transaction.get',[str(txid)])])[0] r = self.network.synchronous_get(('blockchain.transaction.get',[str(txid)]))
except BaseException as e: except BaseException as e:
self.show_message(str(e)) self.show_message(str(e))
return return

View File

@ -11,4 +11,3 @@ import transaction
from transaction import Transaction from transaction import Transaction
from plugins import BasePlugin from plugins import BasePlugin
from commands import Commands, known_commands from commands import Commands, known_commands
from network_proxy import NetworkProxy

View File

@ -148,7 +148,7 @@ class Commands:
"""Return the transaction history of any address. Note: This is a """Return the transaction history of any address. Note: This is a
walletless server query, results are not checked by SPV. walletless server query, results are not checked by SPV.
""" """
return self.network.synchronous_get([('blockchain.address.get_history', [address])])[0] return self.network.synchronous_get(('blockchain.address.get_history', [address]))
@command('nw') @command('nw')
def listunspent(self): def listunspent(self):
@ -165,16 +165,15 @@ class Commands:
"""Returns the UTXO list of any address. Note: This """Returns the UTXO list of any address. Note: This
is a walletless server query, results are not checked by SPV. is a walletless server query, results are not checked by SPV.
""" """
return self.network.synchronous_get([('blockchain.address.listunspent', [address])])[0] return self.network.synchronous_get(('blockchain.address.listunspent', [address]))
@command('n') @command('n')
def getutxoaddress(self, txid, pos): def getutxoaddress(self, txid, pos):
"""Get the address of a UTXO. Note: This is a walletless server query, results are """Get the address of a UTXO. Note: This is a walletless server query, results are
not checked by SPV. not checked by SPV.
""" """
r = self.network.synchronous_get([('blockchain.utxo.get_address', [txid, pos])]) r = self.network.synchronous_get(('blockchain.utxo.get_address', [txid, pos]))
if r: return {'address': r}
return {'address':r[0]}
@command('wp') @command('wp')
def createrawtx(self, inputs, outputs, unsigned=False): def createrawtx(self, inputs, outputs, unsigned=False):
@ -219,7 +218,7 @@ class Commands:
def broadcast(self, tx): def broadcast(self, tx):
"""Broadcast a transaction to the network. """ """Broadcast a transaction to the network. """
t = Transaction(tx) t = Transaction(tx)
return self.network.synchronous_get([('blockchain.transaction.broadcast', [str(t)])])[0] return self.network.synchronous_get(('blockchain.transaction.broadcast', [str(t)]))
@command('') @command('')
def createmultisig(self, num, pubkeys): def createmultisig(self, num, pubkeys):
@ -287,7 +286,7 @@ class Commands:
"""Return the balance of any address. Note: This is a walletless """Return the balance of any address. Note: This is a walletless
server query, results are not checked by SPV. server query, results are not checked by SPV.
""" """
out = self.network.synchronous_get([('blockchain.address.get_balance', [address])])[0] out = self.network.synchronous_get(('blockchain.address.get_balance', [address]))
out["confirmed"] = str(Decimal(out["confirmed"])/COIN) out["confirmed"] = str(Decimal(out["confirmed"])/COIN)
out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN) out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN)
return out return out
@ -295,7 +294,7 @@ class Commands:
@command('n') @command('n')
def getproof(self, address): def getproof(self, address):
"""Get Merkle branch of an address in the UTXO set""" """Get Merkle branch of an address in the UTXO set"""
p = self.network.synchronous_get([('blockchain.address.get_proof', [address])])[0] p = self.network.synchronous_get(('blockchain.address.get_proof', [address]))
out = [] out = []
for i,s in p: for i,s in p:
out.append(i) out.append(i)
@ -305,7 +304,7 @@ class Commands:
def getmerkle(self, txid, height): def getmerkle(self, txid, height):
"""Get Merkle branch of a transaction included in a block. Electrum """Get Merkle branch of a transaction included in a block. Electrum
uses this to verify transactions (Simple Payment Verification).""" uses this to verify transactions (Simple Payment Verification)."""
return self.network.synchronous_get([('blockchain.transaction.get_merkle', [txid, int(height)])])[0] return self.network.synchronous_get(('blockchain.transaction.get_merkle', [txid, int(height)]))
@command('n') @command('n')
def getservers(self): def getservers(self):
@ -522,7 +521,7 @@ class Commands:
"""Retrieve a transaction. """ """Retrieve a transaction. """
tx = self.wallet.transactions.get(txid) if self.wallet else None tx = self.wallet.transactions.get(txid) if self.wallet else None
if tx is None and self.network: if tx is None and self.network:
raw = self.network.synchronous_get([('blockchain.transaction.get', [txid])])[0] raw = self.network.synchronous_get(('blockchain.transaction.get', [txid]))
if raw: if raw:
tx = Transaction(raw) tx = Transaction(raw)
else: else:

View File

@ -5,7 +5,8 @@ import sys
import random import random
import select import select
import traceback import traceback
from collections import deque from collections import defaultdict, deque
from threading import Lock
import socks import socks
import socket import socket
@ -129,20 +130,19 @@ class Network(util.DaemonThread):
Our external API: Our external API:
- Member functions get_header(), get_parameters(), get_status_value(), - Member functions get_header(), get_interfaces(), get_local_height(),
new_blockchain_height(), set_parameters(), start(), get_parameters(), get_server_height(), get_status_value(),
is_connected(), new_blockchain_height(), set_parameters(), start(),
stop() stop()
""" """
def __init__(self, pipe, config=None): def __init__(self, config=None):
if config is None: if config is None:
config = {} # Do not use mutables as default values! config = {} # Do not use mutables as default values!
util.DaemonThread.__init__(self) util.DaemonThread.__init__(self)
self.config = SimpleConfig(config) if type(config) == type({}) else config self.config = SimpleConfig(config) if type(config) == type({}) else config
self.num_server = 8 if not self.config.get('oneserver') else 0 self.num_server = 8 if not self.config.get('oneserver') else 0
self.blockchain = Blockchain(self.config, self) self.blockchain = Blockchain(self.config, self)
self.requests_queue = pipe.send_queue
self.response_queue = pipe.get_queue
# A deque of interface header requests, processed left-to-right # A deque of interface header requests, processed left-to-right
self.bc_requests = deque() self.bc_requests = deque()
# Server for addresses and transactions # Server for addresses and transactions
@ -155,6 +155,10 @@ class Network(util.DaemonThread):
if not self.default_server: if not self.default_server:
self.default_server = pick_random_server() self.default_server = pick_random_server()
self.lock = Lock()
self.pending_sends = []
self.message_id = 0
self.debug = False
self.irc_servers = {} # returned by interface (list from irc) self.irc_servers = {} # returned by interface (list from irc)
self.recent_servers = self.read_recent_servers() self.recent_servers = self.read_recent_servers()
@ -163,6 +167,8 @@ class Network(util.DaemonThread):
self.heights = {} self.heights = {}
self.merkle_roots = {} self.merkle_roots = {}
self.utxo_roots = {} self.utxo_roots = {}
self.subscriptions = defaultdict(list)
self.callbacks = defaultdict(list)
dir_path = os.path.join( self.config.path, 'certs') dir_path = os.path.join( self.config.path, 'certs')
if not os.path.exists(dir_path): if not os.path.exists(dir_path):
@ -188,6 +194,15 @@ class Network(util.DaemonThread):
self.start_network(deserialize_server(self.default_server)[2], self.start_network(deserialize_server(self.default_server)[2],
deserialize_proxy(self.config.get('proxy'))) deserialize_proxy(self.config.get('proxy')))
def register_callback(self, event, callback):
with self.lock:
self.callbacks[event].append(callback)
def trigger_callback(self, event, params=()):
with self.lock:
callbacks = self.callbacks[event][:]
[callback(*params) for callback in callbacks]
def read_recent_servers(self): def read_recent_servers(self):
if not self.config.path: if not self.config.path:
return [] return []
@ -231,6 +246,12 @@ class Network(util.DaemonThread):
def is_connected(self): def is_connected(self):
return self.interface is not None return self.interface is not None
def is_connecting(self):
return self.connection_status == 'connecting'
def is_up_to_date(self):
return self.unanswered_requests == {}
def queue_request(self, method, params): def queue_request(self, method, params):
self.interface.queue_request({'method': method, 'params': params}) self.interface.queue_request({'method': method, 'params': params})
@ -263,7 +284,10 @@ class Network(util.DaemonThread):
def notify(self, key): def notify(self, key):
value = self.get_status_value(key) value = self.get_status_value(key)
self.response_queue.put({'method':'network.status', 'params':[key, value]}) if key in ['status', 'updated']:
self.trigger_callback(key)
else:
self.trigger_callback(key, (value,))
def get_parameters(self): def get_parameters(self):
host, port, protocol = deserialize_server(self.default_server) host, port, protocol = deserialize_server(self.default_server)
@ -337,8 +361,16 @@ class Network(util.DaemonThread):
self.socket_queue = Queue.Queue() self.socket_queue = Queue.Queue()
def set_parameters(self, host, port, protocol, proxy, auto_connect): def set_parameters(self, host, port, protocol, proxy, auto_connect):
self.auto_connect = auto_connect proxy_str = serialize_proxy(proxy)
server = serialize_server(host, port, protocol) server = serialize_server(host, port, protocol)
self.config.set_key('auto_connect', auto_connect, False)
self.config.set_key("proxy", proxy_str, False)
self.config.set_key("server", server, True)
# abort if changes were not allowed by config
if self.config.get('server') != server_str or self.config.get('proxy') != proxy_str:
return
self.auto_connect = auto_connect
if self.proxy != proxy or self.protocol != protocol: if self.proxy != proxy or self.protocol != protocol:
# Restart the network defaulting to the given server # Restart the network defaulting to the given server
self.stop_network() self.stop_network()
@ -405,7 +437,9 @@ class Network(util.DaemonThread):
self.switch_lagging_interface(i.server) self.switch_lagging_interface(i.server)
self.notify('updated') self.notify('updated')
def process_response(self, interface, response): def process_response(self, interface, response, callback):
if self.debug:
self.print_error("<--", response)
error = response.get('error') error = response.get('error')
result = response.get('result') result = response.get('result')
method = response.get('method') method = response.get('method')
@ -437,8 +471,19 @@ class Network(util.DaemonThread):
# Cache address subscription results # Cache address subscription results
if method == 'blockchain.address.subscribe' and error is None: if method == 'blockchain.address.subscribe' and error is None:
addr = response['params'][0] addr = response['params'][0]
self.addr_responses[addr] = result self.addr_responses[addr] = response
self.response_queue.put(response) if callback is None:
params = response['params']
with self.lock:
for k,v in self.subscriptions.items():
if (method, params) in v:
callback = k
break
if callback is None:
self.print_error("received unexpected notification",
method, params)
else:
callback(response)
def process_responses(self, interface): def process_responses(self, interface):
notifications, responses = interface.get_responses() notifications, responses = interface.get_responses()
@ -449,12 +494,14 @@ class Network(util.DaemonThread):
if client_id is not None: if client_id is not None:
if interface != self.interface: if interface != self.interface:
continue continue
self.unanswered_requests.pop(client_id) _req, callback = self.unanswered_requests.pop(client_id)
else:
callback = None
# Copy the request method and params to the response # Copy the request method and params to the response
response['method'] = request.get('method') response['method'] = request.get('method')
response['params'] = request.get('params') response['params'] = request.get('params')
response['id'] = client_id response['id'] = client_id
self.process_response(interface, response) self.process_response(interface, response, callback)
for response in notifications: for response in notifications:
if not response: # Closed remotely if not response: # Closed remotely
@ -466,16 +513,42 @@ class Network(util.DaemonThread):
response['result'] = response['params'][0] response['result'] = response['params'][0]
response['params'] = [] response['params'] = []
elif method == 'blockchain.address.subscribe': elif method == 'blockchain.address.subscribe':
params = response['params']
response['params'] = [params[0]] # addr response['params'] = [params[0]] # addr
response['result'] = params[1] response['result'] = params[1]
self.process_response(interface, response) self.process_response(interface, response, None)
def handle_incoming_requests(self): def send(self, messages, callback):
while not self.requests_queue.empty(): '''Messages is a list of (method, value) tuples'''
self.process_request(self.requests_queue.get()) with self.lock:
self.pending_sends.append((messages, callback))
def process_request(self, request): def process_pending_sends(self):
sends = self.pending_sends
self.pending_sends = []
for messages, callback in sends:
subs = filter(lambda (m,v): m.endswith('.subscribe'), messages)
with self.lock:
for sub in subs:
if sub not in self.subscriptions[callback]:
self.subscriptions[callback].append(sub)
_id = self.message_id
self.message_id += len(messages)
unsent = []
for message in messages:
method, params = message
request = {'id': _id, 'method': method, 'params': params}
if not self.process_request(request, callback):
unsent.append(message)
_id += 1
if unsent:
with self.lock:
self.pending_sends.append((unsent, callback))
# FIXME: inline this function
def process_request(self, request, callback):
'''Returns true if the request was processed.''' '''Returns true if the request was processed.'''
method = request['method'] method = request['method']
params = request['params'] params = request['params']
@ -492,14 +565,14 @@ class Network(util.DaemonThread):
out['error'] = str(e) out['error'] = str(e)
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
self.print_error("network error", str(e)) self.print_error("network error", str(e))
self.response_queue.put(out) callback(out)
return True return True
if method == 'blockchain.address.subscribe': if method == 'blockchain.address.subscribe':
addr = params[0] addr = params[0]
self.subscribed_addresses.add(addr) self.subscribed_addresses.add(addr)
if addr in self.addr_responses: if addr in self.addr_responses:
self.response_queue.put({'id':_id, 'result':self.addr_responses[addr]}) callback(self.addr_responses[addr])
return True return True
# This request needs connectivity. If we don't have an # This request needs connectivity. If we don't have an
@ -507,7 +580,9 @@ class Network(util.DaemonThread):
if not self.interface: if not self.interface:
return False return False
self.unanswered_requests[_id] = request if self.debug:
self.print_error("-->", request)
self.unanswered_requests[_id] = request, callback
self.interface.queue_request(request) self.interface.queue_request(request)
return True return True
@ -679,10 +754,12 @@ class Network(util.DaemonThread):
while self.is_running(): while self.is_running():
self.maintain_sockets() self.maintain_sockets()
self.wait_on_sockets() self.wait_on_sockets()
self.handle_incoming_requests()
self.handle_bc_requests() self.handle_bc_requests()
self.run_jobs() # Synchronizer and Verifier
self.process_pending_sends()
self.stop_network() self.stop_network()
self.trigger_callback('stop')
self.print_error("stopped") self.print_error("stopped")
def on_header(self, i, header): def on_header(self, i, header):
@ -706,3 +783,11 @@ class Network(util.DaemonThread):
def get_local_height(self): def get_local_height(self):
return self.blockchain.height() return self.blockchain.height()
def synchronous_get(self, request, timeout=100000000):
queue = Queue.Queue()
self.send([request], queue.put)
r = queue.get(True, timeout)
if r.get('error'):
raise BaseException(r.get('error'))
return r.get('result')

View File

@ -1,235 +0,0 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2014 Thomas Voegtlin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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 sys
import traceback
import threading
import Queue
import util
from network import Network, serialize_proxy, serialize_server
from simple_config import SimpleConfig
class NetworkProxy(util.DaemonThread):
def __init__(self, socket, config=None):
if config is None:
config = {} # Do not use mutables as default arguments!
util.DaemonThread.__init__(self)
self.config = SimpleConfig(config) if type(config) == type({}) else config
self.message_id = 0
self.unanswered_requests = {}
self.subscriptions = {}
self.debug = False
self.lock = threading.Lock()
self.callbacks = {}
if socket:
self.pipe = util.SocketPipe(socket)
self.network = None
else:
self.pipe = util.QueuePipe()
self.network = Network(self.pipe, config)
self.network.start()
for key in ['fee','status','banner','updated','servers','interfaces']:
value = self.network.get_status_value(key)
self.pipe.get_queue.put({'method':'network.status', 'params':[key, value]})
# status variables
self.status = 'unknown'
self.servers = {}
self.banner = ''
self.blockchain_height = 0
self.server_height = 0
self.interfaces = []
# value returned by estimatefee
self.fee = None
def run(self):
while self.is_running():
self.run_jobs() # Synchronizer and Verifier
try:
response = self.pipe.get()
except util.timeout:
continue
if response is None:
break
# Protect against ill-formed or malicious server responses
try:
self.process(response)
except:
traceback.print_exc(file=sys.stderr)
self.trigger_callback('stop')
if self.network:
self.network.stop()
self.print_error("stopped")
def process(self, response):
if self.debug:
self.print_error("<--", response)
if response.get('method') == 'network.status':
key, value = response.get('params')
if key == 'status':
self.status = value
elif key == 'banner':
self.banner = value
elif key == 'fee':
self.fee = value
elif key == 'updated':
self.blockchain_height, self.server_height = value
elif key == 'servers':
self.servers = value
elif key == 'interfaces':
self.interfaces = value
if key in ['status', 'updated']:
self.trigger_callback(key)
else:
self.trigger_callback(key, (value,))
return
msg_id = response.get('id')
result = response.get('result')
error = response.get('error')
if msg_id is not None:
with self.lock:
method, params, callback = self.unanswered_requests.pop(msg_id)
else:
method = response.get('method')
params = response.get('params')
with self.lock:
for k,v in self.subscriptions.items():
if (method, params) in v:
callback = k
break
else:
self.print_error("received unexpected notification",
method, params)
return
r = {'method':method, 'params':params, 'result':result,
'id':msg_id, 'error':error}
callback(r)
def send(self, messages, callback):
"""return the ids of the requests that we sent"""
# detect subscriptions
sub = []
for message in messages:
m, v = message
if m[-10:] == '.subscribe':
sub.append(message)
if sub:
with self.lock:
if self.subscriptions.get(callback) is None:
self.subscriptions[callback] = []
for message in sub:
if message not in self.subscriptions[callback]:
self.subscriptions[callback].append(message)
with self.lock:
requests = []
ids = []
for m in messages:
method, params = m
request = { 'id':self.message_id, 'method':method, 'params':params }
self.unanswered_requests[self.message_id] = method, params, callback
ids.append(self.message_id)
requests.append(request)
if self.debug:
self.print_error("-->", request)
self.message_id += 1
self.pipe.send_all(requests)
return ids
def synchronous_get(self, requests, timeout=100000000):
queue = Queue.Queue()
ids = self.send(requests, queue.put)
id2 = ids[:]
res = {}
while ids:
r = queue.get(True, timeout)
_id = r.get('id')
ids.remove(_id)
if r.get('error'):
raise BaseException(r.get('error'))
result = r.get('result')
res[_id] = r.get('result')
out = []
for _id in id2:
out.append(res[_id])
return out
def get_servers(self):
return self.servers
def get_interfaces(self):
return self.interfaces
def get_local_height(self):
return self.blockchain_height
def get_server_height(self):
return self.server_height
def is_connected(self):
return self.status == 'connected'
def is_connecting(self):
return self.status == 'connecting'
def is_up_to_date(self):
return self.unanswered_requests == {}
def get_parameters(self):
return self.synchronous_get([('network.get_parameters', [])])[0]
def set_parameters(self, host, port, protocol, proxy, auto_connect):
proxy_str = serialize_proxy(proxy)
server_str = serialize_server(host, port, protocol)
self.config.set_key('auto_connect', auto_connect, False)
self.config.set_key("proxy", proxy_str, False)
self.config.set_key("server", server_str, True)
# abort if changes were not allowed by config
if self.config.get('server') != server_str or self.config.get('proxy') != proxy_str:
return
return self.synchronous_get([('network.set_parameters', (host, port, protocol, proxy, auto_connect))])[0]
def stop_daemon(self):
return self.send([('daemon.stop',[])], None)
def register_callback(self, event, callback):
with self.lock:
if not self.callbacks.get(event):
self.callbacks[event] = []
self.callbacks[event].append(callback)
def trigger_callback(self, event, params=()):
with self.lock:
callbacks = self.callbacks.get(event,[])[:]
if callbacks:
[callback(*params) for callback in callbacks]

View File

@ -545,7 +545,7 @@ class Transaction:
for privkey in privkeys: for privkey in privkeys:
pubkey = public_key_from_private_key(privkey) pubkey = public_key_from_private_key(privkey)
address = address_from_private_key(privkey) address = address_from_private_key(privkey)
u = network.synchronous_get([ ('blockchain.address.listunspent',[address])])[0] u = network.synchronous_get(('blockchain.address.listunspent',[address]))
pay_script = klass.pay_script('address', address) pay_script = klass.pay_script('address', address)
for item in u: for item in u:
item['scriptPubKey'] = pay_script item['scriptPubKey'] = pay_script

View File

@ -40,7 +40,7 @@ class SPV(ThreadJob):
if tx_hash not in self.merkle_roots and tx_height <= lh: if tx_hash not in self.merkle_roots and tx_height <= lh:
request = ('blockchain.transaction.get_merkle', request = ('blockchain.transaction.get_merkle',
[tx_hash, tx_height]) [tx_hash, tx_height])
if self.network.send([request], self.merkle_response): self.network.send([request], self.merkle_response)
self.print_error('requested merkle', tx_hash) self.print_error('requested merkle', tx_hash)
self.merkle_roots[tx_hash] = None self.merkle_roots[tx_hash] = None

View File

@ -478,7 +478,7 @@ class KeepKeyWallet(BIP32_HD_Wallet):
ptx = self.transactions.get(tx_hash) ptx = self.transactions.get(tx_hash)
if ptx is None: if ptx is None:
ptx = self.network.synchronous_get([('blockchain.transaction.get', [tx_hash])])[0] ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
ptx = Transaction(ptx) ptx = Transaction(ptx)
prev_tx[tx_hash] = ptx prev_tx[tx_hash] = ptx

View File

@ -477,7 +477,7 @@ class TrezorWallet(BIP32_HD_Wallet):
ptx = self.transactions.get(tx_hash) ptx = self.transactions.get(tx_hash)
if ptx is None: if ptx is None:
ptx = self.network.synchronous_get([('blockchain.transaction.get', [tx_hash])])[0] ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
ptx = Transaction(ptx) ptx = Transaction(ptx)
prev_tx[tx_hash] = ptx prev_tx[tx_hash] = ptx

View File

@ -7,8 +7,7 @@ import electrum
# start network # start network
c = electrum.SimpleConfig() c = electrum.SimpleConfig()
s = electrum.daemon.get_daemon(c,True) network = electrum.Network(c)
network = electrum.NetworkProxy(s,c)
network.start() network.start()
# wait until connected # wait until connected
@ -26,4 +25,3 @@ network.send([('blockchain.headers.subscribe',[])], callback)
# 3. wait for results # 3. wait for results
while network.is_connected(): while network.is_connected():
time.sleep(1) time.sleep(1)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
from electrum import NetworkProxy, print_json from electrum import Network, print_json
try: try:
addr = sys.argv[1] addr = sys.argv[1]
@ -9,8 +9,7 @@ except Exception:
print "usage: get_history <bitcoin_address>" print "usage: get_history <bitcoin_address>"
sys.exit(1) sys.exit(1)
n = NetworkProxy() n = Network()
n.start(start_daemon=True) n.start()
h = n.synchronous_get([ ('blockchain.address.get_history',[addr]) ])[0] h = n.synchronous_get(('blockchain.address.get_history',[addr]))
print_json(h) print_json(h)

View File

@ -259,8 +259,7 @@ if __name__ == '__main__':
# start network # start network
c = electrum.SimpleConfig({'wallet_path':wallet_path}) c = electrum.SimpleConfig({'wallet_path':wallet_path})
daemon_socket = electrum.daemon.get_daemon(c, True) network = electrum.Network(config)
network = electrum.NetworkProxy(daemon_socket, config)
network.start() network.start()
# wait until connected # wait until connected
@ -299,4 +298,3 @@ if __name__ == '__main__':
server.handle_request() server.handle_request()
except socket.timeout: except socket.timeout:
continue continue

View File

@ -12,8 +12,7 @@ except Exception:
# start network # start network
c = electrum.SimpleConfig() c = electrum.SimpleConfig()
s = electrum.daemon.get_daemon(c,True) network = electrum.Network(c)
network = electrum.NetworkProxy(s,c)
network.start() network.start()
# wait until connected # wait until connected
@ -31,4 +30,3 @@ network.send([('blockchain.address.subscribe',[addr])], callback)
# 3. wait for results # 3. wait for results
while network.is_connected(): while network.is_connected():
time.sleep(1) time.sleep(1)