diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Network/Kademlia/Bootstrap.hs | 68 |
1 files changed, 45 insertions, 23 deletions
diff --git a/src/Network/Kademlia/Bootstrap.hs b/src/Network/Kademlia/Bootstrap.hs index 3540b24e..595659d2 100644 --- a/src/Network/Kademlia/Bootstrap.hs +++ b/src/Network/Kademlia/Bootstrap.hs | |||
@@ -5,6 +5,7 @@ | |||
5 | {-# LANGUAGE FlexibleContexts #-} | 5 | {-# LANGUAGE FlexibleContexts #-} |
6 | {-# LANGUAGE GADTs #-} | 6 | {-# LANGUAGE GADTs #-} |
7 | {-# LANGUAGE KindSignatures #-} | 7 | {-# LANGUAGE KindSignatures #-} |
8 | {-# LANGUAGE LambdaCase #-} | ||
8 | {-# LANGUAGE NamedFieldPuns #-} | 9 | {-# LANGUAGE NamedFieldPuns #-} |
9 | {-# LANGUAGE PartialTypeSignatures #-} | 10 | {-# LANGUAGE PartialTypeSignatures #-} |
10 | {-# LANGUAGE PatternSynonyms #-} | 11 | {-# LANGUAGE PatternSynonyms #-} |
@@ -56,24 +57,32 @@ type SensibleNodeId nid ni = | |||
56 | data BucketRefresher nid ni = forall tok addr. Ord addr => BucketRefresher | 57 | data BucketRefresher nid ni = forall tok addr. Ord addr => BucketRefresher |
57 | { -- | A staleness threshold (if a bucket goes this long without being | 58 | { -- | A staleness threshold (if a bucket goes this long without being |
58 | -- touched, a refresh will be triggered). | 59 | -- touched, a refresh will be triggered). |
59 | refreshInterval :: POSIXTime | 60 | refreshInterval :: POSIXTime |
60 | -- | A TVar with the time-to-refresh schedule for each bucket. | 61 | -- | A TVar with the time-to-refresh schedule for each bucket. |
61 | -- | 62 | -- |
62 | -- To "touch" a bucket and prevent it from being refreshed, reschedule | 63 | -- To "touch" a bucket and prevent it from being refreshed, reschedule |
63 | -- it's refresh time to some time into the future by modifying the | 64 | -- its refresh time to some time into the future by modifying its |
64 | -- 'Int.PSQ' in the TVar. (See 'touchBucket'). | 65 | -- priority in this priority search queue. |
65 | , refreshQueue :: TVar (Int.PSQ POSIXTime) | 66 | , refreshQueue :: TVar (Int.PSQ POSIXTime) |
66 | -- | This is the kademlia node search specification. | 67 | -- | This is the kademlia node search specification. |
67 | , refreshSearch :: Search nid addr tok ni ni | 68 | , refreshSearch :: Search nid addr tok ni ni |
68 | -- | The current kademlia routing table buckets. | 69 | -- | The current kademlia routing table buckets. |
69 | , refreshBuckets :: TVar (R.BucketList ni) | 70 | , refreshBuckets :: TVar (R.BucketList ni) |
70 | -- | Action to ping a node. This is used only during initial bootstrap | 71 | -- | Action to ping a node. This is used only during initial bootstrap |
71 | -- to get some nodes in our table. A 'True' result is interpreted as a a | 72 | -- to get some nodes in our table. A 'True' result is interpreted as a a |
72 | -- pong, where 'False' is a non-response. | 73 | -- pong, where 'False' is a non-response. |
73 | , refreshPing :: ni -> IO Bool | 74 | , refreshPing :: ni -> IO Bool |
75 | , -- | Timestamp of last bucket event. | ||
76 | refreshLastTouch :: TVar POSIXTime | ||
77 | , -- | This variable indicates whether or not we are in bootstrapping mode. | ||
78 | bootstrapMode :: TVar Bool | ||
79 | , -- | When this countdown reaches 0, we exit bootstrap mode. It is decremented on | ||
80 | -- every finished refresh. | ||
81 | bootstrapCountdown :: TVar (Maybe Int) | ||
74 | } | 82 | } |
75 | 83 | ||
76 | newBucketRefresher :: (Ord addr, Hashable addr, SensibleNodeId nid ni) | 84 | newBucketRefresher :: ( Ord addr, Hashable addr |
85 | , SensibleNodeId nid ni ) | ||
77 | => ni | 86 | => ni |
78 | -> Search nid addr tok ni ni | 87 | -> Search nid addr tok ni ni |
79 | -> (ni -> IO Bool) | 88 | -> (ni -> IO Bool) |
@@ -83,12 +92,18 @@ newBucketRefresher template_ni sch ping = do | |||
83 | nodeId = kademliaLocation spc | 92 | nodeId = kademliaLocation spc |
84 | bkts <- newTVar $ R.nullTable (comparing nodeId) (\s -> hashWithSalt s . nodeId) template_ni R.defaultBucketCount | 93 | bkts <- newTVar $ R.nullTable (comparing nodeId) (\s -> hashWithSalt s . nodeId) template_ni R.defaultBucketCount |
85 | sched <- newTVar Int.empty | 94 | sched <- newTVar Int.empty |
95 | lasttouch <- newTVar 0 -- Would use getPOSIXTime here, or minBound, but alas... | ||
96 | bootstrapVar <- newTVar True -- Start in bootstrapping mode. | ||
97 | bootstrapCnt <- newTVar Nothing | ||
86 | return BucketRefresher | 98 | return BucketRefresher |
87 | { refreshInterval = 15 * 60 | 99 | { refreshInterval = 15 * 60 |
88 | , refreshQueue = sched | 100 | , refreshQueue = sched |
89 | , refreshSearch = sch | 101 | , refreshSearch = sch |
90 | , refreshBuckets = bkts | 102 | , refreshBuckets = bkts |
91 | , refreshPing = ping | 103 | , refreshPing = ping |
104 | , refreshLastTouch = lasttouch | ||
105 | , bootstrapMode = bootstrapVar | ||
106 | , bootstrapCountdown = bootstrapCnt | ||
92 | } | 107 | } |
93 | 108 | ||
94 | -- | This was added to avoid the compile error "Record update for | 109 | -- | This was added to avoid the compile error "Record update for |
@@ -99,11 +114,14 @@ updateRefresherIO :: Ord addr | |||
99 | -> (ni -> IO Bool) | 114 | -> (ni -> IO Bool) |
100 | -> BucketRefresher nid ni -> BucketRefresher nid ni | 115 | -> BucketRefresher nid ni -> BucketRefresher nid ni |
101 | updateRefresherIO sch ping BucketRefresher{..} = BucketRefresher | 116 | updateRefresherIO sch ping BucketRefresher{..} = BucketRefresher |
102 | { refreshSearch = sch | 117 | { refreshSearch = sch |
103 | , refreshPing = ping | 118 | , refreshPing = ping |
104 | , refreshInterval = refreshInterval | 119 | , refreshInterval = refreshInterval |
105 | , refreshBuckets = refreshBuckets | 120 | , refreshBuckets = refreshBuckets |
106 | , refreshQueue = refreshQueue | 121 | , refreshQueue = refreshQueue |
122 | , refreshLastTouch = refreshLastTouch | ||
123 | , bootstrapMode = bootstrapMode | ||
124 | , bootstrapCountdown = bootstrapCountdown | ||
107 | } | 125 | } |
108 | 126 | ||
109 | -- | Fork a refresh loop. Kill the returned thread to terminate it. | 127 | -- | Fork a refresh loop. Kill the returned thread to terminate it. |
@@ -197,14 +215,19 @@ refreshBucket sch var n = do | |||
197 | return $ if b then 1 else c | 215 | return $ if b then 1 else c |
198 | return rcount | 216 | return rcount |
199 | 217 | ||
218 | refreshLastBucket :: SensibleNodeId nid ni => BucketRefresher nid ni -> IO () | ||
219 | refreshLastBucket r@BucketRefresher { refreshBuckets, refreshSearch } = do | ||
220 | cnt <- atomically $ bktCount <$> readTVar refreshBuckets | ||
221 | void $ refreshBucket refreshSearch refreshBuckets (cnt - 1) | ||
222 | |||
200 | bootstrap :: (Ord ni, Ord nid, Hashable nid, Hashable ni, Foldable t, Foldable t1, Show nid) => | 223 | bootstrap :: (Ord ni, Ord nid, Hashable nid, Hashable ni, Foldable t, Foldable t1, Show nid) => |
201 | BucketRefresher nid ni | 224 | BucketRefresher nid ni |
202 | -> t1 ni -- ^ Nodes to bootstrap from. | 225 | -> t1 ni -- ^ Nodes to bootstrap from. |
203 | -> t ni -- ^ Fallback nodes; used only if the others are unresponsive. | 226 | -> t ni -- ^ Fallback nodes; used only if the others are unresponsive. |
204 | -> IO () | 227 | -> IO () |
205 | bootstrap BucketRefresher { refreshSearch = sch | 228 | bootstrap r@BucketRefresher { refreshSearch = sch |
206 | , refreshBuckets = var | 229 | , refreshBuckets = var |
207 | , refreshPing = ping } ns ns0 = do | 230 | , refreshPing = ping } ns ns0 = do |
208 | gotPing <- atomically $ newTVar False | 231 | gotPing <- atomically $ newTVar False |
209 | 232 | ||
210 | -- First, ping the given nodes so that they are added to | 233 | -- First, ping the given nodes so that they are added to |
@@ -226,8 +249,7 @@ bootstrap BucketRefresher { refreshSearch = sch | |||
226 | (void $ ping n) | 249 | (void $ ping n) |
227 | hPutStrLn stderr "Finished bootstrap pings." | 250 | hPutStrLn stderr "Finished bootstrap pings." |
228 | -- Now search our own Id by refreshing the last bucket. | 251 | -- Now search our own Id by refreshing the last bucket. |
229 | last <- atomically $ bktCount <$> readTVar var | 252 | refreshLastBucket r |
230 | void $ refreshBucket sch var last | ||
231 | -- That's it. | 253 | -- That's it. |
232 | -- | 254 | -- |
233 | -- Hopefully 'forkPollForRefresh' was invoked and can take over | 255 | -- Hopefully 'forkPollForRefresh' was invoked and can take over |