summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/DHT/Routing.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/BitTorrent/DHT/Routing.hs')
-rw-r--r--src/Network/BitTorrent/DHT/Routing.hs268
1 files changed, 141 insertions, 127 deletions
diff --git a/src/Network/BitTorrent/DHT/Routing.hs b/src/Network/BitTorrent/DHT/Routing.hs
index 6cf7f122..42728a53 100644
--- a/src/Network/BitTorrent/DHT/Routing.hs
+++ b/src/Network/BitTorrent/DHT/Routing.hs
@@ -13,12 +13,14 @@
13-- For more info see: 13-- For more info see:
14-- <http://www.bittorrent.org/beps/bep_0005.html#routing-table> 14-- <http://www.bittorrent.org/beps/bep_0005.html#routing-table>
15-- 15--
16{-# LANGUAGE CPP #-}
16{-# LANGUAGE RecordWildCards #-} 17{-# LANGUAGE RecordWildCards #-}
17{-# LANGUAGE BangPatterns #-} 18{-# LANGUAGE BangPatterns #-}
18{-# LANGUAGE ViewPatterns #-} 19{-# LANGUAGE ViewPatterns #-}
19{-# LANGUAGE TypeOperators #-} 20{-# LANGUAGE TypeOperators #-}
20{-# LANGUAGE DeriveGeneric #-} 21{-# LANGUAGE DeriveGeneric #-}
21{-# LANGUAGE ScopedTypeVariables #-} 22{-# LANGUAGE ScopedTypeVariables #-}
23{-# LANGUAGE StandaloneDeriving, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-} 24{-# OPTIONS_GHC -fno-warn-orphans #-}
23module Network.BitTorrent.DHT.Routing 25module Network.BitTorrent.DHT.Routing
24 ( -- * Table 26 ( -- * Table
@@ -59,8 +61,6 @@ module Network.BitTorrent.DHT.Routing
59 61
60 -- * Routing 62 -- * Routing
61 , Timestamp 63 , Timestamp
62 , Routing
63 , runRouting
64 ) where 64 ) where
65 65
66import Control.Applicative as A 66import Control.Applicative as A
@@ -83,10 +83,16 @@ import Text.PrettyPrint.HughesPJClass (pPrint,Pretty)
83import qualified Data.ByteString as BS 83import qualified Data.ByteString as BS
84import Data.Bits 84import Data.Bits
85 85
86import Network.KRPC.Message (KMessageOf)
87import Data.Torrent 86import Data.Torrent
88import Network.BitTorrent.Address 87import Network.BitTorrent.Address
89import Network.DHT.Mainline 88#ifdef VERSION_bencoding
89import Network.DHT.Mainline ()
90import Network.KRPC.Message (KMessageOf)
91#else
92import Data.Tox as Tox
93type KMessageOf = Tox.Message
94#endif
95
90 96
91{----------------------------------------------------------------------- 97{-----------------------------------------------------------------------
92-- Routing monad 98-- Routing monad
@@ -109,66 +115,6 @@ import Network.DHT.Mainline
109-- 115--
110type Timestamp = POSIXTime 116type Timestamp = POSIXTime
111 117
112-- | Some routing operations might need to perform additional IO.
113data Routing ip result
114 = Full
115 | Done result
116 | GetTime ( Timestamp -> Routing ip result)
117 | NeedPing (NodeAddr ip) ( Bool -> Routing ip result)
118 | Refresh NodeId (Routing ip result)
119
120instance Functor (Routing ip) where
121 fmap _ Full = Full
122 fmap f (Done r) = Done ( f r)
123 fmap f (GetTime g) = GetTime (fmap f . g)
124 fmap f (NeedPing addr g) = NeedPing addr (fmap f . g)
125 fmap f (Refresh nid g) = Refresh nid (fmap f g)
126
127instance Monad (Routing ip) where
128 return = Done
129
130 Full >>= _ = Full
131 Done r >>= m = m r
132 GetTime f >>= m = GetTime $ \ t -> f t >>= m
133 NeedPing a f >>= m = NeedPing a $ \ p -> f p >>= m
134 Refresh n f >>= m = Refresh n $ f >>= m
135
136instance Applicative (Routing ip) where
137 pure = return
138 (<*>) = ap
139
140instance Alternative (Routing ip) where
141 empty = Full
142
143 Full <|> m = m
144 Done a <|> _ = Done a
145 GetTime f <|> m = GetTime $ \ t -> f t <|> m
146 NeedPing a f <|> m = NeedPing a $ \ p -> f p <|> m
147 Refresh n f <|> m = Refresh n (f <|> m)
148
149-- | Run routing table operation.
150runRouting :: Monad m
151 => (NodeAddr ip -> m Bool) -- ^ ping the specific node;
152 -> (NodeId -> m ()) -- ^ refresh nodes;
153 -> m Timestamp -- ^ get current time;
154 -> Routing ip f -- ^ operation to run;
155 -> m (Maybe f) -- ^ operation result;
156runRouting ping_node find_nodes timestamper = go
157 where
158 go Full = return (Nothing)
159 go (Done r) = return (Just r)
160 go (GetTime f) = do
161 t <- timestamper
162 go (f t)
163
164 go (NeedPing addr f) = do
165 pong <- ping_node addr
166 go (f pong)
167
168 go (Refresh nid f) = do
169 find_nodes nid
170 go f
171
172{----------------------------------------------------------------------- 118{-----------------------------------------------------------------------
173 Bucket 119 Bucket
174-----------------------------------------------------------------------} 120-----------------------------------------------------------------------}
@@ -182,7 +128,7 @@ runRouting ping_node find_nodes timestamper = go
182-- other words: new nodes are used only when older nodes disappear. 128-- other words: new nodes are used only when older nodes disappear.
183 129
184-- | Timestamp - last time this node is pinged. 130-- | Timestamp - last time this node is pinged.
185type NodeEntry ip = Binding (NodeInfo KMessageOf ip ()) Timestamp 131type NodeEntry dht ip u = Binding (NodeInfo dht ip u) Timestamp
186 132
187-- TODO instance Pretty where 133-- TODO instance Pretty where
188 134
@@ -213,7 +159,7 @@ fromQ embed project QueueMethods{..} =
213 } 159 }
214-} 160-}
215 161
216seqQ :: QueueMethods Identity (NodeInfo KMessageOf ip ()) (Seq.Seq (NodeInfo KMessageOf ip ())) 162seqQ :: QueueMethods Identity (NodeInfo dht ip u) (Seq.Seq (NodeInfo dht ip u))
217seqQ = QueueMethods 163seqQ = QueueMethods
218 { pushBack = \e fifo -> pure (fifo Seq.|> e) 164 { pushBack = \e fifo -> pure (fifo Seq.|> e)
219 , popFront = \fifo -> case Seq.viewl fifo of 165 , popFront = \fifo -> case Seq.viewl fifo of
@@ -222,9 +168,9 @@ seqQ = QueueMethods
222 , emptyQueue = pure Seq.empty 168 , emptyQueue = pure Seq.empty
223 } 169 }
224 170
225type BucketQueue ip = Seq.Seq (NodeInfo KMessageOf ip ()) 171type BucketQueue dht ip u = Seq.Seq (NodeInfo dht ip u)
226 172
227bucketQ :: QueueMethods Identity (NodeInfo KMessageOf ip ()) (BucketQueue ip) 173bucketQ :: QueueMethods Identity (NodeInfo dht ip u) (BucketQueue dht ip u)
228bucketQ = seqQ 174bucketQ = seqQ
229 175
230-- | Bucket is also limited in its length — thus it's called k-bucket. 176-- | Bucket is also limited in its length — thus it's called k-bucket.
@@ -234,16 +180,45 @@ bucketQ = seqQ
234-- very unlikely that all nodes in bucket fail within an hour of 180-- very unlikely that all nodes in bucket fail within an hour of
235-- each other. 181-- each other.
236-- 182--
237data Bucket ip = Bucket { bktNodes :: !(PSQ (NodeInfo KMessageOf ip ()) Timestamp) 183data Bucket dht ip u = Bucket { bktNodes :: !(PSQ (NodeInfo dht ip u) Timestamp)
238 , bktQ :: !(BucketQueue ip) 184 , bktQ :: !(BucketQueue dht ip u)
239 } deriving (Show,Generic) 185 } deriving Generic
240 186
241instance (Eq ip, Serialize ip) => Serialize (Bucket ip) where 187deriving instance (Show ip, Show u, Show (NodeId dht)) => Show (Bucket dht ip u)
242 get = Bucket . psqFromPairList <$> get <*> pure (runIdentity $ emptyQueue bucketQ) 188
243 put = put . psqToPairList . bktNodes 189
244 190getGenericNode :: ( Serialize (NodeId dht)
191 , Serialize ip
192 , Serialize u
193 ) => Get (NodeInfo dht ip u)
194getGenericNode = do
195 nid <- get
196 naddr <- get
197 u <- get
198 return NodeInfo
199 { nodeId = nid
200 , nodeAddr = naddr
201 , nodeAnnotation = u
202 }
203
204putGenericNode :: ( Serialize (NodeId dht)
205 , Serialize ip
206 , Serialize u
207 ) => NodeInfo dht ip u -> Put
208putGenericNode (NodeInfo nid naddr u) = do
209 put nid
210 put naddr
211 put u
212
213instance (Eq ip, Ord (NodeId dht), Serialize (NodeId dht), Serialize ip, Serialize u) => Serialize (Bucket dht ip u) where
214 get = Bucket . psqFromPairList <$> getListOf ( (,) <$> getGenericNode <*> get ) <*> pure (runIdentity $ emptyQueue bucketQ)
215 put = putListOf (\(ni,stamp) -> putGenericNode ni >> put stamp) . psqToPairList . bktNodes
216
217
218psqFromPairList :: (Ord p, Ord k) => [(k, p)] -> OrdPSQ k p ()
245psqFromPairList xs = PSQ.fromList $ map (\(a,b) -> a :-> b) xs 219psqFromPairList xs = PSQ.fromList $ map (\(a,b) -> a :-> b) xs
246 220
221psqToPairList :: OrdPSQ t t1 () -> [(t, t1)]
247psqToPairList psq = map (\(a :-> b) -> (a,b)) $ PSQ.toList psq 222psqToPairList psq = map (\(a :-> b) -> (a,b)) $ PSQ.toList psq
248 223
249-- | Update interval, in seconds. 224-- | Update interval, in seconds.
@@ -253,8 +228,8 @@ delta = 15 * 60
253-- | Should maintain a set of stable long running nodes. 228-- | Should maintain a set of stable long running nodes.
254-- 229--
255-- Note: pings are triggerd only when a bucket is full. 230-- Note: pings are triggerd only when a bucket is full.
256insertBucket :: (Eq ip, Alternative f) => Timestamp -> Event ip -> Bucket ip 231insertBucket :: (Eq ip, Alternative f, Ord (NodeId dht)) => Timestamp -> Event dht ip u -> Bucket dht ip u
257 -> f ([CheckPing ip], Bucket ip) 232 -> f ([CheckPing dht ip u], Bucket dht ip u)
258insertBucket curTime (TryInsert info) bucket 233insertBucket curTime (TryInsert info) bucket
259 -- just update timestamp if a node is already in bucket 234 -- just update timestamp if a node is already in bucket
260 | already_have 235 | already_have
@@ -305,7 +280,9 @@ insertBucket curTime (PingResult bad_node got_response) bucket
305 pure $ PSQ.insert info curTime nodes' 280 pure $ PSQ.insert info curTime nodes'
306 | otherwise = id 281 | otherwise = id
307 282
308updateStamps :: Eq ip => Timestamp -> [NodeInfo KMessageOf ip ()] -> PSQ (NodeInfo KMessageOf ip ()) Timestamp -> PSQ (NodeInfo KMessageOf ip ()) Timestamp 283updateStamps :: ( Eq ip
284 , Ord (NodeId dht)
285 ) => Timestamp -> [NodeInfo dht ip u] -> PSQ (NodeInfo dht ip u) Timestamp -> PSQ (NodeInfo dht ip u) Timestamp
309updateStamps curTime stales nodes = foldl' (\q n -> PSQ.insert n curTime q) nodes stales 286updateStamps curTime stales nodes = foldl' (\q n -> PSQ.insert n curTime q) nodes stales
310 287
311 288
@@ -327,7 +304,11 @@ partitionQ imp test q0 = do
327 select f = if test e then \(a,b) -> flip (,) b <$> f a 304 select f = if test e then \(a,b) -> flip (,) b <$> f a
328 else \(a,b) -> (,) a <$> f b 305 else \(a,b) -> (,) a <$> f b
329 306
330split :: Eq ip => BitIx -> Bucket ip -> (Bucket ip, Bucket ip) 307split :: forall dht ip u.
308 ( Eq ip
309 , Ord (NodeId dht)
310 , FiniteBits (NodeId dht)
311 ) => BitIx -> Bucket dht ip u -> (Bucket dht ip u, Bucket dht ip u)
331split i b = (Bucket ns qs, Bucket ms rs) 312split i b = (Bucket ns qs, Bucket ms rs)
332 where 313 where
333 (ns,ms) = (PSQ.fromList *** PSQ.fromList) . partition (spanBit . key) . PSQ.toList $ bktNodes b 314 (ns,ms) = (PSQ.fromList *** PSQ.fromList) . partition (spanBit . key) . PSQ.toList $ bktNodes b
@@ -337,7 +318,7 @@ split i b = (Bucket ns qs, Bucket ms rs)
337 FiniteBits (Network.RPC.NodeId dht) => 318 FiniteBits (Network.RPC.NodeId dht) =>
338 NodeInfo dht addr u -> Bool 319 NodeInfo dht addr u -> Bool
339 -} 320 -}
340 spanBit :: NodeInfo KMessageOf addr () -> Bool 321 spanBit :: NodeInfo dht addr u -> Bool
341 spanBit entry = testIdBit (nodeId entry) i 322 spanBit entry = testIdBit (nodeId entry) i
342 323
343{----------------------------------------------------------------------- 324{-----------------------------------------------------------------------
@@ -350,12 +331,15 @@ type BucketCount = Int
350defaultBucketCount :: BucketCount 331defaultBucketCount :: BucketCount
351defaultBucketCount = 20 332defaultBucketCount = 20
352 333
353data Info ip = Info 334data Info dht ip u = Info
354 { myBuckets :: Table ip 335 { myBuckets :: Table dht ip u
355 , myNodeId :: NodeId 336 , myNodeId :: NodeId dht
356 , myAddress :: SockAddr 337 , myAddress :: SockAddr
357 } 338 }
358 deriving (Eq, Show, Generic) 339 deriving Generic
340
341deriving instance (Eq ip, Eq u, Eq (NodeId dht)) => Eq (Info dht ip u)
342deriving instance (Show ip, Show u, Show (NodeId dht)) => Show (Info dht ip u)
359 343
360-- instance (Eq ip, Serialize ip) => Serialize (Info ip) 344-- instance (Eq ip, Serialize ip) => Serialize (Info ip)
361 345
@@ -375,31 +359,33 @@ data Info ip = Info
375-- is always split into two new buckets covering the ranges @0..2 ^ 359-- is always split into two new buckets covering the ranges @0..2 ^
376-- 159@ and @2 ^ 159..2 ^ 160@. 360-- 159@ and @2 ^ 159..2 ^ 160@.
377-- 361--
378data Table ip 362data Table dht ip u
379 -- most nearest bucket 363 -- most nearest bucket
380 = Tip NodeId BucketCount (Bucket ip) 364 = Tip (NodeId dht) BucketCount (Bucket dht ip u)
381 365
382 -- left biased tree branch 366 -- left biased tree branch
383 | Zero (Table ip) (Bucket ip) 367 | Zero (Table dht ip u) (Bucket dht ip u)
384 368
385 -- right biased tree branch 369 -- right biased tree branch
386 | One (Bucket ip) (Table ip) 370 | One (Bucket dht ip u) (Table dht ip u)
387 deriving (Show, Generic) 371 deriving Generic
388 372
389instance Eq ip => Eq (Table ip) where 373instance (Eq ip, Eq (NodeId dht)) => Eq (Table dht ip u) where
390 (==) = (==) `on` Network.BitTorrent.DHT.Routing.toList 374 (==) = (==) `on` Network.BitTorrent.DHT.Routing.toList
391 375
392instance Serialize NominalDiffTime where 376instance Serialize NominalDiffTime where
393 put = putWord32be . fromIntegral . fromEnum 377 put = putWord32be . fromIntegral . fromEnum
394 get = (toEnum . fromIntegral) <$> getWord32be 378 get = (toEnum . fromIntegral) <$> getWord32be
395 379
380deriving instance (Show ip, Show u, Show (NodeId dht)) => Show (Table dht ip u)
381
396-- | Normally, routing table should be saved between invocations of 382-- | Normally, routing table should be saved between invocations of
397-- the client software. Note that you don't need to store /this/ 383-- the client software. Note that you don't need to store /this/
398-- 'NodeId' since it is already included in routing table. 384-- 'NodeId' since it is already included in routing table.
399instance (Eq ip, Serialize ip) => Serialize (Table ip) 385instance (Eq ip, Serialize ip, Ord (NodeId dht), Serialize (NodeId dht), Serialize u) => Serialize (Table dht ip u)
400 386
401-- | Shape of the table. 387-- | Shape of the table.
402instance Pretty (Table ip) where 388instance Pretty (Table dht ip u) where
403 pPrint t 389 pPrint t
404 | bucketCount < 6 = hcat $ punctuate ", " $ L.map PP.int ss 390 | bucketCount < 6 = hcat $ punctuate ", " $ L.map PP.int ss
405 | otherwise = brackets $ 391 | otherwise = brackets $
@@ -410,26 +396,26 @@ instance Pretty (Table ip) where
410 ss = shape t 396 ss = shape t
411 397
412-- | Empty table with specified /spine/ node id. 398-- | Empty table with specified /spine/ node id.
413nullTable :: Eq ip => NodeId -> BucketCount -> Table ip 399nullTable :: Eq ip => NodeId dht -> BucketCount -> Table dht ip u
414nullTable nid n = Tip nid (bucketCount (pred n)) (Bucket PSQ.empty (runIdentity $ emptyQueue bucketQ)) 400nullTable nid n = Tip nid (bucketCount (pred n)) (Bucket PSQ.empty (runIdentity $ emptyQueue bucketQ))
415 where 401 where
416 bucketCount x = max 0 (min 159 x) 402 bucketCount x = max 0 (min 159 x)
417 403
418-- | Test if table is empty. In this case DHT should start 404-- | Test if table is empty. In this case DHT should start
419-- bootstrapping process until table becomes 'full'. 405-- bootstrapping process until table becomes 'full'.
420null :: Table ip -> Bool 406null :: Table dht ip u -> Bool
421null (Tip _ _ b) = PSQ.null $ bktNodes b 407null (Tip _ _ b) = PSQ.null $ bktNodes b
422null _ = False 408null _ = False
423 409
424-- | Test if table have maximum number of nodes. No more nodes can be 410-- | Test if table have maximum number of nodes. No more nodes can be
425-- 'insert'ed, except old ones becomes bad. 411-- 'insert'ed, except old ones becomes bad.
426full :: Table ip -> Bool 412full :: Table dht ip u -> Bool
427full (Tip _ n _) = n == 0 413full (Tip _ n _) = n == 0
428full (Zero t b) = PSQ.size (bktNodes b) == defaultBucketSize && full t 414full (Zero t b) = PSQ.size (bktNodes b) == defaultBucketSize && full t
429full (One b t) = PSQ.size (bktNodes b) == defaultBucketSize && full t 415full (One b t) = PSQ.size (bktNodes b) == defaultBucketSize && full t
430 416
431-- | Get the /spine/ node id. 417-- | Get the /spine/ node id.
432thisId :: Table ip -> NodeId 418thisId :: Table dht ip u -> NodeId dht
433thisId (Tip nid _ _) = nid 419thisId (Tip nid _ _) = nid
434thisId (Zero table _) = thisId table 420thisId (Zero table _) = thisId table
435thisId (One _ table) = thisId table 421thisId (One _ table) = thisId table
@@ -439,18 +425,19 @@ type NodeCount = Int
439 425
440-- | Internally, routing table is similar to list of buckets or a 426-- | Internally, routing table is similar to list of buckets or a
441-- /matrix/ of nodes. This function returns the shape of the matrix. 427-- /matrix/ of nodes. This function returns the shape of the matrix.
442shape :: Table ip -> [BucketSize] 428shape :: Table dht ip u -> [BucketSize]
443shape = map (PSQ.size . bktNodes) . toBucketList 429shape = map (PSQ.size . bktNodes) . toBucketList
444 430
445-- | Get number of nodes in the table. 431-- | Get number of nodes in the table.
446size :: Table ip -> NodeCount 432size :: Table dht ip u -> NodeCount
447size = L.sum . shape 433size = L.sum . shape
448 434
449-- | Get number of buckets in the table. 435-- | Get number of buckets in the table.
450depth :: Table ip -> BucketCount 436depth :: Table dht ip u -> BucketCount
451depth = L.length . shape 437depth = L.length . shape
452 438
453lookupBucket :: NodeId -> Table ip -> [Bucket ip] 439lookupBucket :: ( FiniteBits (NodeId dht)
440 ) => NodeId dht -> Table dht ip u -> [Bucket dht ip u]
454lookupBucket nid = go 0 [] 441lookupBucket nid = go 0 []
455 where 442 where
456 go i bs (Zero table bucket) 443 go i bs (Zero table bucket)
@@ -461,14 +448,18 @@ lookupBucket nid = go 0 []
461 | otherwise = bucket : toBucketList table ++ bs 448 | otherwise = bucket : toBucketList table ++ bs
462 go _ bs (Tip _ _ bucket) = bucket : bs 449 go _ bs (Tip _ _ bucket) = bucket : bs
463 450
464compatibleNodeId :: Table ip -> IO NodeId 451compatibleNodeId :: forall dht ip u.
452 ( Serialize (NodeId dht)
453 , FiniteBits (NodeId dht)
454 ) => Table dht ip u -> IO (NodeId dht)
465compatibleNodeId tbl = genBucketSample prefix br 455compatibleNodeId tbl = genBucketSample prefix br
466 where 456 where
467 br = bucketRange (L.length (shape tbl) - 1) True 457 br = bucketRange (L.length (shape tbl) - 1) True
458 nodeIdSize = finiteBitSize (undefined :: NodeId dht) `div` 8
468 bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0 459 bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0
469 prefix = either error id $ S.decode bs 460 prefix = either error id $ S.decode bs
470 461
471tablePrefix :: Table ip -> [Word8] 462tablePrefix :: Table dht ip u -> [Word8]
472tablePrefix = map (packByte . take 8 . (++repeat False)) 463tablePrefix = map (packByte . take 8 . (++repeat False))
473 . chunksOf 8 464 . chunksOf 8
474 . tableBits 465 . tableBits
@@ -477,7 +468,7 @@ tablePrefix = map (packByte . take 8 . (++repeat False))
477 bitmask ix True = bit ix 468 bitmask ix True = bit ix
478 bitmask _ _ = 0 469 bitmask _ _ = 0
479 470
480tableBits :: Table ip -> [Bool] 471tableBits :: Table dht ip u -> [Bool]
481tableBits (One _ tbl) = True : tableBits tbl 472tableBits (One _ tbl) = True : tableBits tbl
482tableBits (Zero tbl _) = False : tableBits tbl 473tableBits (Zero tbl _) = False : tableBits tbl
483tableBits (Tip _ _ _) = [] 474tableBits (Tip _ _ _) = []
@@ -498,20 +489,23 @@ type K = Int
498defaultK :: K 489defaultK :: K
499defaultK = 8 490defaultK = 8
500 491
501class TableKey k where 492class TableKey dht k where
502 toNodeId :: k -> NodeId 493 toNodeId :: k -> NodeId dht
503 494
504instance TableKey NodeId where 495instance TableKey dht (NodeId dht) where
505 toNodeId = id 496 toNodeId = id
506 497
507instance TableKey InfoHash where 498instance TableKey KMessageOf InfoHash where
508 toNodeId = either (error msg) id . S.decode . S.encode 499 toNodeId = either (error msg) id . S.decode . S.encode
509 where -- TODO unsafe coerse? 500 where -- TODO unsafe coerse?
510 msg = "tableKey: impossible" 501 msg = "tableKey: impossible"
511 502
512-- | Get a list of /K/ closest nodes using XOR metric. Used in 503-- | Get a list of /K/ closest nodes using XOR metric. Used in
513-- 'find_node' and 'get_peers' queries. 504-- 'find_node' and 'get_peers' queries.
514kclosest :: Eq ip => TableKey a => K -> a -> Table ip -> [NodeInfo KMessageOf ip ()] 505kclosest :: ( Eq ip
506 , Ord (NodeId dht)
507 , FiniteBits (NodeId dht)
508 ) => TableKey dht a => K -> a -> Table dht ip u -> [NodeInfo dht ip u]
515kclosest k (toNodeId -> nid) tbl = take k $ rank nodeId nid (L.concat bucket) 509kclosest k (toNodeId -> nid) tbl = take k $ rank nodeId nid (L.concat bucket)
516 ++ rank nodeId nid (L.concat everyone) 510 ++ rank nodeId nid (L.concat everyone)
517 where 511 where
@@ -525,7 +519,10 @@ kclosest k (toNodeId -> nid) tbl = take k $ rank nodeId nid (L.concat bucket)
525-- Routing 519-- Routing
526-----------------------------------------------------------------------} 520-----------------------------------------------------------------------}
527 521
528splitTip :: Eq ip => NodeId -> BucketCount -> BitIx -> Bucket ip -> Table ip 522splitTip :: ( Eq ip
523 , Ord (NodeId dht)
524 , FiniteBits (NodeId dht)
525 ) => NodeId dht -> BucketCount -> BitIx -> Bucket dht ip u -> Table dht ip u
529splitTip nid n i bucket 526splitTip nid n i bucket
530 | testIdBit nid i = (One zeros (Tip nid (pred n) ones)) 527 | testIdBit nid i = (One zeros (Tip nid (pred n) ones))
531 | otherwise = (Zero (Tip nid (pred n) zeros) ones) 528 | otherwise = (Zero (Tip nid (pred n) zeros) ones)
@@ -538,11 +535,15 @@ splitTip nid n i bucket
538-- k nodes in them. Which subtrees I mean is illustrated in Fig 1. of Kademlia 535-- k nodes in them. Which subtrees I mean is illustrated in Fig 1. of Kademlia
539-- paper. The rule requiring additional splits is in section 2.4. 536-- paper. The rule requiring additional splits is in section 2.4.
540modifyBucket 537modifyBucket
541 :: forall ip xs. (Eq ip) => 538 :: forall xs dht ip u.
542 NodeId -> (Bucket ip -> Maybe (xs, Bucket ip)) -> Table ip -> Maybe (xs,Table ip) 539 ( Eq ip
540 , Ord (NodeId dht)
541 , FiniteBits (NodeId dht)
542 ) =>
543 NodeId dht -> (Bucket dht ip u -> Maybe (xs, Bucket dht ip u)) -> Table dht ip u -> Maybe (xs,Table dht ip u)
543modifyBucket nodeId f = go (0 :: BitIx) 544modifyBucket nodeId f = go (0 :: BitIx)
544 where 545 where
545 go :: BitIx -> Table ip -> Maybe (xs, Table ip) 546 go :: BitIx -> Table dht ip u -> Maybe (xs, Table dht ip u)
546 go !i (Zero table bucket) 547 go !i (Zero table bucket)
547 | testIdBit nodeId i = second (Zero table) <$> f bucket 548 | testIdBit nodeId i = second (Zero table) <$> f bucket
548 | otherwise = second (`Zero` bucket) <$> go (succ i) table 549 | otherwise = second (`Zero` bucket) <$> go (succ i) table
@@ -555,23 +556,36 @@ modifyBucket nodeId f = go (0 :: BitIx)
555 <|> go i (splitTip nid n i bucket) 556 <|> go i (splitTip nid n i bucket)
556 557
557-- | Triggering event for atomic table update 558-- | Triggering event for atomic table update
558data Event ip = TryInsert { foreignNode :: NodeInfo KMessageOf ip () } 559data Event dht ip u = TryInsert { foreignNode :: NodeInfo dht ip u }
559 | PingResult { foreignNode :: NodeInfo KMessageOf ip () 560 | PingResult { foreignNode :: NodeInfo dht ip u
560 , ponged :: Bool 561 , ponged :: Bool
561 } 562 }
562 deriving (Eq,Show) -- Ord 563deriving instance Eq (NodeId dht) => Eq (Event dht ip u)
563 564deriving instance ( Show ip
564eventId :: Event ip -> NodeId 565 , Show (NodeId dht)
566 , Show u
567 ) => Show (Event dht ip u)
568
569eventId :: Event dht ip u -> NodeId dht
565eventId (TryInsert NodeInfo{..}) = nodeId 570eventId (TryInsert NodeInfo{..}) = nodeId
566eventId (PingResult NodeInfo{..} _) = nodeId 571eventId (PingResult NodeInfo{..} _) = nodeId
567 572
568-- | Actions requested by atomic table update 573-- | Actions requested by atomic table update
569data CheckPing ip = CheckPing [NodeInfo KMessageOf ip ()] 574data CheckPing dht ip u = CheckPing [NodeInfo dht ip u]
570 deriving (Eq,Show) -- Ord 575
576deriving instance Eq (NodeId dht) => Eq (CheckPing dht ip u)
577deriving instance ( Show ip
578 , Show (NodeId dht)
579 , Show u
580 ) => Show (CheckPing dht ip u)
571 581
572 582
573-- | Atomic 'Table' update 583-- | Atomic 'Table' update
574insert :: (Eq ip, Applicative m) => Timestamp -> Event ip -> Table ip -> m ([CheckPing ip], Table ip) 584insert :: ( Eq ip
585 , Applicative m
586 , Ord (NodeId dht)
587 , FiniteBits (NodeId dht)
588 ) => Timestamp -> Event dht ip u -> Table dht ip u -> m ([CheckPing dht ip u], Table dht ip u)
575insert tm event tbl = pure $ fromMaybe ([],tbl) $ modifyBucket (eventId event) (insertBucket tm event) tbl 589insert tm event tbl = pure $ fromMaybe ([],tbl) $ modifyBucket (eventId event) (insertBucket tm event) tbl
576 590
577 591
@@ -579,16 +593,16 @@ insert tm event tbl = pure $ fromMaybe ([],tbl) $ modifyBucket (eventId event) (
579-- Conversion 593-- Conversion
580-----------------------------------------------------------------------} 594-----------------------------------------------------------------------}
581 595
582type TableEntry ip = (NodeInfo KMessageOf ip (), Timestamp) 596type TableEntry dht ip u = (NodeInfo dht ip u, Timestamp)
583 597
584tableEntry :: NodeEntry ip -> TableEntry ip 598tableEntry :: NodeEntry dht ip u -> TableEntry dht ip u
585tableEntry (a :-> b) = (a, b) 599tableEntry (a :-> b) = (a, b)
586 600
587-- | Non-empty list of buckets. 601-- | Non-empty list of buckets.
588toBucketList :: Table ip -> [Bucket ip] 602toBucketList :: Table dht ip u -> [Bucket dht ip u]
589toBucketList (Tip _ _ b) = [b] 603toBucketList (Tip _ _ b) = [b]
590toBucketList (Zero t b) = b : toBucketList t 604toBucketList (Zero t b) = b : toBucketList t
591toBucketList (One b t) = b : toBucketList t 605toBucketList (One b t) = b : toBucketList t
592 606
593toList :: Eq ip => Table ip -> [[TableEntry ip]] 607toList :: Eq ip => Table dht ip u -> [[TableEntry dht ip u]]
594toList = L.map (L.map tableEntry . PSQ.toList . bktNodes) . toBucketList 608toList = L.map (L.map tableEntry . PSQ.toList . bktNodes) . toBucketList