diff options
-rw-r--r-- | Kademlia.hs | 6 | ||||
-rw-r--r-- | src/Network/DHT/Routing.hs | 69 |
2 files changed, 41 insertions, 34 deletions
diff --git a/Kademlia.hs b/Kademlia.hs index 7bffe4c1..63c2b494 100644 --- a/Kademlia.hs +++ b/Kademlia.hs | |||
@@ -114,10 +114,10 @@ contramapIR f ir = InsertionReporter | |||
114 | -- | All the IO operations neccessary to maintain a Kademlia routing table. | 114 | -- | All the IO operations neccessary to maintain a Kademlia routing table. |
115 | data TableStateIO nid ni = TableStateIO | 115 | data TableStateIO nid ni = TableStateIO |
116 | { -- | Write the routing table. Typically 'writeTVar'. | 116 | { -- | Write the routing table. Typically 'writeTVar'. |
117 | tblWrite :: R.Table ni nid -> STM () | 117 | tblWrite :: R.Table ni -> STM () |
118 | 118 | ||
119 | -- | Read the routing table. Typically 'readTVar'. | 119 | -- | Read the routing table. Typically 'readTVar'. |
120 | , tblRead :: STM (R.Table ni nid) | 120 | , tblRead :: STM (R.Table ni) |
121 | 121 | ||
122 | -- | Issue a ping to a remote node and report 'True' if the node | 122 | -- | Issue a ping to a remote node and report 'True' if the node |
123 | -- responded within an acceptable time and 'False' otherwise. | 123 | -- responded within an acceptable time and 'False' otherwise. |
@@ -136,7 +136,7 @@ data TableStateIO nid ni = TableStateIO | |||
136 | , tblChanged :: RoutingTableChanged ni -> STM (IO ()) | 136 | , tblChanged :: RoutingTableChanged ni -> STM (IO ()) |
137 | } | 137 | } |
138 | 138 | ||
139 | vanillaIO :: TVar (Table ni nid) -> (ni -> IO Bool) -> TableStateIO nid ni | 139 | vanillaIO :: TVar (Table ni) -> (ni -> IO Bool) -> TableStateIO nid ni |
140 | vanillaIO var ping = TableStateIO | 140 | vanillaIO var ping = TableStateIO |
141 | { tblRead = readTVar var | 141 | { tblRead = readTVar var |
142 | , tblWrite = writeTVar var | 142 | , tblWrite = writeTVar var |
diff --git a/src/Network/DHT/Routing.hs b/src/Network/DHT/Routing.hs index e2bc5aa9..0004ac09 100644 --- a/src/Network/DHT/Routing.hs +++ b/src/Network/DHT/Routing.hs | |||
@@ -19,6 +19,7 @@ | |||
19 | {-# LANGUAGE ViewPatterns #-} | 19 | {-# LANGUAGE ViewPatterns #-} |
20 | {-# LANGUAGE TypeOperators #-} | 20 | {-# LANGUAGE TypeOperators #-} |
21 | {-# LANGUAGE DeriveGeneric #-} | 21 | {-# LANGUAGE DeriveGeneric #-} |
22 | {-# LANGUAGE DeriveFunctor #-} | ||
22 | {-# LANGUAGE ScopedTypeVariables #-} | 23 | {-# LANGUAGE ScopedTypeVariables #-} |
23 | {-# LANGUAGE StandaloneDeriving, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances #-} | 24 | {-# LANGUAGE StandaloneDeriving, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances #-} |
24 | {-# OPTIONS_GHC -fno-warn-orphans #-} | 25 | {-# OPTIONS_GHC -fno-warn-orphans #-} |
@@ -175,10 +176,13 @@ bucketQ = seqQ | |||
175 | data Bucket ni = Bucket | 176 | data Bucket ni = Bucket |
176 | { bktNodes :: !(PSQ ni Timestamp) -- current routing nodes | 177 | { bktNodes :: !(PSQ ni Timestamp) -- current routing nodes |
177 | , bktQ :: !(BucketQueue (Timestamp,ni)) -- replacements pending time-outs | 178 | , bktQ :: !(BucketQueue (Timestamp,ni)) -- replacements pending time-outs |
178 | } deriving Generic | 179 | } deriving (Generic) |
179 | 180 | ||
180 | deriving instance Show ni => Show (Bucket ni) | 181 | deriving instance Show ni => Show (Bucket ni) |
181 | 182 | ||
183 | mapBucket :: Ord ni => (a -> ni) -> Bucket a -> Bucket ni | ||
184 | mapBucket f (Bucket ns q) = Bucket (PSQ.fromList $ map (\(ni :-> tm) -> (f ni :-> tm)) $ PSQ.toList ns) | ||
185 | (fmap (second f) q) | ||
182 | 186 | ||
183 | 187 | ||
184 | #if 0 | 188 | #if 0 |
@@ -335,7 +339,7 @@ defaultBucketCount :: Int | |||
335 | defaultBucketCount = 20 | 339 | defaultBucketCount = 20 |
336 | 340 | ||
337 | data Info ni nid = Info | 341 | data Info ni nid = Info |
338 | { myBuckets :: Table ni nid | 342 | { myBuckets :: Table ni |
339 | , myNodeId :: nid | 343 | , myNodeId :: nid |
340 | , myAddress :: SockAddr | 344 | , myAddress :: SockAddr |
341 | } | 345 | } |
@@ -362,18 +366,22 @@ deriving instance (Show ni, Show nid) => Show (Info ni nid) | |||
362 | -- is always split into two new buckets covering the ranges @0..2 ^ | 366 | -- is always split into two new buckets covering the ranges @0..2 ^ |
363 | -- 159@ and @2 ^ 159..2 ^ 160@. | 367 | -- 159@ and @2 ^ 159..2 ^ 160@. |
364 | -- | 368 | -- |
365 | data Table ni nid | 369 | data Table ni |
366 | -- most nearest bucket | 370 | -- most nearest bucket |
367 | = Tip nid Int (Bucket ni) | 371 | = Tip ni Int (Bucket ni) |
368 | 372 | ||
369 | -- left biased tree branch | 373 | -- left biased tree branch |
370 | | Zero (Table ni nid) (Bucket ni) | 374 | | Zero (Table ni) (Bucket ni) |
371 | 375 | ||
372 | -- right biased tree branch | 376 | -- right biased tree branch |
373 | | One (Bucket ni) (Table ni nid) | 377 | | One (Bucket ni) (Table ni) |
374 | deriving Generic | 378 | deriving Generic |
375 | 379 | ||
376 | instance (Eq ni, Eq nid) => Eq (Table ni nid) where | 380 | mapTable f (One b t) = One (mapBucket f b) (mapTable f t) |
381 | mapTable f (Zero t b) = Zero (mapTable f t) (mapBucket f b) | ||
382 | mapTable f (Tip ni n b) = Tip (f ni) n (mapBucket f b) | ||
383 | |||
384 | instance (Eq ni) => Eq (Table ni) where | ||
377 | (==) = (==) `on` Network.DHT.Routing.toList | 385 | (==) = (==) `on` Network.DHT.Routing.toList |
378 | 386 | ||
379 | #if 0 | 387 | #if 0 |
@@ -384,7 +392,7 @@ instance Serialize NominalDiffTime where | |||
384 | 392 | ||
385 | #endif | 393 | #endif |
386 | 394 | ||
387 | deriving instance (Show ni, Show nid) => Show (Table ni nid) | 395 | deriving instance (Show ni) => Show (Table ni) |
388 | 396 | ||
389 | #if 0 | 397 | #if 0 |
390 | 398 | ||
@@ -396,7 +404,7 @@ instance (Eq ip, Serialize ip, Ord (NodeId), Serialize (NodeId), Serialize u) => | |||
396 | #endif | 404 | #endif |
397 | 405 | ||
398 | -- | Shape of the table. | 406 | -- | Shape of the table. |
399 | instance Pretty (Table ni nid) where | 407 | instance Pretty (Table ni) where |
400 | pPrint t | 408 | pPrint t |
401 | | bucketCount < 6 = hcat $ punctuate ", " $ L.map PP.int ss | 409 | | bucketCount < 6 = hcat $ punctuate ", " $ L.map PP.int ss |
402 | | otherwise = brackets $ | 410 | | otherwise = brackets $ |
@@ -407,8 +415,8 @@ instance Pretty (Table ni nid) where | |||
407 | ss = shape t | 415 | ss = shape t |
408 | 416 | ||
409 | -- | Empty table with specified /spine/ node id. | 417 | -- | Empty table with specified /spine/ node id. |
410 | nullTable :: nid -> Int -> Table ni nid | 418 | nullTable :: ni -> Int -> Table ni |
411 | nullTable nid n = Tip nid (bucketCount (pred n)) (Bucket PSQ.empty (runIdentity $ emptyQueue bucketQ)) | 419 | nullTable ni n = Tip ni (bucketCount (pred n)) (Bucket PSQ.empty (runIdentity $ emptyQueue bucketQ)) |
412 | where | 420 | where |
413 | bucketCount x = max 0 (min 159 x) | 421 | bucketCount x = max 0 (min 159 x) |
414 | 422 | ||
@@ -440,7 +448,7 @@ type NodeCount = Int | |||
440 | 448 | ||
441 | -- | Internally, routing table is similar to list of buckets or a | 449 | -- | Internally, routing table is similar to list of buckets or a |
442 | -- /matrix/ of nodes. This function returns the shape of the matrix. | 450 | -- /matrix/ of nodes. This function returns the shape of the matrix. |
443 | shape :: Table ni nid -> [Int] | 451 | shape :: Table ni -> [Int] |
444 | shape = map (PSQ.size . bktNodes) . toBucketList | 452 | shape = map (PSQ.size . bktNodes) . toBucketList |
445 | 453 | ||
446 | #if 0 | 454 | #if 0 |
@@ -457,7 +465,7 @@ depth = L.length . shape | |||
457 | 465 | ||
458 | lookupBucket :: ( FiniteBits nid | 466 | lookupBucket :: ( FiniteBits nid |
459 | , Ord nid | 467 | , Ord nid |
460 | ) => nid -> Table ni nid -> [Bucket ni] | 468 | ) => nid -> Table ni -> [Bucket ni] |
461 | lookupBucket nid = go 0 [] | 469 | lookupBucket nid = go 0 [] |
462 | where | 470 | where |
463 | go i bs (Zero table bucket) | 471 | go i bs (Zero table bucket) |
@@ -471,7 +479,7 @@ lookupBucket nid = go 0 [] | |||
471 | 479 | ||
472 | compatibleNodeId :: forall ni nid. | 480 | compatibleNodeId :: forall ni nid. |
473 | ( Serialize nid, FiniteBits nid) => | 481 | ( Serialize nid, FiniteBits nid) => |
474 | Table ni nid -> IO nid | 482 | Table ni -> IO nid |
475 | compatibleNodeId tbl = genBucketSample prefix br | 483 | compatibleNodeId tbl = genBucketSample prefix br |
476 | where | 484 | where |
477 | br = bucketRange (L.length (shape tbl) - 1) True | 485 | br = bucketRange (L.length (shape tbl) - 1) True |
@@ -479,7 +487,7 @@ compatibleNodeId tbl = genBucketSample prefix br | |||
479 | bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0 | 487 | bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0 |
480 | prefix = either error id $ S.decode bs | 488 | prefix = either error id $ S.decode bs |
481 | 489 | ||
482 | tablePrefix :: Table ni nid -> [Word8] | 490 | tablePrefix :: Table ni -> [Word8] |
483 | tablePrefix = map (packByte . take 8 . (++repeat False)) | 491 | tablePrefix = map (packByte . take 8 . (++repeat False)) |
484 | . chunksOf 8 | 492 | . chunksOf 8 |
485 | . tableBits | 493 | . tableBits |
@@ -488,7 +496,7 @@ tablePrefix = map (packByte . take 8 . (++repeat False)) | |||
488 | bitmask ix True = bit ix | 496 | bitmask ix True = bit ix |
489 | bitmask _ _ = 0 | 497 | bitmask _ _ = 0 |
490 | 498 | ||
491 | tableBits :: Table ni nid -> [Bool] | 499 | tableBits :: Table ni -> [Bool] |
492 | tableBits (One _ tbl) = True : tableBits tbl | 500 | tableBits (One _ tbl) = True : tableBits tbl |
493 | tableBits (Zero tbl _) = False : tableBits tbl | 501 | tableBits (Zero tbl _) = False : tableBits tbl |
494 | tableBits (Tip _ _ _) = [] | 502 | tableBits (Tip _ _ _) = [] |
@@ -540,7 +548,7 @@ rank f nid = L.sortBy (comparing (distance nid . f)) | |||
540 | -- 'find_node' and 'get_peers' queries. | 548 | -- 'find_node' and 'get_peers' queries. |
541 | kclosest :: ( FiniteBits nid | 549 | kclosest :: ( FiniteBits nid |
542 | , Ord nid | 550 | , Ord nid |
543 | ) => (ni -> nid) -> Int -> nid -> Table ni nid -> [ni] | 551 | ) => (ni -> nid) -> Int -> nid -> Table ni -> [ni] |
544 | kclosest nodeId k nid tbl = take k $ rank nodeId nid (L.concat bucket) | 552 | kclosest nodeId k nid tbl = take k $ rank nodeId nid (L.concat bucket) |
545 | ++ rank nodeId nid (L.concat everyone) | 553 | ++ rank nodeId nid (L.concat everyone) |
546 | where | 554 | where |
@@ -558,12 +566,11 @@ kclosest nodeId k nid tbl = take k $ rank nodeId nid (L.concat bucket) | |||
558 | 566 | ||
559 | splitTip :: -- ( Eq ip , Ord (NodeId) , FiniteBits (NodeId)) => | 567 | splitTip :: -- ( Eq ip , Ord (NodeId) , FiniteBits (NodeId)) => |
560 | Ord ni => | 568 | Ord ni => |
561 | (nid -> Word -> Bool) | 569 | (ni -> Word -> Bool) |
562 | -> (ni -> Word -> Bool) | 570 | -> ni -> Int -> BitIx -> Bucket ni -> Table ni |
563 | -> nid -> Int -> BitIx -> Bucket ni -> Table ni nid | 571 | splitTip testNodeBit ni n i bucket |
564 | splitTip testIdBit testNodeBit nid n i bucket | 572 | | testNodeBit ni i = (One zeros (Tip ni (pred n) ones)) |
565 | | testIdBit nid i = (One zeros (Tip nid (pred n) ones)) | 573 | | otherwise = (Zero (Tip ni (pred n) zeros) ones) |
566 | | otherwise = (Zero (Tip nid (pred n) zeros) ones) | ||
567 | where | 574 | where |
568 | (ones, zeros) = split testNodeBit i bucket | 575 | (ones, zeros) = split testNodeBit i bucket |
569 | 576 | ||
@@ -577,10 +584,10 @@ modifyBucket | |||
577 | forall ni nid xs. Ord ni => | 584 | forall ni nid xs. Ord ni => |
578 | (nid -> Word -> Bool) | 585 | (nid -> Word -> Bool) |
579 | -> (ni -> Word -> Bool) | 586 | -> (ni -> Word -> Bool) |
580 | -> nid -> (Bucket ni -> Maybe (xs, Bucket ni)) -> Table ni nid -> Maybe (xs,Table ni nid) | 587 | -> nid -> (Bucket ni -> Maybe (xs, Bucket ni)) -> Table ni -> Maybe (xs,Table ni) |
581 | modifyBucket testIdBit testNodeBit nodeId f = go (0 :: BitIx) | 588 | modifyBucket testIdBit testNodeBit nodeId f = go (0 :: BitIx) |
582 | where | 589 | where |
583 | go :: BitIx -> Table ni nid -> Maybe (xs, Table ni nid) | 590 | go :: BitIx -> Table ni -> Maybe (xs, Table ni) |
584 | go !i (Zero table bucket) | 591 | go !i (Zero table bucket) |
585 | | testIdBit nodeId i = second (Zero table) <$> f bucket | 592 | | testIdBit nodeId i = second (Zero table) <$> f bucket |
586 | | otherwise = second (`Zero` bucket) <$> go (succ i) table | 593 | | otherwise = second (`Zero` bucket) <$> go (succ i) table |
@@ -590,7 +597,7 @@ modifyBucket testIdBit testNodeBit nodeId f = go (0 :: BitIx) | |||
590 | go !i (Tip nid n bucket) | 597 | go !i (Tip nid n bucket) |
591 | | n == 0 = second (Tip nid n) <$> f bucket | 598 | | n == 0 = second (Tip nid n) <$> f bucket |
592 | | otherwise = second (Tip nid n) <$> f bucket | 599 | | otherwise = second (Tip nid n) <$> f bucket |
593 | <|> go i (splitTip testIdBit testNodeBit nid n i bucket) | 600 | <|> go i (splitTip testNodeBit nid n i bucket) |
594 | 601 | ||
595 | 602 | ||
596 | -- | Triggering event for atomic table update | 603 | -- | Triggering event for atomic table update |
@@ -639,7 +646,7 @@ deriving instance ( Show ip | |||
639 | -- | 646 | -- |
640 | updateForInbound :: Ord ni => | 647 | updateForInbound :: Ord ni => |
641 | KademliaSpace nid ni | 648 | KademliaSpace nid ni |
642 | -> Timestamp -> ni -> Table ni nid -> (Bool, [ni], Table ni nid) | 649 | -> Timestamp -> ni -> Table ni -> (Bool, [ni], Table ni) |
643 | updateForInbound space tm ni tbl = | 650 | updateForInbound space tm ni tbl = |
644 | maybe (False, [],tbl) (\(ps,tbl') -> (True, ps, tbl')) | 651 | maybe (False, [],tbl) (\(ps,tbl') -> (True, ps, tbl')) |
645 | $ modifyBucket (kademliaTestBit space) | 652 | $ modifyBucket (kademliaTestBit space) |
@@ -656,8 +663,8 @@ updateForPingResult :: Ord ni => | |||
656 | KademliaSpace nid ni | 663 | KademliaSpace nid ni |
657 | -> ni -- ^ The pinged node. | 664 | -> ni -- ^ The pinged node. |
658 | -> Bool -- ^ True if we got a reply, False if it timed out. | 665 | -> Bool -- ^ True if we got a reply, False if it timed out. |
659 | -> Table ni nid -- ^ The routing table. | 666 | -> Table ni -- ^ The routing table. |
660 | -> ( [(ni,(Timestamp, ni))], Table ni nid ) | 667 | -> ( [(ni,(Timestamp, ni))], Table ni ) |
661 | updateForPingResult space ni got_reply tbl = | 668 | updateForPingResult space ni got_reply tbl = |
662 | fromMaybe ([],tbl) | 669 | fromMaybe ([],tbl) |
663 | $ modifyBucket (kademliaTestBit space) | 670 | $ modifyBucket (kademliaTestBit space) |
@@ -677,12 +684,12 @@ tableEntry :: NodeEntry ni -> TableEntry ni | |||
677 | tableEntry (a :-> b) = (a, b) | 684 | tableEntry (a :-> b) = (a, b) |
678 | 685 | ||
679 | -- | Non-empty list of buckets. | 686 | -- | Non-empty list of buckets. |
680 | toBucketList :: Table ni nid -> [Bucket ni] | 687 | toBucketList :: Table ni -> [Bucket ni] |
681 | toBucketList (Tip _ _ b) = [b] | 688 | toBucketList (Tip _ _ b) = [b] |
682 | toBucketList (Zero t b) = b : toBucketList t | 689 | toBucketList (Zero t b) = b : toBucketList t |
683 | toBucketList (One b t) = b : toBucketList t | 690 | toBucketList (One b t) = b : toBucketList t |
684 | 691 | ||
685 | toList :: Table ni nid -> [[TableEntry ni]] | 692 | toList :: Table ni -> [[TableEntry ni]] |
686 | toList = L.map (L.map tableEntry . PSQ.toList . bktNodes) . toBucketList | 693 | toList = L.map (L.map tableEntry . PSQ.toList . bktNodes) . toBucketList |
687 | 694 | ||
688 | data KademliaSpace nid ni = KademliaSpace | 695 | data KademliaSpace nid ni = KademliaSpace |