|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +jvm-libp2p is a JVM implementation of the [libp2p](https://libp2p.io/) networking stack, written in Kotlin. It provides peer-to-peer networking capabilities including transport protocols (TCP, QUIC, WebSocket), security channels (Noise, TLS), stream multiplexing (Yamux, Mplex), and pub/sub messaging (Gossipsub, Floodsub). |
| 8 | + |
| 9 | +Notable users: Teku (Ethereum Consensus Layer client), Nabu (minimal IPFS), Peergos (peer-to-peer encrypted filesystem). |
| 10 | + |
| 11 | +## Build Commands |
| 12 | + |
| 13 | +```bash |
| 14 | +# Build the entire project |
| 15 | +./gradlew build |
| 16 | + |
| 17 | +# Run all tests (excludes interop tests tagged with "interop") |
| 18 | +./gradlew test |
| 19 | + |
| 20 | +# Run tests for a specific module |
| 21 | +./gradlew :libp2p:test |
| 22 | + |
| 23 | +# Run a specific test class |
| 24 | +./gradlew :libp2p:test --tests "io.libp2p.pubsub.gossip.GossipRpcPartsQueueTest" |
| 25 | + |
| 26 | +# Run a specific test method |
| 27 | +./gradlew :libp2p:test --tests "io.libp2p.pubsub.gossip.GossipRpcPartsQueueTest.mergeMessageParts*" |
| 28 | + |
| 29 | +# Check code formatting |
| 30 | +./gradlew spotlessCheck |
| 31 | + |
| 32 | +# Apply code formatting |
| 33 | +./gradlew spotlessApply |
| 34 | + |
| 35 | +# Run static analysis (Detekt) |
| 36 | +./gradlew detekt |
| 37 | + |
| 38 | +# Generate documentation |
| 39 | +./gradlew dokkaHtml |
| 40 | +# Output in build/dokka/ |
| 41 | + |
| 42 | +# Clean build artifacts |
| 43 | +./gradlew clean |
| 44 | +``` |
| 45 | + |
| 46 | +**Requirements:** JDK 11 or higher |
| 47 | + |
| 48 | +**Module Structure:** |
| 49 | +- `:libp2p` - Main library module |
| 50 | +- `:tools:simulator` - Gossip network simulator |
| 51 | +- `:tools:schedulers` - Test scheduling utilities |
| 52 | +- `:examples:chatter`, `:examples:cli-chatter`, `:examples:pinger` - Example applications |
| 53 | +- `:interop-test-client` - Interoperability testing client |
| 54 | + |
| 55 | +## Architecture Overview |
| 56 | + |
| 57 | +### Core Abstraction Layers |
| 58 | + |
| 59 | +The library follows a layered architecture with protocol negotiation at each layer: |
| 60 | + |
| 61 | +``` |
| 62 | +Application Layer |
| 63 | + ↓ (Protocol negotiation via multistream-select) |
| 64 | +Stream/Protocol Layer (PingProtocol, ChatProtocol, PubsubRouter) |
| 65 | + ↓ (Stream creation) |
| 66 | +Stream Multiplexing Layer (Yamux, Mplex) |
| 67 | + ↓ (Multiplexer negotiation) |
| 68 | +Security Layer (Noise, TLS) |
| 69 | + ↓ (Security negotiation) |
| 70 | +Transport Layer (TCP, QUIC, WebSocket) |
| 71 | + ↓ |
| 72 | +Raw Network |
| 73 | +``` |
| 74 | + |
| 75 | +### Key Interfaces and Their Roles |
| 76 | + |
| 77 | +**`Host`** (`core/Host.kt`): |
| 78 | +- Main entry point for all libp2p operations |
| 79 | +- Manages identity (`PeerId`, `PrivKey`), network, and protocol handlers |
| 80 | +- Created via DSL builder: `host { identity { ... }; transports { ... }; protocols { ... } }` |
| 81 | + |
| 82 | +**`Network`** (`core/Network.kt`): |
| 83 | +- Manages transports and active connections |
| 84 | +- Handles `listen()` and `dial()` operations |
| 85 | +- Reuses connections to the same peer |
| 86 | + |
| 87 | +**`Connection`** and **`Stream`** (both extend `P2PChannel`): |
| 88 | +- `Connection`: Secured, multiplexed connection between two peers |
| 89 | +- `Stream`: Logical stream over a connection for a specific protocol |
| 90 | + |
| 91 | +**`Transport`** (`transport/Transport.kt`): |
| 92 | +- Handles raw connection establishment (TCP, QUIC, WebSocket) |
| 93 | +- Each transport parses specific multiaddr formats (e.g., `/ip4/127.0.0.1/tcp/30333`) |
| 94 | + |
| 95 | +**`SecureChannel`** (`security/SecureChannel.kt`): |
| 96 | +- Protocol binding for security layer negotiation |
| 97 | +- Returns `SecureChannel.Session` with `remoteId`, `remotePubKey` |
| 98 | +- Implementations: `NoiseXXSecureChannel` (production), `TlsSecureChannel` (beta) |
| 99 | + |
| 100 | +**`StreamMuxer`** (`mux/StreamMuxer.kt`): |
| 101 | +- Protocol binding for multiplexer negotiation |
| 102 | +- Returns `StreamMuxer.Session` for creating/receiving streams |
| 103 | +- Implementations: `MplexStreamMuxer` (production), `YamuxStreamMuxer` (beta) |
| 104 | + |
| 105 | +### The Connection Upgrade Pipeline |
| 106 | + |
| 107 | +When a raw transport connection is established, it goes through staged upgrades: |
| 108 | + |
| 109 | +``` |
| 110 | +1. Raw Transport (TCP/QUIC/WS) |
| 111 | + ↓ |
| 112 | +2. ConnectionBuilder (transport/implementation/ConnectionBuilder.kt) |
| 113 | + ↓ |
| 114 | +3. Security Negotiation → SecureChannel.Session |
| 115 | + ↓ |
| 116 | +4. Multiplexer Negotiation → StreamMuxer.Session |
| 117 | + ↓ |
| 118 | +5. Full Connection Ready → ConnectionOverNetty |
| 119 | +``` |
| 120 | + |
| 121 | +**Key Class:** `ConnectionUpgrader` (`transport/implementation/ConnectionUpgrader.kt`) |
| 122 | +- Orchestrates security and muxer protocol negotiation |
| 123 | +- Uses `MultistreamProtocol` for protocol selection |
| 124 | +- Supports early muxer negotiation (TLS 1.3 feature) |
| 125 | + |
| 126 | +### Protocol Handler Pattern |
| 127 | + |
| 128 | +Custom protocols implement `ProtocolHandler<TController>`: |
| 129 | + |
| 130 | +```kotlin |
| 131 | +// Define protocol binding |
| 132 | +StrictProtocolBinding("/ipfs/ping/1.0.0", PingProtocol()) |
| 133 | + |
| 134 | +// Implement handler |
| 135 | +class PingProtocol : ProtocolHandler<PingController> { |
| 136 | + override fun onStartInitiator(stream: Stream): CompletableFuture<PingController> |
| 137 | + override fun onStartResponder(stream: Stream): CompletableFuture<PingController> |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +See `examples/chatter/ChatProtocol.kt` for a complete example. |
| 142 | + |
| 143 | +### Pub/Sub Architecture |
| 144 | + |
| 145 | +The pub/sub system is located in `pubsub/` and follows this structure: |
| 146 | + |
| 147 | +**`AbstractRouter`** (`pubsub/AbstractRouter.kt`): |
| 148 | +- Base class providing common pubsub logic |
| 149 | +- Manages peer subscriptions via `peersTopics` (multi-bimap) |
| 150 | +- Implements message validation, deduplication (via `SeenCache`), and batching |
| 151 | +- Uses single-threaded event loop (`P2PService`) for thread-safety |
| 152 | + |
| 153 | +**Message Batching via `RpcPartsQueue`**: |
| 154 | +- Per-peer queue that accumulates message parts before transmission |
| 155 | +- Pattern: accumulate parts → flush via `takeMerged()` → send merged RPC |
| 156 | +- Default implementation merges all parts into single RPC |
| 157 | +- Gossip implementation (`GossipRpcPartsQueue`) splits messages to respect per-category limits |
| 158 | + |
| 159 | +**Message Flow:** |
| 160 | +``` |
| 161 | +Outbound: publish() → validateAndBroadcast() → submitPublishMessage(peer) |
| 162 | + → queue.addPublish() → flushPending() → queue.takeMerged() → send() |
| 163 | +
|
| 164 | +Inbound: channelRead() → onInbound() → validate & deduplicate |
| 165 | + → broadcastInbound() → queue.addPublish() → flushPending() |
| 166 | +``` |
| 167 | + |
| 168 | +**Gossip-Specific:** |
| 169 | +- **`GossipRouter`** extends `AbstractRouter` with mesh topology management |
| 170 | +- Heartbeat mechanism for GRAFT/PRUNE/IHAVE/IWANT control messages |
| 171 | +- Peer scoring for spam resistance |
| 172 | +- Control messages batched via `GossipRpcPartsQueue` |
| 173 | + |
| 174 | +**Key Flush Triggers:** |
| 175 | +- After processing inbound messages (sync validation complete) |
| 176 | +- After async message validation completes |
| 177 | +- On peer activation (sends initial subscriptions) |
| 178 | +- During Gossip heartbeat (mesh management operations) |
| 179 | +- After explicit publish/subscribe API calls |
| 180 | + |
| 181 | +### Multistream Protocol Negotiation |
| 182 | + |
| 183 | +**`MultistreamProtocol`** (`protocol/multistream/MultistreamProtocol.kt`): |
| 184 | +- Used at three layers: security negotiation, muxer negotiation, protocol negotiation |
| 185 | +- Contains list of `ProtocolBinding`s with protocol names |
| 186 | +- Delegates to `Negotiator` (initiator/responder) |
| 187 | +- Completes with `ProtocolSelect<T>` containing selected protocol handler |
| 188 | + |
| 189 | +**Pattern:** Any negotiable component extends `ProtocolBinding<T>`: |
| 190 | +- Security channels, stream muxers, application protocols all use this pattern |
| 191 | + |
| 192 | +## Development Patterns |
| 193 | + |
| 194 | +### Netty Integration |
| 195 | + |
| 196 | +All protocol logic is implemented as Netty `ChannelHandler`s: |
| 197 | +- **`P2PChannelOverNetty`**: Base wrapper for both `Connection` and `Stream` |
| 198 | +- **`ConnectionOverNetty`**: Wraps connection-level channel with secure and muxer sessions |
| 199 | +- **`StreamOverNetty`**: Wraps stream-level channel with protocol negotiation |
| 200 | + |
| 201 | +### Async Pattern |
| 202 | + |
| 203 | +Extensive use of `CompletableFuture<T>` for async operations: |
| 204 | +- Protocol negotiation with timeouts |
| 205 | +- Connection establishment across multiple addresses |
| 206 | +- Message publishing and validation |
| 207 | + |
| 208 | +### Event Thread Safety |
| 209 | + |
| 210 | +The pub/sub system (and other components) use single-threaded event loops via `P2PService`: |
| 211 | +- All operations run on `executor: ScheduledExecutorService` |
| 212 | +- Components like `RpcPartsQueue` are explicitly "NOT thread safe" but guaranteed single-threaded access |
| 213 | +- Methods: `runOnEventThread {}`, `submitOnEventThread {}`, `submitAsyncOnEventThread {}` |
| 214 | + |
| 215 | +### Testing Patterns |
| 216 | + |
| 217 | +**JUnit 5** with: |
| 218 | +- `@Test` for standard tests |
| 219 | +- `@ParameterizedTest` with `@MethodSource` for data-driven tests |
| 220 | +- AssertJ for fluent assertions (`assertThat(...)`) |
| 221 | +- MockK for mocking |
| 222 | + |
| 223 | +**Test Infrastructure:** |
| 224 | +- Test fixtures in `src/testFixtures/` for shared test utilities |
| 225 | +- Host builder DSL used extensively in tests |
| 226 | +- `TestChannel` and `TestLogAppender` utilities |
| 227 | + |
| 228 | +**Example Test Pattern (from GossipRpcPartsQueueTest):** |
| 229 | +```kotlin |
| 230 | +@ParameterizedTest |
| 231 | +@MethodSource("testCases") |
| 232 | +fun `test message merging`(params: GossipParams, queue: TestQueue) { |
| 233 | + val monolith = queue.mergedSingle() // Ground truth |
| 234 | + val split = queue.takeMerged() // Actual implementation |
| 235 | + |
| 236 | + // Verify limits respected |
| 237 | + assertThat(split).allMatch { router.validateMessageListLimits(it) } |
| 238 | + |
| 239 | + // Verify semantic equivalence |
| 240 | + assertThat(split.merge().disperse()).isEqualTo(monolith.disperse()) |
| 241 | +} |
| 242 | +``` |
| 243 | + |
| 244 | +### Code Style |
| 245 | + |
| 246 | +- Kotlin 1.6 with JVM target 11 |
| 247 | +- ktlint formatting (run `./gradlew spotlessApply`) |
| 248 | +- Detekt static analysis |
| 249 | +- Wildcard imports allowed |
| 250 | +- No trailing commas enforced |
| 251 | +- All warnings as errors (`allWarningsAsErrors = true`) |
| 252 | + |
| 253 | +## Important Implementation Details |
| 254 | + |
| 255 | +### Protobuf Code Generation |
| 256 | + |
| 257 | +Protobuf definitions in `src/main/proto/` are compiled via `com.google.protobuf` Gradle plugin. |
| 258 | +Generated code in `build/generated/source/proto/main/java/`. |
| 259 | + |
| 260 | +To regenerate: `./gradlew :libp2p:clean :libp2p:build` |
| 261 | + |
| 262 | +### Multiaddr Format |
| 263 | + |
| 264 | +Network addresses use multiaddr format: |
| 265 | +- Example: `/ip4/127.0.0.1/tcp/30333/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N` |
| 266 | +- Parsed/managed in `core/multiformats/` |
| 267 | +- Each transport validates specific multiaddr components |
| 268 | + |
| 269 | +### PeerId Generation |
| 270 | + |
| 271 | +`PeerId` is derived from peer's public key: |
| 272 | +- Multihash of the public key bytes |
| 273 | +- 32-50 bytes depending on key type |
| 274 | +- Supports RSA, Ed25519, Secp256k1, ECDSA |
| 275 | + |
| 276 | +### Security Handshake Timeout |
| 277 | + |
| 278 | +Default timeout for security handshakes: **5 seconds** |
| 279 | +- Applies to Noise and TLS handshakes |
| 280 | +- Configurable in protocol implementations |
| 281 | + |
| 282 | +## Common Development Workflows |
| 283 | + |
| 284 | +### Adding a New Protocol |
| 285 | + |
| 286 | +1. Define protocol binding with multistream name (e.g., `/myapp/myprotocol/1.0.0`) |
| 287 | +2. Implement `ProtocolHandler<TController>` with initiator/responder logic |
| 288 | +3. Register with Host via `protocols { add(...) }` in builder |
| 289 | +4. Implement controller interface for protocol operations |
| 290 | + |
| 291 | +See `examples/chatter/` for a complete example. |
| 292 | + |
| 293 | +### Adding a New Transport |
| 294 | + |
| 295 | +1. Extend `Transport` interface |
| 296 | +2. Implement `listen()` and `dial()` for raw connection establishment |
| 297 | +3. Delegate to `ConnectionUpgrader` for security/muxer negotiation |
| 298 | +4. Add multiaddr parsing logic for transport-specific components |
| 299 | +5. Register with Host via `transports { add(...) }` |
| 300 | + |
| 301 | +### Debugging Connection Issues |
| 302 | + |
| 303 | +- Use `ConnectionVisitor` and `StreamVisitor` for lifecycle observation |
| 304 | +- Enable debug logging for `io.libp2p` package |
| 305 | +- Check multiaddr format compatibility between peers |
| 306 | +- Verify protocol versions match (especially for security/muxer) |
| 307 | + |
| 308 | +### Working with Pub/Sub |
| 309 | + |
| 310 | +- All pub/sub operations run on event thread (thread-safe by design) |
| 311 | +- Message validation happens before broadcasting |
| 312 | +- Seen cache prevents duplicate message processing |
| 313 | +- Control messages automatically batched for efficiency |
| 314 | +- Gossip mesh heartbeat runs every 1 second (default) |
0 commit comments