diff --git a/Hash2Pub/src/Hash2Pub/FediChord.hs b/Hash2Pub/src/Hash2Pub/FediChord.hs index d03c5e8..4ca5afc 100644 --- a/Hash2Pub/src/Hash2Pub/FediChord.hs +++ b/Hash2Pub/src/Hash2Pub/FediChord.hs @@ -17,6 +17,7 @@ module Hash2Pub.FediChord ( , NodeCache , CacheEntry , cacheGetNodeStateUnvalidated + , addCacheEntry , genNodeID , genNodeIDBS , genKeyID @@ -26,7 +27,7 @@ module Hash2Pub.FediChord ( import qualified Data.Map.Strict as Map import Network.Socket -import Data.Time.Clock.System +import Data.Time.Clock import Control.Exception -- for hashing and ID conversion @@ -127,9 +128,21 @@ type NodeCache = Map.Map NodeID CacheEntry type CacheEntry = ( Bool , NodeState - , SystemTime + , UTCTime ) -- ^ ( a node's validation status, data, timestamp for cache entry expiration ) +addCacheEntry :: NodeState -> Integer -> NodeCache -> IO NodeCache +addCacheEntry node diffSeconds cache = do + now <- getCurrentTime + let + -- TODO: limit diffSeconds to some maximum value to prevent malicious nodes from inserting entries valid nearly until eternity + timestamp = fromInteger (negate diffSeconds) `addUTCTime` now + newCache = Map.insert (nid node) (False, node, timestamp) cache + return newCache + +-- clean up cache entries: once now - entry > maxAge +-- transfer difference now - entry to other node + -- | return the @NodeState@ data from a cache entry without checking its validation status cacheGetNodeStateUnvalidated :: CacheEntry -> NodeState cacheGetNodeStateUnvalidated (_, nState, _) = nState diff --git a/Hash2Pub/test/FediChordSpec.hs b/Hash2Pub/test/FediChordSpec.hs index a1d6fee..a566a55 100644 --- a/Hash2Pub/test/FediChordSpec.hs +++ b/Hash2Pub/test/FediChordSpec.hs @@ -11,12 +11,6 @@ import Hash2Pub.FediChord spec :: Spec spec = do - -- define some sensible test data - let - nodeDomain = "example.social" - vs = 4 - ip = tupleToHostAddress6 (0x2001, 0x16b8, 0x755a, 0xb110, 0x7d6a, 0x12ab, 0xf0c5, 0x386e) - describe "NodeID" $ do it "can store a numeral ID" $ getNodeID (toNodeID 2342) `shouldBe` 2342 @@ -36,8 +30,8 @@ spec = do it "throws an exception when @toNodeID@ on out-of-bound values" pending it "can be generated" $ do - genNodeIDBS ip nodeDomain vs `shouldBe` "\ACK\211\183&S\GS\214\247Xn8\216\232\195\247\162\182\253\210\SOHG7I\194\251\196\130\142RSx\219" - genNodeID ip nodeDomain vs `shouldBe` toNodeID 3087945874980469002564169693112490135217795916629034079089428181202645514459 + genNodeIDBS exampleIp exampleNodeDomain exampleVs `shouldBe` "\ACK\211\183&S\GS\214\247Xn8\216\232\195\247\162\182\253\210\SOHG7I\194\251\196\130\142RSx\219" + genNodeID exampleIp exampleNodeDomain exampleVs `shouldBe` toNodeID 3087945874980469002564169693112490135217795916629034079089428181202645514459 describe "ByteString to Integer conversion" $ it "correctly interprets ByteStrings as unsigned big-endian integers" $ do @@ -51,27 +45,13 @@ spec = do genKeyID "#sometag" `shouldBe` 80934974700514031200587628522801847528706765451025022694022301350330549806700 genKeyID "#ÄปӥicоdeTag" `shouldBe` 5709825004658123480531764908635278432808461265905814952223156184506818894505 describe "NodeState" $ do - it "can be initialised" $ do - let ns = NodeState { - nid = toNodeID 12 - , domain = nodeDomain - , ipAddr = ip - , dhtPort = 2342 - , apPort = Nothing - , nodeCache = Map.empty - , successors = [] - , predecessors = [] - , kNeighbours = 3 - , lNumBestNodes = 3 - , pNumParallelQueries = 2 - , jEntriesPerSlice = 2 - } - print ns + it "can be initialised" $ + print exampleNodeState it "can be initialised partly and then modified later" $ do let ns = NodeState { nid = undefined - , domain = nodeDomain - , ipAddr = ip + , domain = exampleNodeDomain + , ipAddr = exampleIp , dhtPort = 2342 , apPort = Nothing , nodeCache = Map.empty @@ -86,4 +66,37 @@ spec = do nid = genNodeID (ipAddr ns) (domain ns) 3 } print nsReady + describe "NodeCache" $ do + it "entries can be added to a node cache" $ do + let + emptyCache = nodeCache exampleNodeState + anotherNode = exampleNodeState { nid = (toNodeID 2^(23::Integer)+1)} + newCache <- addCacheEntry exampleNodeState 0 =<< addCacheEntry anotherNode 10 emptyCache + Map.size newCache `shouldBe` 2 + +-- some example data + +exampleNodeState :: NodeState +exampleNodeState = NodeState { + nid = toNodeID 12 + , domain = exampleNodeDomain + , ipAddr = exampleIp + , dhtPort = 2342 + , apPort = Nothing + , nodeCache = Map.empty + , successors = [] + , predecessors = [] + , kNeighbours = 3 + , lNumBestNodes = 3 + , pNumParallelQueries = 2 + , jEntriesPerSlice = 2 + } + +exampleNodeDomain :: String +exampleNodeDomain = "example.social" +exampleVs :: (Integral i) => i +exampleVs = 4 +exampleIp :: HostAddress6 +exampleIp = tupleToHostAddress6 (0x2001, 0x16b8, 0x755a, 0xb110, 0x7d6a, 0x12ab, 0xf0c5, 0x386e) +