diff options
author | joe <joe@jerkface.net> | 2018-06-06 01:14:12 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2018-06-06 01:14:12 -0400 |
commit | d2fb613ff61d3a8177736f55360ccd08952afc74 (patch) | |
tree | f905c4d1dffcc0346197e113dc75bf26dbc31e28 /src/Network/Kademlia | |
parent | 891606b5ddf0670657735efeabe5daf4b4f04049 (diff) |
Moved persistence and generic dht api from main module to library.
Diffstat (limited to 'src/Network/Kademlia')
-rw-r--r-- | src/Network/Kademlia/CommonAPI.hs | 84 | ||||
-rw-r--r-- | src/Network/Kademlia/Persistence.hs | 51 |
2 files changed, 135 insertions, 0 deletions
diff --git a/src/Network/Kademlia/CommonAPI.hs b/src/Network/Kademlia/CommonAPI.hs new file mode 100644 index 00000000..601be5d8 --- /dev/null +++ b/src/Network/Kademlia/CommonAPI.hs | |||
@@ -0,0 +1,84 @@ | |||
1 | {-# LANGUAGE ExistentialQuantification #-} | ||
2 | module Network.Kademlia.CommonAPI where | ||
3 | |||
4 | |||
5 | import Control.Concurrent | ||
6 | import Control.Concurrent.STM | ||
7 | import Data.Aeson as J (FromJSON, ToJSON) | ||
8 | import Data.Hashable | ||
9 | import qualified Data.Map as Map | ||
10 | import Data.Serialize as S | ||
11 | import qualified Data.Set as Set | ||
12 | import Data.Time.Clock.POSIX | ||
13 | import Data.Typeable | ||
14 | |||
15 | import Network.Kademlia.Search | ||
16 | import Network.Kademlia.Routing as R | ||
17 | import Crypto.Tox (SecretKey,PublicKey) | ||
18 | |||
19 | data DHT = forall nid ni. ( Show ni | ||
20 | , Read ni | ||
21 | , ToJSON ni | ||
22 | , FromJSON ni | ||
23 | , Ord ni | ||
24 | , Hashable ni | ||
25 | , Show nid | ||
26 | , Ord nid | ||
27 | , Hashable nid | ||
28 | , Typeable ni | ||
29 | , S.Serialize nid | ||
30 | ) => | ||
31 | DHT | ||
32 | { dhtBuckets :: TVar (BucketList ni) | ||
33 | , dhtSecretKey :: STM (Maybe SecretKey) | ||
34 | , dhtPing :: Map.Map String (DHTPing ni) | ||
35 | , dhtQuery :: Map.Map String (DHTQuery nid ni) | ||
36 | , dhtAnnouncables :: Map.Map String (DHTAnnouncable nid) | ||
37 | , dhtParseId :: String -> Either String nid | ||
38 | , dhtSearches :: TVar (Map.Map (String,nid) (DHTSearch nid ni)) | ||
39 | , dhtFallbackNodes :: IO [ni] | ||
40 | , dhtBootstrap :: [ni] -> [ni] -> IO () | ||
41 | } | ||
42 | |||
43 | data DHTQuery nid ni = forall addr r tok. | ||
44 | ( Ord addr | ||
45 | , Typeable r | ||
46 | , Typeable tok | ||
47 | , Typeable ni | ||
48 | ) => DHTQuery | ||
49 | { qsearch :: Search nid addr tok ni r | ||
50 | , qhandler :: ni -> nid -> IO ([ni], [r], Maybe tok) -- ^ Invoked on local node, when there is no query destination. | ||
51 | , qshowR :: r -> String | ||
52 | , qshowTok :: tok -> Maybe String | ||
53 | } | ||
54 | |||
55 | data DHTAnnouncable nid = forall dta tok ni r. | ||
56 | ( Show r | ||
57 | , Typeable dta -- information being announced | ||
58 | , Typeable tok -- token | ||
59 | , Typeable r -- search result | ||
60 | , Typeable ni -- node | ||
61 | ) => DHTAnnouncable | ||
62 | { announceParseData :: String -> Either String dta | ||
63 | , announceParseToken :: dta -> String -> Either String tok | ||
64 | , announceParseAddress :: String -> Either String ni | ||
65 | , announceSendData :: Either ( String {- search name -} | ||
66 | , String -> Either String r | ||
67 | , PublicKey {- me -} -> dta -> r -> IO ()) | ||
68 | (dta -> tok -> Maybe ni -> IO (Maybe r)) | ||
69 | , announceInterval :: POSIXTime | ||
70 | , announceTarget :: dta -> nid | ||
71 | } | ||
72 | |||
73 | data DHTSearch nid ni = forall addr tok r. DHTSearch | ||
74 | { searchThread :: ThreadId | ||
75 | , searchState :: SearchState nid addr tok ni r | ||
76 | , searchShowTok :: tok -> Maybe String | ||
77 | , searchResults :: TVar (Set.Set String) | ||
78 | } | ||
79 | |||
80 | data DHTPing ni = forall r. DHTPing | ||
81 | { pingQuery :: [String] -> ni -> IO (Maybe r) | ||
82 | , pingShowResult :: r -> String | ||
83 | } | ||
84 | |||
diff --git a/src/Network/Kademlia/Persistence.hs b/src/Network/Kademlia/Persistence.hs new file mode 100644 index 00000000..d7431671 --- /dev/null +++ b/src/Network/Kademlia/Persistence.hs | |||
@@ -0,0 +1,51 @@ | |||
1 | {-# LANGUAGE NamedFieldPuns #-} | ||
2 | module Network.Kademlia.Persistence where | ||
3 | |||
4 | import Network.Kademlia.CommonAPI | ||
5 | import Network.Kademlia.Routing as R | ||
6 | |||
7 | import Control.Concurrent.STM | ||
8 | import qualified Data.Aeson as J | ||
9 | ;import Data.Aeson as J (FromJSON) | ||
10 | import qualified Data.ByteString.Lazy as L | ||
11 | import qualified Data.HashMap.Strict as HashMap | ||
12 | import Data.List | ||
13 | import qualified Data.Vector as V | ||
14 | import System.IO.Error | ||
15 | |||
16 | saveNodes :: String -> DHT -> IO () | ||
17 | saveNodes netname DHT{dhtBuckets} = do | ||
18 | bkts <- atomically $ readTVar dhtBuckets | ||
19 | let ns = map fst $ concat $ R.toList bkts | ||
20 | bs = J.encode ns | ||
21 | fname = nodesFileName netname | ||
22 | L.writeFile fname bs | ||
23 | |||
24 | loadNodes :: FromJSON ni => String -> IO [ni] | ||
25 | loadNodes netname = do | ||
26 | let fname = nodesFileName netname | ||
27 | attempt <- tryIOError $ do | ||
28 | J.decode <$> L.readFile fname | ||
29 | >>= maybe (ioError $ userError "Nothing") return | ||
30 | either (const $ fallbackLoad fname) return attempt | ||
31 | |||
32 | nodesFileName :: String -> String | ||
33 | nodesFileName netname = netname ++ "-nodes.json" | ||
34 | |||
35 | fallbackLoad :: FromJSON t => FilePath -> IO [t] | ||
36 | fallbackLoad fname = do | ||
37 | attempt <- tryIOError $ do | ||
38 | J.decode <$> L.readFile fname | ||
39 | >>= maybe (ioError $ userError "Nothing") return | ||
40 | let go r = do | ||
41 | let m = HashMap.lookup "nodes" (r :: J.Object) | ||
42 | ns0 = case m of Just (J.Array v) -> V.toList v | ||
43 | Nothing -> [] | ||
44 | ns1 = zip (map J.fromJSON ns0) ns0 | ||
45 | issuc (J.Error _,_) = False | ||
46 | issuc _ = True | ||
47 | (ss,fs) = partition issuc ns1 | ||
48 | ns = map (\(J.Success n,_) -> n) ss | ||
49 | mapM_ print (map snd fs) >> return ns | ||
50 | either (const $ return []) go attempt | ||
51 | |||