libp2p-hs-0.1.0.0: Haskell implementation of the libp2p networking stack
Safe HaskellNone
LanguageGHC2021

LibP2P

Description

libp2p-hs: Haskell implementation of the libp2p networking stack.

This is the public API facade for the library. Import this module for the common types and functions needed to build a libp2p node:

import LibP2P

main :: IO ()
main = do
  (pid, kp) <- generateAndPeerId
  sw <- newSwitch pid kp
  tcp <- newTCPTransport
  addTransport sw tcp
  registerIdentifyHandlers sw
  registerPingHandler sw
  _relayState <- registerNATHandlers sw defaultNATConfig
  addrs <- switchListen sw defaultConnectionGater [fromText "ip4127.0.0.1tcp0"]
  print addrs
  -- ... dial other peers, etc.
  switchClose sw
Synopsis

Identity

data PeerId Source #

A Peer ID is a multihash of the serialized public key.

Instances

Instances details
Show PeerId Source # 
Instance details

Defined in LibP2P.Crypto.PeerId

Eq PeerId Source # 
Instance details

Defined in LibP2P.Crypto.PeerId

Methods

(==) :: PeerId -> PeerId -> Bool #

(/=) :: PeerId -> PeerId -> Bool #

Ord PeerId Source # 
Instance details

Defined in LibP2P.Crypto.PeerId

data KeyPair Source #

A key pair containing both public and private keys.

generateKeyPair :: IO (Either String KeyPair) Source #

Generate a new random Ed25519 key pair. Returns Left on cryptographic failure (should not occur with proper RNG).

fromPublicKey :: PublicKey -> PeerId Source #

Derive a Peer ID from a public key.

peerIdBytes :: PeerId -> ByteString Source #

Get the raw multihash bytes of a Peer ID.

toBase58 :: PeerId -> Text Source #

Encode a Peer ID as base58btc text.

parsePeerId :: Text -> Either String PeerId Source #

Parse a Peer ID from text, accepting both base58btc and CIDv1 (base32lower) formats. CIDv1 format: b prefix + base32lower(0x01 || 0x72 || multihash)

toCIDv1 :: PeerId -> Text Source #

Encode a Peer ID as CIDv1 text (base32lower, no padding). Format: b + base32lower(varint(1) + varint(0x72) + multihash_bytes)

Addressing

newtype Multiaddr Source #

A multiaddr is a list of protocol components.

Constructors

Multiaddr [Protocol] 

Instances

Instances details
Show Multiaddr Source # 
Instance details

Defined in LibP2P.Multiaddr

Eq Multiaddr Source # 
Instance details

Defined in LibP2P.Multiaddr

data Protocol Source #

A single protocol component within a multiaddr.

Constructors

IP4 !Word32

IPv4 address (4 bytes big-endian)

IP6 !ByteString

IPv6 address (16 bytes)

TCP !Word16

TCP port

UDP !Word16

UDP port

P2P !ByteString

Peer ID as multihash bytes

QuicV1

QUIC v1 (no address)

WS

WebSocket (no address)

WSS

WebSocket Secure (no address)

DNS !Text

DNS hostname

DNS4 !Text

DNS4 hostname

DNS6 !Text

DNS6 hostname

DNSAddr !Text

DNSAddr hostname

P2PCircuit

Circuit relay marker (no address)

WebTransport

WebTransport (no address)

NoiseProto

Noise protocol marker (no address)

Instances

Instances details
Show Protocol Source # 
Instance details

Defined in LibP2P.Multiaddr.Protocol

Eq Protocol Source # 
Instance details

Defined in LibP2P.Multiaddr.Protocol

splitP2P :: Multiaddr -> Maybe (Multiaddr, PeerId) Source #

Split off the trailing p2ppeerId component from a multiaddr. Returns the transport address and the peer ID, or Nothing if the multiaddr does not end with a p2p component.

toText :: Multiaddr -> Text Source #

Render a multiaddr as text.

fromText :: Text -> Either String Multiaddr Source #

Parse a multiaddr from its text representation (e.g. "ip4127.0.0.1tcp4001").

Switch (central coordinator)

data Switch Source #

The Switch: central coordinator of the libp2p networking stack.

Manages transports, connection pool, protocol handlers, and events. All mutable state is STM-based for safe concurrent access.

newSwitch :: PeerId -> KeyPair -> IO Switch Source #

Create a new Switch with the given local identity. All internal state is initialized empty.

addTransport :: Switch -> Transport -> IO () Source #

Register a transport with the switch. Appends to the list of transports; order matters for selectTransport.

switchListen :: Switch -> ConnectionGater -> [Multiaddr] -> IO [Multiaddr] Source #

Start listening on the given addresses.

For each address, selects a matching transport, binds a listener, and spawns an accept loop that handles inbound connections. Returns the actual bound addresses (port 0 resolved to actual port). Fails if the switch is already closed.

switchListenAddrs :: Switch -> IO [Multiaddr] Source #

Get the current listen addresses from all active listeners.

switchClose :: Switch -> IO () Source #

Shut down the switch. Cancels all accept loop threads, closes all listeners, tears down all pooled connections, then sets the closed flag.

dial :: Switch -> PeerId -> [Multiaddr] -> IO (Either DialError Connection) Source #

Dial a peer, reusing existing connections or establishing new ones.

Implements the full dial flow: 1. Pool reuse: return existing Open connection if available 2. Backoff check: reject if peer recently failed 3. Deduplication: coalesce concurrent dials to same peer via TMVar 4. Staggered parallel dial with 250ms delay (Happy Eyeballs) 5. First success: upgrade, add to pool, return 6. All fail: record backoff, return error

closeConnection :: Switch -> Connection -> IO () Source #

Tear down a connection: remove it from the pool, release its resource reservation, publish a Disconnected event, and close the muxer session together with the underlying transport.

Idempotent: the state transition to ConnClosed is atomic, so concurrent calls (accept loop exit, explicit close, switchClose) perform the teardown exactly once.

newStream :: Switch -> Connection -> IO (Either ResourceError StreamIO) Source #

Open an outbound stream on a connection, reserving a stream slot against the peer's resource scope. The slot is released when the returned stream is closed (exactly once, even on double close).

data DialError Source #

Errors that can occur during a dial operation.

Constructors

DialBackoff

Peer is in backoff period (recently failed)

DialNoAddresses

No addresses provided for dialing

DialNoTransport !Multiaddr

No registered transport can handle this address

DialAllFailed ![String]

All dial attempts failed

DialUpgradeFailed !String

Connection upgrade pipeline failed

DialSwitchClosed

Switch has been shut down

DialResourceLimit !ResourceError

Resource limit exceeded

DialPeerIdMismatch !PeerId !PeerId

Expected vs actual remote PeerId

Instances

Instances details
Show DialError Source # 
Instance details

Defined in LibP2P.Switch.Types

Eq DialError Source # 
Instance details

Defined in LibP2P.Switch.Types

data ResourceError Source #

Resource limit violation error.

setStreamHandler :: Switch -> ProtocolId -> StreamHandler -> IO () Source #

Register a protocol stream handler. Overwrites any existing handler for the same protocol ID. The changed protocol set is pushed to connected peers via identify push (specs/identify) in the background.

removeStreamHandler :: Switch -> ProtocolId -> IO () Source #

Remove a protocol stream handler. The changed protocol set is pushed to connected peers via identify push (specs/identify) in the background.

data StreamIO Source #

Abstraction for stream I/O to enable testing with in-memory buffers.

Constructors

StreamIO 

Fields

type StreamHandler = Connection -> StreamIO -> IO () Source #

A protocol stream handler.

Receives the connection the stream runs over and the stream I/O. The connection exposes the remote peer identity (connPeerId) and addresses (connRemoteAddr, connLocalAddr), which protocols like Identify (observedAddr) and the NAT stack need.

type ProtocolId = Text Source #

A protocol identifier (e.g. "noise", "yamux/1.0.0").

data Connection Source #

An upgraded (secure + multiplexed) connection to a remote peer.

Constructors

Connection 

Fields

Transport

data Transport Source #

Transport provides dial/listen capabilities for a specific protocol.

Constructors

Transport 

Fields

newTCPTransport :: IO Transport Source #

Create a new TCP transport.

Connection gating

data ConnectionGater Source #

Connection gater: policy-based admission control.

Called at multiple points during connection establishment to allow or deny based on policy (IP blocklist, Peer ID allowlist, etc.).

Constructors

ConnectionGater 

Fields

defaultConnectionGater :: ConnectionGater Source #

Default gater that allows all connections.

Identify protocol

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

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).

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.

Ping protocol

registerPingHandler :: Switch -> IO () Source #

Register the Ping handler on the Switch.

The installed handler shares one PingLimiter, so concurrent inbound ping streams are capped at maxPingStreamsPerPeer per remote peer.

sendPing :: Switch -> Connection -> IO (Either PingError PingResult) Source #

Send a single Ping to a remote peer (initiator side).

Convenience wrapper: opens a ping session, pings once, and closes the stream. For repeated pings to the same peer, use withPingSession to reuse one stream instead of opening one per call.

data PingSession Source #

The single outbound ping stream to a peer, negotiated and ready.

Obtain with openPingSession (or scoped via withPingSession), send pings with ping, and always release with closePingSession. A session whose ping failed (timeout, mismatch, I/O error) closes its stream immediately and rejects further pings.

Concurrent ping calls on one session are serialized on psLock: exactly one write/echo exchange runs on the stream at a time, so concurrent callers queue instead of interleaving their 32-byte payloads on the wire.

openPingSession :: Switch -> Connection -> IO (Either PingError PingSession) Source #

Open a ping stream on the connection and negotiate the protocol.

The stream is opened through the Switch so it is counted against the peer's stream limits; the reservation is released when the session is closed. On any failure the stream (if opened) is closed before returning.

ping :: PingSession -> IO (Either PingError PingResult) Source #

Send one ping on the session with the default timeout (pingTimeoutMicros). The session's stream is reused across calls.

pingWithTimeout :: Int -> PingSession -> IO (Either PingError PingResult) Source #

Send one ping on the session, waiting at most the given number of microseconds for the echo. On failure the session is closed: a stream whose echo timed out or went wrong cannot be reused, because a late echo would corrupt the next ping.

The whole exchange runs under the session lock, so concurrent callers are queued one after another on the single stream. The closed check happens under the lock too: a caller queued behind a failed ping sees the session as closed instead of writing into a poisoned stream.

closePingSession :: PingSession -> IO () Source #

Close the session's stream (signalling EOF to the responder's echo loop) and release its stream reservation. Idempotent.

withPingSession :: Switch -> Connection -> (PingSession -> IO a) -> IO (Either PingError a) Source #

Run an action with a ping session, closing it afterwards even if the action throws. Returns Left if the session could not be opened.

data PingResult Source #

Successful ping result.

Constructors

PingResult 

Fields

Instances

Instances details
Show PingResult Source # 
Instance details

Defined in LibP2P.Protocol.Ping

Eq PingResult Source # 
Instance details

Defined in LibP2P.Protocol.Ping

data PingError Source #

Ping error types.

Constructors

PingTimeout

No echo within the timeout

PingMismatch

Response doesn't match sent bytes

PingStreamError !String

Stream open, negotiation, or I/O error

Instances

Instances details
Show PingError Source # 
Instance details

Defined in LibP2P.Protocol.Ping

Eq PingError Source # 
Instance details

Defined in LibP2P.Protocol.Ping

NAT traversal (AutoNAT, Circuit Relay v2, DCUtR)

data NATConfig Source #

Configuration for the NAT traversal handlers.

Constructors

NATConfig 

Fields

  • ncRelayConfig :: !RelayConfig

    Resource limits for the Circuit Relay v2 server side

  • ncOnRelayedStream :: !(PeerId -> Maybe RelayLimit -> StreamIO -> IO ())

    Invoked when a relay delivers an inbound relayed stream (stop protocol) after the CONNECT/OK exchange: source peer, limit advertised by the relay, and the relayed stream. The application owns the stream from this point (e.g. to run DCUtR over it).

defaultNATConfig :: NATConfig Source #

Default NAT configuration: default relay limits, and inbound relayed streams are left to the remote end (no local consumer).

registerNATHandlers :: Switch -> NATConfig -> IO RelayState Source #

Register all four NAT protocol handlers on the Switch.

Creates the relay server state from ncRelayConfig and returns it so callers can inspect reservations/circuits.

registerAutoNATHandler :: Switch -> IO () Source #

Register the AutoNAT server handler (libp2pautonat/1.0.0).

The dial-back deliberately bypasses the connection pool: reusing the requester's existing connection would always report success. Instead a fresh transport dial + upgrade verifies both reachability and identity, and the probe connection is closed immediately (go-libp2p uses a separate dialer host for the same reason).

registerRelayHopHandler :: Switch -> RelayState -> IO () Source #

Register the Circuit Relay v2 hop handler (libp2pcircuitrelay0.2.0/hop): serve RESERVE and CONNECT requests.

registerRelayStopHandler :: Switch -> (PeerId -> Maybe RelayLimit -> StreamIO -> IO ()) -> IO () Source #

Register the Circuit Relay v2 stop handler (libp2pcircuitrelay0.2.0/stop): accept inbound relayed streams and hand them to the application callback.

registerDCUtRHandler :: Switch -> IO () Source #

Register the DCUtR handler (libp2pdcutr).

Answers the CONNECT/SYNC exchange with our listen addresses and dials the initiator's addresses through the Switch for the hole punch.

data RelayState Source #

Mutable relay server state.

data RelayConfig Source #

Relay server configuration.

Constructors

RelayConfig 

Fields

Instances

Instances details
Show RelayConfig Source # 
Instance details

Defined in LibP2P.NAT.Relay

Eq RelayConfig Source # 
Instance details

Defined in LibP2P.NAT.Relay

defaultRelayConfig :: RelayConfig Source #

Default relay configuration.

newRelayState :: RelayConfig -> IO RelayState Source #

Create new relay state from configuration.

GossipSub

data GossipSubNode Source #

A GossipSub node: Router + Switch integration.

Constructors

GossipSubNode 

Fields

data GossipSubParams Source #

GossipSub router parameters.

Constructors

GossipSubParams 

Fields

defaultGossipSubParams :: GossipSubParams Source #

Default GossipSub parameters per spec.

newGossipSubNode :: Switch -> GossipSubParams -> IO GossipSubNode Source #

Create a new GossipSub node with a Router wired to the Switch.

The Router's gsSendRPC callback opens/reuses outbound streams to peers via the Switch's connection pool.

startGossipSub :: GossipSubNode -> IO () Source #

Start the GossipSub node: register stream handler, notifier, and start heartbeat.

stopGossipSub :: GossipSubNode -> IO () Source #

Stop the GossipSub node: cancel heartbeat and unregister handler.

gossipJoin :: GossipSubNode -> Topic -> IO () Source #

Subscribe to a topic.

gossipLeave :: GossipSubNode -> Topic -> IO () Source #

Unsubscribe from a topic.

gossipPublish :: GossipSubNode -> Topic -> ByteString -> IO () Source #

Publish a message to a topic (signed with the Switch's identity key).