| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
LibP2P.DHT
Description
DHT node state, RPC handler, and record/provider stores.
The DHTNode is the top-level coordinator for Kademlia DHT operations. It owns the routing table, record store, provider store, and handles both inbound (as handler) and outbound (dhtSendRequest) RPC.
The outbound sender is wired to the Switch by newDHTNode; it remains
a record field so tests can inject mocks without a real network.
Synopsis
- data DHTNode = DHTNode {
- dhtSwitch :: !Switch
- dhtRoutingTable :: !(TVar RoutingTable)
- dhtRecordStore :: !(TVar (Map ByteString DHTRecord))
- dhtProviderStore :: !(TVar (Map ByteString [ProviderEntry]))
- dhtLocalKey :: !DHTKey
- dhtLocalPeerId :: !PeerId
- dhtMode :: !DHTMode
- dhtValidator :: !Validator
- dhtStreams :: !(TVar (Map PeerId PeerSession))
- dhtSendRequest :: !(PeerId -> DHTMessage -> IO (Either String DHTMessage))
- dhtDisconnectHook :: !(IORef (Maybe (Connection -> IO ())))
- dhtQueryTimeout :: !Int
- dhtBootstrapWorker :: !(TVar (Maybe (Async ())))
- data DHTMode
- data PeerSession
- data ProviderEntry = ProviderEntry {
- peProvider :: !PeerId
- peAddrs :: ![Multiaddr]
- peTimestamp :: !UTCTime
- data Validator = Validator {
- valValidate :: ByteString -> ByteString -> Either String ()
- valSelect :: ByteString -> [ByteString] -> Either String Int
- defaultValidator :: Validator
- namespacedValidator :: Map ByteString Validator -> Validator
- pkValidator :: Validator
- newDHTNode :: Switch -> DHTMode -> IO DHTNode
- newPeerSession :: Maybe StreamIO -> IO PeerSession
- stopDHTNode :: DHTNode -> IO ()
- defaultQueryTimeoutMicros :: Int
- registerDHTHandler :: DHTNode -> IO ()
- handleDHTRequest :: DHTNode -> StreamIO -> PeerId -> IO ()
- addPeerToTable :: DHTNode -> BucketEntry -> IO InsertResult
- storeRecord :: DHTNode -> DHTRecord -> IO ()
- lookupRecord :: DHTNode -> ByteString -> IO (Maybe DHTRecord)
- addProvider :: DHTNode -> ByteString -> ProviderEntry -> IO ()
- getProviders :: DHTNode -> ByteString -> IO [ProviderEntry]
- decodePeerAddrs :: [ByteString] -> [Multiaddr]
- dhtProtocolId :: Text
- providerRecordTTL :: NominalDiffTime
Types
Top-level DHT node state.
Constructors
| DHTNode | |
Fields
| |
Server or client mode.
data PeerSession Source #
A cached outbound ipfskad/1.0.0 stream together with its
exchange lock, held as a single MVar that is both.
Kademlia RPC messages carry no request identifier, so two exchanges
interleaved on one stream cannot be reassociated afterwards: a caller
reads whichever response arrives next, not necessarily its own. The
complete write + read exchange therefore has to be serialized per
peer. go-libp2p pairs its per-peer cached stream with exactly this
kind of exchange-wide lock (peerMessageSender.lk).
Making the MVar hold the stream slot rather than guard a separate
one means replacing a dead stream is, by construction, something only
the caller currently holding the exchange can do.
psInvalid is set when the peer's last connection closes so a caller
that still holds the slot cannot put a live stream back into a map
entry that has already been removed (go-libp2p's invalidate()).
data ProviderEntry Source #
A provider record for content routing.
Constructors
| ProviderEntry | |
Fields
| |
Instances
| Show ProviderEntry Source # | |
Defined in LibP2P.DHT Methods showsPrec :: Int -> ProviderEntry -> ShowS # show :: ProviderEntry -> String # showList :: [ProviderEntry] -> ShowS # | |
| Eq ProviderEntry Source # | |
Defined in LibP2P.DHT Methods (==) :: ProviderEntry -> ProviderEntry -> Bool # (/=) :: ProviderEntry -> ProviderEntry -> Bool # | |
Validator interface for record validation.
valValidate checks that a value is well-formed for its key and
returns an error for records that must not be stored or served.
valSelect picks the index of the best value among conflicting
candidates for the same key (used for GET_VALUE conflict resolution).
Constructors
| Validator | |
Fields
| |
Validators
defaultValidator :: Validator Source #
The default validator set: pk records only. Additional
namespaces can be registered by building a custom
namespacedValidator and storing it in the DHT node.
namespacedValidator :: Map ByteString Validator -> Validator Source #
Dispatch validation by key namespace. Keys without a registered namespace are rejected, matching go-libp2p's namespaced validator ("invalid record keytype").
pkValidator :: Validator Source #
Validator for the pk namespace: the value must be a serialized
PublicKey protobuf whose derived Peer ID equals the multihash in the
key path. Public keys never conflict, so valSelect keeps the first
candidate (go-libp2p's record.PublicKeyValidator does the same).
Construction
newDHTNode :: Switch -> DHTMode -> IO DHTNode Source #
Create a new DHT node with the outbound sender wired to the Switch.
Registers a disconnect notifier so a cached session is dropped when the peer's last connection closes (#279).
newPeerSession :: Maybe StreamIO -> IO PeerSession Source #
Create an empty or pre-loaded peer session (tests inject the latter).
stopDHTNode :: DHTNode -> IO () Source #
Stop the DHT node: drop cached sessions and deregister the disconnect notifier so a stopped node cannot keep a callback alive on the Switch.
defaultQueryTimeoutMicros :: Int Source #
Default querybootstrap timeout: 10 seconds (specskad-dht).
Handler registration
registerDHTHandler :: DHTNode -> IO () Source #
Register the DHT handler on the Switch.
Per specs/kad-dht (client and server mode), nodes operating in client
mode do not offer the Kademlia protocol identifier for incoming
streams, so this is a no-op for DHTClient nodes: they keep issuing
outbound queries via dhtSendRequest but never serve inbound RPC.
Inbound RPC handler
handleDHTRequest :: DHTNode -> StreamIO -> PeerId -> IO () Source #
Handle an inbound DHT stream.
Per specs/kad-dht, implementations must handle additional RPC request messages on the same incoming stream: go-libp2p keeps one long-lived stream per peer and pipelines requests over it. Loop until the stream errors, is reset, or reaches EOF.
Routing table maintenance
addPeerToTable :: DHTNode -> BucketEntry -> IO InsertResult Source #
Insert a peer into the routing table, applying the Kademlia full-bucket eviction policy.
When the target bucket is full, the least-recently-seen peer is
probed with a FIND_NODE request (the DHT liveness check; our
MessageType has no PING, and go-libp2p likewise treats any
successful RPC as proof of liveness):
- if the LRS peer answers, it is kept (refreshed to
most-recently-seen) and the new peer is dropped (
BucketFull); - if it does not answer, it is evicted and the new peer takes its
place (
Inserted).
Store operations
lookupRecord :: DHTNode -> ByteString -> IO (Maybe DHTRecord) Source #
Look up a record by key.
addProvider :: DHTNode -> ByteString -> ProviderEntry -> IO () Source #
Add a provider entry for a content key.
A provider republishing on schedule replaces its previous entry (deduplicated by peer ID) instead of appending a duplicate, so the entry's timestamp is refreshed and GET_PROVIDERS responses stay bounded.
getProviders :: DHTNode -> ByteString -> IO [ProviderEntry] Source #
Get providers for a content key, pruning entries older than
providerRecordTTL (48h expiration interval per specs/kad-dht).
Wire helpers
decodePeerAddrs :: [ByteString] -> [Multiaddr] Source #
Decode raw wire multiaddrs from a Peer record, dropping any that fail to parse: a malformed address from a remote peer must not poison the rest of the record.
Constants
dhtProtocolId :: Text Source #
DHT protocol identifier for multistream-select.
providerRecordTTL :: NominalDiffTime Source #
Provider record expiration interval, per specs/kad-dht (48 hours).
Expired entries are pruned on read in getProviders.