| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
LibP2P.Protocol.Identify
Description
Identify protocol implementation (specs/identify).
Protocol ID: ipfsid/1.0.0
After a connection is established, both sides exchange IdentifyInfo messages to learn about each other's capabilities, listen addresses, and agent version. Like all libp2p protobuf streams, the message is varint-length-delimited on the wire: uvarint(len) ++ protobuf. This matches the delimited reader/writer used by go-libp2p (pbio), rust-libp2p, and js-libp2p.
Also implements Identify Push (ipfsidpush1.0.0) for proactive updates when local state changes.
Synopsis
- identifyProtocolId :: ProtocolId
- identifyPushProtocolId :: ProtocolId
- handleIdentify :: Switch -> Connection -> StreamIO -> IO ()
- requestIdentify :: Connection -> IO (Either String IdentifyInfo)
- handleIdentifyPush :: Switch -> Connection -> StreamIO -> IO ()
- pushIdentify :: Switch -> IO ()
- mergeIdentify :: IdentifyInfo -> IdentifyInfo -> IdentifyInfo
- identifyPeer :: Switch -> Connection -> IO (Either String ())
- identifyTimeoutMicros :: Int
- buildLocalIdentify :: Switch -> Maybe Connection -> IO IdentifyInfo
- registerIdentifyHandlers :: Switch -> IO ()
- encodeFramedIdentify :: IdentifyInfo -> ByteString
- readFramedIdentify :: StreamIO -> Int -> IO (Either String IdentifyInfo)
Protocol IDs
identifyProtocolId :: ProtocolId Source #
Identify protocol ID.
identifyPushProtocolId :: ProtocolId Source #
Identify Push protocol ID.
Protocol logic
handleIdentify :: Switch -> Connection -> StreamIO -> IO () Source #
Handle an inbound Identify request (responder side).
Sends our local IdentifyInfo as a varint-length-prefixed protobuf, then closes the stream (per specs/identify: respond and close). The connection provides the remote address used for observedAddr.
requestIdentify :: Connection -> IO (Either String IdentifyInfo) Source #
Request Identify from a remote peer (initiator side).
Opens a new stream, negotiates ipfsid/1.0.0, then reads one
varint-length-prefixed protobuf message. The publicKey field is
validated against the connection's authenticated peer id (see
validatePublicKey).
Identify is a one-shot exchange, so the stream is closed on every exit
path — success, negotiation refusal, decode failure, timeout and
exception alike. A caller that runs this per connection would
otherwise leak a half-open stream per connection. The whole exchange
is bounded by identifyTimeoutMicros: a peer that negotiates and then
never answers must not pin a stream and a thread forever.
handleIdentifyPush :: Switch -> Connection -> StreamIO -> IO () Source #
Handle an inbound Identify Push (responder side).
Reads the pushed varint-length-prefixed IdentifyInfo from the remote peer. The length prefix is the message boundary — identify push has no stream-close boundary to fall back on. The local stream side is still closed on every exit path so a one-shot push cannot leak a half-open Yamux stream (go-libp2p's handleIdentifyResponse does the same with defer s.Close()).
The pushed info is merged into the existing peer entry via
mergeIdentify: pushes may be partial updates, so fields absent
from the message must not erase what we already know.
pushIdentify :: Switch -> IO () Source #
Push our current IdentifyInfo to every connected peer (sender side of ipfsidpush1.0.0).
Per specs/identify: open a stream to each remote peer, negotiate the push protocol id, send one Identify message and close the stream. Call this whenever local state advertised via identify changes (listen addresses, registered protocols). Failures on individual peers (e.g. push protocol not supported) are ignored.
mergeIdentify :: IdentifyInfo -> IdentifyInfo -> IdentifyInfo Source #
Merge a received (possibly partial) Identify update into the previously known info for a peer.
Per specs/identify: "missing fields should be ignored, as peers may choose to send partial updates containing only the fields whose values have changed." Optional fields keep the known value when the update omits them; repeated fields (protobuf cannot distinguish absent from empty) keep the known list when the update's is empty and are replaced wholesale otherwise, matching go-libp2p.
Identify on connect
identifyPeer :: Switch -> Connection -> IO (Either String ()) Source #
Run Identify against a freshly established connection and record the result in the peer store (specs/identify).
This is what makes a peer's advertised addresses and protocols known
to us: without it swPeerStore only ever fills from an inbound push,
so nothing is known about a peer we dialled or accepted. go-libp2p
drives its IDService from the swarm's Connected notification for the
same reason.
Failure is returned rather than thrown: the connection stays usable, and no peer store entry is created. There is no retry — a peer that does not answer Identify now will be picked up by a later push, if it sends one.
identifyTimeoutMicros :: Int Source #
Timeout for one Identify exchange: 5 seconds.
Matches go-libp2p's identify.DefaultTimeout, which it applies to all
id interactions in both directions.
Building local info
buildLocalIdentify :: Switch -> Maybe Connection -> IO IdentifyInfo Source #
Build our local IdentifyInfo from Switch state, including a signed peer record (RFC 0003) over our listen addresses, sealed with the identity key.
Registration
registerIdentifyHandlers :: Switch -> IO () Source #
Register Identify protocol handlers on the Switch.
Registers: ipfsid/1.0.0 — respond to Identify requests ipfsidpush1.0.0 — handle Identify Push from remote
Wire framing
encodeFramedIdentify :: IdentifyInfo -> ByteString Source #
Encode an IdentifyInfo with its uvarint length prefix, as written on the wire: uvarint(len) ++ protobuf.
readFramedIdentify :: StreamIO -> Int -> IO (Either String IdentifyInfo) Source #
Read one varint-length-prefixed Identify message from a stream.
Reads the uvarint length prefix, then exactly that many payload bytes, and decodes the protobuf. Rejects messages larger than maxSize before reading the payload.