electrum-bitcoinprivate/lib/network_proxy.py

188 lines
5.7 KiB
Python
Raw Normal View History

2014-07-24 01:59:13 -07:00
#!/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 socket
import time
import sys
import os
import threading
import traceback
import json
import Queue
from network import Network
from util import print_error, print_stderr
from simple_config import SimpleConfig
from daemon import parse_json, NetworkServer, DAEMON_PORT
class NetworkProxy(threading.Thread):
def __init__(self, socket, config=None):
if config is None:
config = {} # Do not use mutables as default arguments!
threading.Thread.__init__(self)
self.config = SimpleConfig(config) if type(config) == type({}) else config
self.socket = socket
self.socket.settimeout(0.1)
self.message_id = 0
self.unanswered_requests = {}
self.subscriptions = {}
self.debug = False
self.lock = threading.Lock()
self.pending_transactions_for_notifications = []
self.banner = ''
self.callbacks = {}
self.running = True
self.daemon = True
def is_running(self):
return self.running
def run(self):
# read responses and trigger callbacks
message = ''
while self.is_running():
try:
data = self.socket.recv(1024)
except socket.timeout:
continue
except:
data = ''
if not data:
break
message += data
while True:
response, message = parse_json(message)
if response is not None:
self.process(response)
else:
break
# fixme: server does not detect if we don't call shutdown
self.socket.shutdown(2)
self.socket.close()
print_error("NetworkProxy thread terminating")
def process(self, response):
if self.debug:
print_error("<--", response)
if response.get('method') == 'network.subscribe':
status = response.get('status')
self.trigger_callback(status)
return
msg_id = response.get('id')
with self.lock:
method, params, callback = self.unanswered_requests.pop(msg_id)
result = response.get('result')
callback(None, {'method':method, 'params':params, 'result':result, 'id':msg_id})
def subscribe(self, messages, callback):
# detect if it is a subscription
with self.lock:
if self.subscriptions.get(callback) is None:
self.subscriptions[callback] = []
for message in messages:
if message not in self.subscriptions[callback]:
self.subscriptions[callback].append(message)
self.send( messages, callback )
def send(self, messages, callback):
"""return the ids of the requests that we sent"""
with self.lock:
out = ''
ids = []
for m in messages:
method, params = m
request = json.dumps( { 'id':self.message_id, 'method':method, 'params':params } )
self.unanswered_requests[self.message_id] = method, params, callback
ids.append(self.message_id)
if self.debug:
print_error("-->", request)
self.message_id += 1
out += request + '\n'
while out:
sent = self.socket.send( out )
out = out[sent:]
return ids
def synchronous_get(self, requests, timeout=100000000):
queue = Queue.Queue()
ids = self.send(requests, lambda i,x: queue.put(x))
id2 = ids[:]
res = {}
while ids:
r = queue.get(True, timeout)
_id = r.get('id')
if _id in ids:
ids.remove(_id)
res[_id] = r.get('result')
else:
raise
out = []
for _id in id2:
out.append(res[_id])
return out
def get_servers(self):
return self.synchronous_get([('network.get_servers',[])])[0]
def get_header(self, height):
return self.synchronous_get([('network.get_header',[height])])[0]
def get_local_height(self):
return self.synchronous_get([('network.get_local_height',[])])[0]
def is_connected(self):
return self.synchronous_get([('network.is_connected',[])])[0]
def is_up_to_date(self):
return self.synchronous_get([('network.is_up_to_date',[])])[0]
def main_server(self):
return self.synchronous_get([('network.main_server',[])])[0]
def stop(self):
self.running = False
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):
with self.lock:
callbacks = self.callbacks.get(event,[])[:]
if callbacks:
[callback() for callback in callbacks]
print_error("trigger_callback", event, len(callbacks))