diff --git a/src/Hash2Pub/DHTProtocol.hs b/src/Hash2Pub/DHTProtocol.hs index 230f7df..e562b98 100644 --- a/src/Hash2Pub/DHTProtocol.hs +++ b/src/Hash2Pub/DHTProtocol.hs @@ -90,7 +90,9 @@ import Debug.Trace (trace) queryLocalCache :: LocalNodeState -> NodeCache -> Int -> NodeID -> QueryResponse queryLocalCache ownState nCache lBestNodes targetID -- as target ID falls between own ID and first predecessor, it is handled by this node - | targetID `isInOwnResponsibilitySlice` ownState = FOUND . toRemoteNodeState $ ownState + -- This only makes sense if the node is part of the DHT by having joined. + -- A default answer to nodes querying an unjoined node is provided by 'respondQueryID'. + | isJoined ownState && targetID `isInOwnResponsibilitySlice` ownState = FOUND . toRemoteNodeState $ ownState -- my interpretation: the "l best next hops" are the l-1 closest preceding nodes and -- the closest succeeding node (like with the p initiated parallel queries | otherwise = FORWARD $ closestSuccessor `Set.union` closestCachePredecessors (lBestNodes-1) targetID nCache @@ -296,7 +298,11 @@ respondQueryID nsSTM msgSet = do cache <- readTVar $ nodeCacheSTM nsSnap let responsePayload = QueryIDResponsePayload { - queryResult = queryLocalCache nsSnap cache (fromIntegral $ queryLBestNodes senderPayload') (queryTargetID senderPayload') + queryResult = if isJoined nsSnap + then queryLocalCache nsSnap cache (fromIntegral $ queryLBestNodes senderPayload') (queryTargetID senderPayload') + -- if not joined yet, attract responsibility for + -- all keys to make bootstrapping possible + else FOUND (toRemoteNodeState nsSnap) } queryResponseMsg = Response { requestID = requestID aRequestPart diff --git a/test/FediChordSpec.hs b/test/FediChordSpec.hs index 4f05e72..545c3dd 100644 --- a/test/FediChordSpec.hs +++ b/test/FediChordSpec.hs @@ -132,10 +132,11 @@ spec = do cacheWith2Entries :: NodeCache cacheWith2Entries = addCacheEntryPure 10 (RemoteCacheEntry node5 10) (addCacheEntryPure 10 (RemoteCacheEntry node2 10) emptyCache) cacheWith4Entries = addCacheEntryPure 10 (RemoteCacheEntry node3 10) (addCacheEntryPure 10 (RemoteCacheEntry node4 10) cacheWith2Entries) - it "nodes not joined provide the default answer FOUND" $ do + it "unjoined nodes should never return themselfs" $ do exampleLocalNodeAsRemote <- toRemoteNodeState <$> exampleLocalNode - queryLocalCache <$> exampleLocalNode <*> pure emptyCache <*> pure 3 <*> pure (toNodeID 2^(9::Integer)+5) `shouldReturn` FOUND exampleLocalNodeAsRemote - queryLocalCache <$> exampleLocalNode <*> pure cacheWith4Entries <*> pure 1 <*> pure (toNodeID 2342) `shouldReturn` FOUND exampleLocalNodeAsRemote + queryLocalCache <$> exampleLocalNode <*> pure emptyCache <*> pure 3 <*> pure (toNodeID 2^(9::Integer)+5) `shouldReturn` FORWARD Set.empty + (FORWARD fwSet) <- queryLocalCache <$> exampleLocalNode <*> pure cacheWith4Entries <*> pure 1 <*> (getNid <$> exampleLocalNode) + remoteNode (head $ Set.elems fwSet) `shouldBe` node4 it "joined nodes do not fall back to the default" $ queryLocalCache <$> node1 <*> pure emptyCache <*> pure 1 <*> pure (toNodeID 3) `shouldReturn` FORWARD Set.empty it "works on a cache with less entries than needed" $ do @@ -265,6 +266,17 @@ spec = do let startAt5 = serialiseMessage 600 (largeMessage {part = 5}) Map.lookup 1 startAt5 `shouldBe` Nothing part <$> (deserialiseMessage . fromJust) (Map.lookup 5 startAt5) `shouldBe` Right 5 + describe "join cache lookup" $ + it "A bootstrap cache initialised with just one node returns that one." $ do + let + bootstrapNid = toNodeID 34804191837661041451755206127000721433747285589603756490902196113256157045194 + bootstrapNode = setNid bootstrapNid exampleNodeState + bootstrapCache = addCacheEntryPure 10 (RemoteCacheEntry bootstrapNode 19) initCache + ownId = toNodeID 34804191837661041451755206127000721433707928516052624394829818586723613390165 + ownNode <- setNid ownId <$> exampleLocalNode + let (FORWARD qResult) = queryLocalCache ownNode bootstrapCache 2 ownId + remoteNode (head $ Set.elems qResult) `shouldBe` bootstrapNode + -- some example data