diff --git a/src/network/__init__.py b/src/network/__init__.py index 1b5aef92..49f77c43 100644 --- a/src/network/__init__.py +++ b/src/network/__init__.py @@ -16,6 +16,7 @@ __all__ = ["AnnounceThread", "BMConnectionPool", "StoppableThread"] def start(config, state): """Start network threads""" + import state from .addrthread import AddrThread from .dandelion import Dandelion from .downloadthread import DownloadThread @@ -27,7 +28,7 @@ def start(config, state): readKnownNodes() # init, needs to be early because other thread may access it early - Dandelion() + state.Dandelion = Dandelion() BMConnectionPool().connectToStream(1) for thread in ( BMNetworkThread(), InvThread(), AddrThread(), diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 76467087..685e3c59 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -7,7 +7,6 @@ import time import protocol import state from highlevelcrypto import calculateInventoryHash -from network.dandelion import Dandelion logger = logging.getLogger('default') @@ -112,7 +111,7 @@ class BMObject(object): # pylint: disable=too-many-instance-attributes or advertise it unnecessarily) """ # if it's a stem duplicate, pretend we don't have it - if Dandelion().hasHash(self.inventoryHash): + if state.Dandelion.hasHash(self.inventoryHash): return if self.inventoryHash in state.Inventory: raise BMObjectAlreadyHaveError() diff --git a/src/network/bmproto.py b/src/network/bmproto.py index 2a05b942..f4dd8845 100644 --- a/src/network/bmproto.py +++ b/src/network/bmproto.py @@ -25,7 +25,6 @@ from network.bmobject import ( BMObjectInsufficientPOWError, BMObjectInvalidError, BMObjectUnwantedStreamError ) -from network.dandelion import Dandelion from network.proxy import ProxyError from node import Node, Peer @@ -355,10 +354,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker): return True for i in map(str, items): - if i in state.Inventory and not Dandelion().hasHash(i): + if i in state.Inventory and not state.Dandelion.hasHash(i): continue - if dandelion and not Dandelion().hasHash(i): - Dandelion().addHash(i, self) + if dandelion and not state.Dandelion.hasHash(i): + state.Dandelion.addHash(i, self) self.handleReceivedInventory(i) return True @@ -420,9 +419,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker): except KeyError: pass - if self.object.inventoryHash in state.Inventory and Dandelion().hasHash( + if self.object.inventoryHash in state.Inventory and state.Dandelion.hasHash( self.object.inventoryHash): - Dandelion().removeHash( + state.Dandelion.removeHash( self.object.inventoryHash, "cycle detection") state.Inventory[self.object.inventoryHash] = ( diff --git a/src/network/dandelion.py b/src/network/dandelion.py index 4f3cd07b..8a7aff29 100644 --- a/src/network/dandelion.py +++ b/src/network/dandelion.py @@ -10,7 +10,6 @@ from time import time import connectionpool import state from queues import invQueue -from singleton import Singleton # randomise routes after 600 seconds REASSIGN_INTERVAL = 600 @@ -26,7 +25,6 @@ Stem = namedtuple('Stem', ['child', 'stream', 'timeout']) logger = logging.getLogger('default') -@Singleton class Dandelion: # pylint: disable=old-style-class """Dandelion class for tracking stem/fluff stages.""" def __init__(self): diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py index b668d813..777f190d 100644 --- a/src/network/downloadthread.py +++ b/src/network/downloadthread.py @@ -6,7 +6,6 @@ import state import addresses import helper_random import protocol -from dandelion import Dandelion from network.connectionpool import BMConnectionPool from objectracker import missingObjects from threads import StoppableThread @@ -60,7 +59,7 @@ class DownloadThread(StoppableThread): payload = bytearray() chunkCount = 0 for chunk in request: - if chunk in state.Inventory and not Dandelion().hasHash(chunk): + if chunk in state.Inventory and not state.Dandelion.hasHash(chunk): try: del i.objectsNewToMe[chunk] except KeyError: diff --git a/src/network/invthread.py b/src/network/invthread.py index 14217041..e1dc1b65 100644 --- a/src/network/invthread.py +++ b/src/network/invthread.py @@ -9,7 +9,6 @@ import addresses import protocol import state from network.connectionpool import BMConnectionPool -from network.dandelion import Dandelion from queues import invQueue from threads import StoppableThread @@ -40,10 +39,10 @@ class InvThread(StoppableThread): @staticmethod def handleLocallyGenerated(stream, hashId): """Locally generated inventory items require special handling""" - Dandelion().addHash(hashId, stream=stream) + state.Dandelion.addHash(hashId, stream=stream) for connection in BMConnectionPool().connections(): if state.dandelion and connection != \ - Dandelion().objectChildStem(hashId): + state.Dandelion.objectChildStem(hashId): continue connection.objectsNewToThem[hashId] = time() @@ -52,7 +51,7 @@ class InvThread(StoppableThread): chunk = [] while True: # Dandelion fluff trigger by expiration - handleExpiredDandelion(Dandelion().expire()) + handleExpiredDandelion(state.Dandelion.expire()) try: data = invQueue.get(False) chunk.append((data[0], data[1])) @@ -75,7 +74,7 @@ class InvThread(StoppableThread): except KeyError: continue try: - if connection == Dandelion().objectChildStem(inv[1]): + if connection == state.Dandelion.objectChildStem(inv[1]): # Fluff trigger by RNG # auto-ignore if config set to 0, i.e. dandelion is off if random.randint(1, 100) >= state.dandelion: # nosec B311 @@ -105,7 +104,7 @@ class InvThread(StoppableThread): for _ in range(len(chunk)): invQueue.task_done() - if Dandelion().refresh < time(): - Dandelion().reRandomiseStems() + if state.Dandelion.refresh < time(): + state.Dandelion.reRandomiseStems() self.stop.wait(1) diff --git a/src/network/objectracker.py b/src/network/objectracker.py index 65e06de4..8de098ce 100644 --- a/src/network/objectracker.py +++ b/src/network/objectracker.py @@ -4,8 +4,8 @@ Module for tracking objects import time from threading import RLock +import state import network.connectionpool -from network.dandelion import Dandelion from randomtrackingdict import RandomTrackingDict haveBloom = False @@ -107,14 +107,14 @@ class ObjectTracker(object): del i.objectsNewToMe[hashid] except KeyError: if streamNumber in i.streams and ( - not Dandelion().hasHash(hashid) - or Dandelion().objectChildStem(hashid) == i): + not state.Dandelion.hasHash(hashid) + or state.Dandelion.objectChildStem(hashid) == i): with i.objectsNewToThemLock: i.objectsNewToThem[hashid] = time.time() # update stream number, # which we didn't have when we just received the dinv # also resets expiration of the stem mode - Dandelion().setHashStream(hashid, streamNumber) + state.Dandelion.setHashStream(hashid, streamNumber) if i == self: try: diff --git a/src/network/tcp.py b/src/network/tcp.py index 0acbf89a..0964d8b8 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -25,7 +25,6 @@ import connectionpool import knownnodes from network.advanceddispatcher import AdvancedDispatcher from network.bmproto import BMProto -from network.dandelion import Dandelion from network.objectracker import ObjectTracker from network.socks4a import Socks4aConnection from network.socks5 import Socks5Connection @@ -169,7 +168,7 @@ class TCPConnection(BMProto, TLSDispatcher): knownnodes.increaseRating(self.destination) knownnodes.addKnownNode( self.streams, self.destination, time.time()) - Dandelion().maybeAddStem(self) + state.Dandelion.maybeAddStem(self) self.sendAddr() self.sendBigInv() @@ -231,7 +230,7 @@ class TCPConnection(BMProto, TLSDispatcher): with self.objectsNewToThemLock: for objHash in state.Inventory.unexpired_hashes_by_stream(stream): # don't advertise stem objects on bigInv - if Dandelion().hasHash(objHash): + if state.Dandelion.hasHash(objHash): continue bigInvList[objHash] = 0 objectCount = 0 @@ -293,7 +292,7 @@ class TCPConnection(BMProto, TLSDispatcher): if host_is_global: knownnodes.addKnownNode( self.streams, self.destination, time.time()) - Dandelion().maybeRemoveStem(self) + state.Dandelion.maybeRemoveStem(self) else: self.checkTimeOffsetNotification() if host_is_global: diff --git a/src/network/uploadthread.py b/src/network/uploadthread.py index a72b80ef..8b3e5938 100644 --- a/src/network/uploadthread.py +++ b/src/network/uploadthread.py @@ -7,7 +7,6 @@ import helper_random import protocol import state from network.connectionpool import BMConnectionPool -from network.dandelion import Dandelion from randomtrackingdict import RandomTrackingDict from threads import StoppableThread @@ -41,8 +40,8 @@ class UploadThread(StoppableThread): chunk_count = 0 for chunk in request: del i.pendingUpload[chunk] - if Dandelion().hasHash(chunk) and \ - i != Dandelion().objectChildStem(chunk): + if state.Dandelion.hasHash(chunk) and \ + i != state.Dandelion.objectChildStem(chunk): i.antiIntersectionDelay() self.logger.info( '%s asked for a stem object we didn\'t offer to it.', diff --git a/src/state.py b/src/state.py index a72e230d..21584560 100644 --- a/src/state.py +++ b/src/state.py @@ -97,3 +97,6 @@ class Placeholder(object): # pylint:disable=too-few-public-methods Inventory = Placeholder("Inventory") + + +Dandelion = Placeholder("Dandelion")