summaryrefslogtreecommitdiff
path: root/src/Network/Kademlia/Bootstrap.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/Kademlia/Bootstrap.hs')
-rw-r--r--src/Network/Kademlia/Bootstrap.hs68
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 =
56data BucketRefresher nid ni = forall tok addr. Ord addr => BucketRefresher 57data 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
76newBucketRefresher :: (Ord addr, Hashable addr, SensibleNodeId nid ni) 84newBucketRefresher :: ( 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
101updateRefresherIO sch ping BucketRefresher{..} = BucketRefresher 116updateRefresherIO 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
218refreshLastBucket :: SensibleNodeId nid ni => BucketRefresher nid ni -> IO ()
219refreshLastBucket r@BucketRefresher { refreshBuckets, refreshSearch } = do
220 cnt <- atomically $ bktCount <$> readTVar refreshBuckets
221 void $ refreshBucket refreshSearch refreshBuckets (cnt - 1)
222
200bootstrap :: (Ord ni, Ord nid, Hashable nid, Hashable ni, Foldable t, Foldable t1, Show nid) => 223bootstrap :: (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 ()
205bootstrap BucketRefresher { refreshSearch = sch 228bootstrap 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