From 0e33d442f97f4a947ae7e417e8a0c604f902932c Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Mon, 23 Mar 2020 21:15:10 +0100 Subject: [PATCH] EpiChord: create a stub cache-invariant checker rough logic is implemented, but only for successors and not predecessors. Additionally it just returns empty IO actions instead of lookup operations, until those are implemented. part of #1 Untested! --- Hash2Pub/Hash2Pub.cabal | 2 +- Hash2Pub/src/Hash2Pub/FediChord.hs | 47 +++++++++++++++++++++++++++++- Hash2Pub/src/Hash2Pub/Utils.hs | 6 ++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Hash2Pub/src/Hash2Pub/Utils.hs diff --git a/Hash2Pub/Hash2Pub.cabal b/Hash2Pub/Hash2Pub.cabal index 2d8c26e..8955263 100644 --- a/Hash2Pub/Hash2Pub.cabal +++ b/Hash2Pub/Hash2Pub.cabal @@ -58,7 +58,7 @@ library exposed-modules: Hash2Pub.FediChord -- Modules included in this library but not exported. - -- other-modules: + other-modules: Hash2Pub.Utils -- LANGUAGE extensions used by modules in this package. other-extensions: GeneralizedNewtypeDeriving, DataKinds, OverloadedStrings diff --git a/Hash2Pub/src/Hash2Pub/FediChord.hs b/Hash2Pub/src/Hash2Pub/FediChord.hs index 8dba8d8..872f4e1 100644 --- a/Hash2Pub/src/Hash2Pub/FediChord.hs +++ b/Hash2Pub/src/Hash2Pub/FediChord.hs @@ -36,6 +36,8 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.UTF8 as BSU import qualified Data.ByteArray as BA +import Hash2Pub.Utils + -- define protocol constants -- | static definition of ID length in bits idBits :: Integer @@ -94,7 +96,7 @@ data NodeState = NodeState { , apPort :: Maybe PortNumber -- ^ port of the ActivityPub relay and storage service -- might have to be queried first - , nodeCache :: Map.Map NodeID CacheEntry + , nodeCache :: NodeCache -- ^ EpiChord node cache with expiry times for nodes -- as the map is ordered, lookups for the closes preceding node can be done using @lookupLT@ , successors :: [NodeID] @@ -110,8 +112,13 @@ data NodeState = NodeState { , pNumParallelQueries :: Int -- ^ number of parallel sent queries -- needs to be parameterisable for simulation purposes + , jEntriesPerSlice :: Int + -- ^ number of desired entries per cache slice + -- needs to be parameterisable for simulation purposes } deriving (Show) +type NodeCache = Map.Map NodeID CacheEntry + -- |an entry of the 'nodeCache' type CacheEntry = ( NodeState @@ -168,6 +175,44 @@ byteStringToUInteger bs = sum $ parsedBytes 0 bs +-- TODO: needs testing +-- |checks wether the cache entries fulfill the logarithmic EpiChord invariant +-- of having j entries per slice, and creates a list of necessary lookup actions. +-- Should be invoked periodically. +checkCacheSlices :: NodeState -> [IO ()] +checkCacheSlices state = + checkSlice jEntries (nid state) startBound lastSucc cache' + -- TODO: do the same for predecessors + where + jEntries = jEntriesPerSlice state + cache' = nodeCache state + lastSucc = last <$> maybeEmpty (successors state) + startBound = NodeID 2^(255::Integer) + nid state + checkSlice :: Int -> NodeID -> NodeID -> Maybe NodeID -> NodeCache -> [IO ()] + checkSlice _ _ _ Nothing _ = [] + checkSlice j ownID upperBound (Just lastSuccNode) cache + | upperBound < lastSuccNode = [] + | otherwise = + -- continuously half the DHT namespace, take the upper part as a slice, + -- check for existing entries in that slice and create a lookup action + -- and recursively do this on the lower half. + -- recursion edge case: all successors/ predecessors need to be in the + -- first slice. + let + diff = getNodeID $ upperBound - ownID + lowerBound = ownID + NodeID (diff `div` 2) + in + -- TODO: replace empty IO actions with actual lookups + -- TODO: validate ID before adding to cache + case Map.lookupLT upperBound cache of + Nothing -> return () : checkSlice j ownID lowerBound (Just lastSuccNode) cache + Just (matchID, _) -> + if + matchID <= lowerBound then return () : checkSlice j ownID lowerBound (Just lastSuccNode) cache + else + checkSlice j ownID lowerBound (Just lastSuccNode) cache + + -- Todo: DHT backend can learn potential initial bootstrapping points through the instances mentioned in the received AP-relay messages -- needs to know its own domain anyways for ID generation -- persist them on disk so they can be used for all following bootstraps diff --git a/Hash2Pub/src/Hash2Pub/Utils.hs b/Hash2Pub/src/Hash2Pub/Utils.hs new file mode 100644 index 0000000..5e04a9c --- /dev/null +++ b/Hash2Pub/src/Hash2Pub/Utils.hs @@ -0,0 +1,6 @@ +module Hash2Pub.Utils where + +-- |wraps a list into a Maybe, by replacing empty lists with Nothing +maybeEmpty :: [a] -> Maybe [a] +maybeEmpty [] = Nothing +maybeEmpty nonemptyList = Just nonemptyList