Unify message IDs between network and interfaces
Previously network.py had its own idea of request IDs, and each interface had its own which was sent on the wire. The interface would jump through hoops to translate one to the other. This unifies them so that a message ID is passed when queueing a request, in addition to the method and params. network.py is now solely responsible for message ID management. Apart from being simpler and clearer, this also should be faster as there is much less data structure manipulation and rebuilding happening.
This commit is contained in:
105
lib/network.py
105
lib/network.py
@@ -254,15 +254,26 @@ class Network(util.DaemonThread):
|
||||
def is_up_to_date(self):
|
||||
return self.unanswered_requests == {}
|
||||
|
||||
def queue_request(self, method, params):
|
||||
self.interface.queue_request({'method': method, 'params': params})
|
||||
def queue_request(self, method, params, interface=None):
|
||||
# If you want to queue a request on any interface it must go
|
||||
# through this function so message ids are properly tracked
|
||||
if interface is None:
|
||||
interface = self.interface
|
||||
message_id = self.message_id
|
||||
self.message_id += 1
|
||||
interface.queue_request(method, params, message_id)
|
||||
return message_id
|
||||
|
||||
def send_subscriptions(self):
|
||||
# clear cache
|
||||
self.cached_responses = {}
|
||||
self.print_error('sending subscriptions to', self.interface.server, len(self.unanswered_requests), len(self.subscribed_addresses))
|
||||
for r in self.unanswered_requests.values():
|
||||
self.interface.queue_request(r[0])
|
||||
# Resend unanswered requests
|
||||
requests = self.unanswered_requests.values()
|
||||
self.unanswered_requests = {}
|
||||
for request in requests:
|
||||
message_id = self.queue_request(request[0], request[1])
|
||||
self.unanswered_requests[message_id] = request
|
||||
for addr in self.subscribed_addresses:
|
||||
self.queue_request('blockchain.address.subscribe', [addr])
|
||||
self.queue_request('server.banner', [])
|
||||
@@ -488,37 +499,38 @@ class Network(util.DaemonThread):
|
||||
callback(response)
|
||||
|
||||
def process_responses(self, interface):
|
||||
notifications, responses = interface.get_responses()
|
||||
responses = interface.get_responses()
|
||||
|
||||
for request, response in responses:
|
||||
# Client ID was given by the daemon or proxy
|
||||
client_id = request.get('id')
|
||||
if client_id is not None:
|
||||
if interface != self.interface:
|
||||
continue
|
||||
_req, callback = self.unanswered_requests.pop(client_id)
|
||||
callback = None
|
||||
if request:
|
||||
method, params, message_id = request
|
||||
# client requests go through self.send() with a
|
||||
# callback, are only sent to the current interface,
|
||||
# and are placed in the unanswered_requests dictionary
|
||||
client_req = self.unanswered_requests.pop(message_id, None)
|
||||
if client_req:
|
||||
assert interface == self.interface
|
||||
callback = client_req[2]
|
||||
# Copy the request method and params to the response
|
||||
response['method'] = method
|
||||
response['params'] = params
|
||||
else:
|
||||
callback = None
|
||||
# Copy the request method and params to the response
|
||||
response['method'] = request.get('method')
|
||||
response['params'] = request.get('params')
|
||||
response['id'] = client_id
|
||||
self.process_response(interface, response, callback)
|
||||
if not response: # Closed remotely / misbehaving
|
||||
self.connection_down(interface.server)
|
||||
break
|
||||
# Rewrite response shape to match subscription request response
|
||||
method = response.get('method')
|
||||
params = response.get('params')
|
||||
if method == 'blockchain.headers.subscribe':
|
||||
response['result'] = params[0]
|
||||
response['params'] = []
|
||||
elif method == 'blockchain.address.subscribe':
|
||||
response['params'] = [params[0]] # addr
|
||||
response['result'] = params[1]
|
||||
|
||||
for response in notifications:
|
||||
if not response: # Closed remotely
|
||||
self.connection_down(interface.server)
|
||||
break
|
||||
# Rewrite response shape to match subscription request response
|
||||
method = response.get('method')
|
||||
params = response.get('params')
|
||||
if method == 'blockchain.headers.subscribe':
|
||||
response['result'] = params[0]
|
||||
response['params'] = []
|
||||
elif method == 'blockchain.address.subscribe':
|
||||
response['params'] = [params[0]] # addr
|
||||
response['result'] = params[1]
|
||||
self.process_response(interface, response, None)
|
||||
# Response is now in canonical form
|
||||
self.process_response(interface, response, callback)
|
||||
|
||||
def send(self, messages, callback):
|
||||
'''Messages is a list of (method, value) tuples'''
|
||||
@@ -535,16 +547,11 @@ class Network(util.DaemonThread):
|
||||
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):
|
||||
if not self.process_request(message, callback):
|
||||
unsent.append(message)
|
||||
_id += 1
|
||||
|
||||
if unsent:
|
||||
with self.lock:
|
||||
@@ -553,12 +560,10 @@ class Network(util.DaemonThread):
|
||||
# FIXME: inline this function
|
||||
def process_request(self, request, callback):
|
||||
'''Returns true if the request was processed.'''
|
||||
method = request['method']
|
||||
params = request['params']
|
||||
_id = request['id']
|
||||
method, params = request
|
||||
|
||||
if method.startswith('network.'):
|
||||
out = {'id':_id}
|
||||
out = {}
|
||||
try:
|
||||
f = getattr(self, method[8:])
|
||||
out['result'] = f(*params)
|
||||
@@ -585,8 +590,8 @@ class Network(util.DaemonThread):
|
||||
|
||||
if self.debug:
|
||||
self.print_error("-->", request)
|
||||
self.unanswered_requests[_id] = request, callback
|
||||
self.interface.queue_request(request)
|
||||
message_id = self.queue_request(method, params)
|
||||
self.unanswered_requests[message_id] = method, params, callback
|
||||
return True
|
||||
|
||||
def connection_down(self, server):
|
||||
@@ -603,8 +608,7 @@ class Network(util.DaemonThread):
|
||||
def new_interface(self, server, socket):
|
||||
self.add_recent_server(server)
|
||||
self.interfaces[server] = interface = Interface(server, socket)
|
||||
interface.queue_request({'method': 'blockchain.headers.subscribe',
|
||||
'params': []})
|
||||
self.queue_request('blockchain.headers.subscribe', [], interface)
|
||||
if server == self.default_server:
|
||||
self.switch_to_interface(server)
|
||||
self.notify('interfaces')
|
||||
@@ -625,9 +629,8 @@ class Network(util.DaemonThread):
|
||||
if interface.has_timed_out():
|
||||
self.connection_down(interface.server)
|
||||
elif interface.ping_required():
|
||||
version_req = {'method': 'server.version',
|
||||
'params': [ELECTRUM_VERSION, PROTOCOL_VERSION]}
|
||||
interface.queue_request(version_req)
|
||||
params = [ELECTRUM_VERSION, PROTOCOL_VERSION]
|
||||
self.queue_request('server.version', params, interface)
|
||||
|
||||
now = time.time()
|
||||
# nodes
|
||||
@@ -653,8 +656,7 @@ class Network(util.DaemonThread):
|
||||
|
||||
def request_chunk(self, interface, data, idx):
|
||||
interface.print_error("requesting chunk %d" % idx)
|
||||
interface.queue_request({'method':'blockchain.block.get_chunk',
|
||||
'params':[idx]})
|
||||
self.queue_request('blockchain.block.get_chunk', [idx], interface)
|
||||
data['chunk_idx'] = idx
|
||||
data['req_time'] = time.time()
|
||||
|
||||
@@ -675,8 +677,7 @@ class Network(util.DaemonThread):
|
||||
|
||||
def request_header(self, interface, data, height):
|
||||
interface.print_error("requesting header %d" % height)
|
||||
interface.queue_request({'method':'blockchain.block.get_header',
|
||||
'params':[height]})
|
||||
self.queue_request('blockchain.block.get_header', [height], interface)
|
||||
data['header_height'] = height
|
||||
data['req_time'] = time.time()
|
||||
if not 'chain' in data:
|
||||
|
||||
Reference in New Issue
Block a user