From 9bf16c5758ec80b77344042a40fbc12be6060d1a Mon Sep 17 00:00:00 2001 From: Kevin F Date: Sun, 13 Mar 2022 00:35:22 -0600 Subject: [PATCH] Work on stemout --- src/gossip/client/announce.py | 2 +- src/gossip/client/dandelionstem.py | 62 ++++++++++++++++++++++++++---- src/gossip/constants.py | 2 +- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/gossip/client/announce.py b/src/gossip/client/announce.py index 8aaab390..e336f6d1 100644 --- a/src/gossip/client/announce.py +++ b/src/gossip/client/announce.py @@ -18,7 +18,7 @@ def do_announce(peer_set): except AttributeError: pass sock = announce_peer.get_socket() - sock.send( + sock.sendall( command_to_byte(GossipCommands.ANNOUNCE) + our_transport_address) if int.from_bytes(sock.recv(1), 'big') != 1: logger.warn( diff --git a/src/gossip/client/dandelionstem.py b/src/gossip/client/dandelionstem.py index e03c9bab..7012f14e 100644 --- a/src/gossip/client/dandelionstem.py +++ b/src/gossip/client/dandelionstem.py @@ -1,5 +1,5 @@ from queue import Queue -from threading import Timer +from threading import Thread, Timer from time import sleep from secrets import choice import traceback @@ -11,20 +11,24 @@ from blockdb import add_block_to_db import logger from ..constants import BLACKHOLE_EVADE_TIMER_SECS, MAX_OUTBOUND_DANDELION_EDGE +from ..commands import GossipCommands, command_to_byte +from .. import dandelion if TYPE_CHECKING: from ordered_set import OrderedSet from onionrblocks import Block from ..peer import Peer from ..dandelion.phase import DandelionPhase + import socket class NotEnoughEdges(ValueError): pass # noqa +class StemConnectionDenied(ConnectionRefusedError): pass # noqa -def _setup_edge( +async def _setup_edge( peer_set: OrderedSet['Peer'], exclude_set: OrderedSet['Peer']): - """Negotiate stem connection with random peer, add to exclude set if fail""" + """Negotiate stem connection with random peer, add to exclu set if fail""" try: peer: 'Peer' = choice(peer_set - exclude_set) except IndexError: @@ -35,13 +39,46 @@ def _setup_edge( logger.debug(traceback.format_exc()) exclude_set.add(peer) + try: + s.sendall(command_to_byte(GossipCommands.PUT_BLOCKS)) + if s.recv(1) == dandelion.StemAcceptResult.DENY: + raise StemConnectionDenied + except StemConnectionDenied: + logger.debug( + "Stem connection denied (peer has too many) " + + f"{peer.transport_address}") + except Exception: + logger.warn( + "Error asking peer to establish stem connection" + + traceback.format_exc(), terminal=True) + else: + # Return peer socket if it is in stem reception mode successfully + return s + finally: + # If peer is good or bad, exclude it no matter what + exclude_set.add(peer) + # If they won't accept stem blocks, close the socket + s.close() -def stem_out( +async def _do_stem_stream( + peer_socket: 'socket.socket', + block_queue: Queue['Block'], + d_phase: 'DandelionPhase'): + return + + +async def stem_out( block_queues: Tuple[Queue['Block'], Queue['Block']], peer_set: OrderedSet['Peer'], d_phase: 'DandelionPhase'): + + # don't bother if there are no possible outbound edges + if not len(peer_set): + sleep(1) + return + # Spawn threads with deep copied block queue to add to db after time # for black hole attack for block_q in block_queues: @@ -49,14 +86,23 @@ def stem_out( lambda q: set(map(add_block_to_db, q)), BLACKHOLE_EVADE_TIMER_SECS, list(block_q.queue)) - # don't bother if there are no possible outbound edges - if not len(peer_set): - sleep(1) - return + peer_sockets: List['socket.socket'] = [] # Pick edges randomly # Using orderedset for the tried edges to ensure random pairing with queue tried_edges: OrderedSet['Peer'] = OrderedSet() + while len(peer_sockets) < MAX_OUTBOUND_DANDELION_EDGE: + try: + peer_sockets.append(_setup_edge(peer_set, tried_edges)) + except NotEnoughEdges: + logger.debug("Not able to build enough peers for stemout") + break + finally: + if not d_phase.is_stem_phase() or d_phase.remaining_time() < 5: + logger.error( + "Did not stem out any blocks in time, " + + "if this happens regularly you may be under attack", + terminal=True) diff --git a/src/gossip/constants.py b/src/gossip/constants.py index 3ce02139..dbe32cda 100644 --- a/src/gossip/constants.py +++ b/src/gossip/constants.py @@ -6,7 +6,7 @@ BLOCK_ID_SIZE = 128 DANDELION_EPOCH_LENGTH = 60 # Magic number i made up, not really specified in dandelion++ paper -MAX_INBOUND_DANDELION_EDGE = 50 # Mainly picked to avoid slowlorisstor browser dvm 16 +MAX_INBOUND_DANDELION_EDGE = 50 # Mainly picked to avoid slowloris # Dandelion subgraph is aprox 4-regular MAX_OUTBOUND_DANDELION_EDGE = 2