A few peer handling improvements
based on suggestions from hsmiths Set transport to None if a connection is lost Only subscribe to peers if the peer is verified on same network
This commit is contained in:
parent
9d6b353eec
commit
0149f2785f
@ -776,6 +776,7 @@ class JSONSession(JSONSessionBase, asyncio.Protocol):
|
||||
|
||||
def connection_lost(self, exc):
|
||||
'''Trigger timeouts of all pending requests.'''
|
||||
self.transport = None
|
||||
self.timeout_session()
|
||||
|
||||
def is_closing(self):
|
||||
|
||||
@ -77,7 +77,6 @@ class PeerSession(JSONSession):
|
||||
proto_ver = (version.PROTOCOL_MIN, version.PROTOCOL_MAX)
|
||||
self.send_request(self.on_version, 'server.version',
|
||||
[version.VERSION, proto_ver])
|
||||
self.send_request(self.on_peers_subscribe, 'server.peers.subscribe')
|
||||
self.send_request(self.on_features, 'server.features')
|
||||
|
||||
def connection_lost(self, exc):
|
||||
@ -99,8 +98,6 @@ class PeerSession(JSONSession):
|
||||
|
||||
Each update is expected to be of the form:
|
||||
[ip_addr, hostname, ['v1.0', 't51001', 's51002']]
|
||||
|
||||
Return True if we're in the list of peers.
|
||||
'''
|
||||
try:
|
||||
real_names = [' '.join([u[1]] + u[2]) for u in updates]
|
||||
@ -108,7 +105,7 @@ class PeerSession(JSONSession):
|
||||
for real_name in real_names]
|
||||
except Exception:
|
||||
self.log_error('bad server.peers.subscribe response')
|
||||
return False
|
||||
return
|
||||
|
||||
self.peer_mgr.add_peers(peers)
|
||||
my = self.peer_mgr.myself
|
||||
@ -124,41 +121,51 @@ class PeerSession(JSONSession):
|
||||
'''Handle the response to the add_peer message.'''
|
||||
self.close_if_done()
|
||||
|
||||
def peer_verified(self, is_good):
|
||||
'''Call when it has been determined whether or not the peer seems to
|
||||
be on the same network.
|
||||
'''
|
||||
if is_good:
|
||||
self.send_request(self.on_peers_subscribe,
|
||||
'server.peers.subscribe')
|
||||
else:
|
||||
self.peer.mark_bad()
|
||||
self.failed = True
|
||||
|
||||
def on_features(self, features, error):
|
||||
# Several peers don't implement this. If they do, check they are
|
||||
# the same network with the genesis hash.
|
||||
verified = False
|
||||
if not error and isinstance(features, dict):
|
||||
forget = False
|
||||
our_hash = self.peer_mgr.env.coin.GENESIS_HASH
|
||||
their_hash = features.get('genesis_hash')
|
||||
if their_hash:
|
||||
verified = their_hash == our_hash
|
||||
forget = their_hash != our_hash
|
||||
if forget:
|
||||
self.failed = True
|
||||
self.peer.mark_bad()
|
||||
if our_hash != features.get('genesis_hash'):
|
||||
self.peer_verified(False)
|
||||
self.log_warning('incorrect genesis hash')
|
||||
else:
|
||||
self.peer_verified(True)
|
||||
self.peer.update_features(features)
|
||||
verified = True
|
||||
# For legacy peers not implementing features, check their height
|
||||
# as a proxy to determining they're on our network
|
||||
if not verified:
|
||||
if not verified and not self.peer.bad:
|
||||
self.send_request(self.on_headers, 'blockchain.headers.subscribe')
|
||||
self.close_if_done()
|
||||
|
||||
def on_headers(self, result, error):
|
||||
'''Handle the response to the version message.'''
|
||||
if error or not isinstance(result, dict):
|
||||
if error:
|
||||
self.failed = True
|
||||
self.log_error('blockchain.headers.subscribe returned an error')
|
||||
elif not isinstance(result, dict):
|
||||
self.log_error('bad blockchain.headers.subscribe response')
|
||||
self.peer_verified(False)
|
||||
else:
|
||||
our_height = self.peer_mgr.controller.bp.db_height
|
||||
their_height = result.get('block_height')
|
||||
if (not isinstance(their_height, int) or
|
||||
abs(our_height - their_height) > 5):
|
||||
self.failed = True
|
||||
self.peer.mark_bad()
|
||||
is_good = (isinstance(their_height, int) and
|
||||
abs(our_height - their_height) <= 5)
|
||||
self.peer_verified(is_good)
|
||||
if not is_good:
|
||||
self.log_warning('bad height {}'.format(their_height))
|
||||
self.close_if_done()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user