libp2p / libp2p/unified-testing

c-v0.0.1 x jvm-v1.2 yamux ping stream premature close

Open
#9 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

c jvm
Dominant language
Shell
Stars
7
Forks
19
PR merge metrics
No merged PRs in 30d

Description

**Repos:**
- [Pier-Two/c-libp2p](https://github.com/Pier-Two/c-libp2p) (commit `23a617223a3bbfb4b2af8f219f389e440b9c1ac2`)
- [libp2p/jvm-libp2p](https://github.com/libp2p/jvm-libp2p) (commit `737456de5d96db030aa04c8527585af1a510898f`)

**Transport:** tcp, **Secure:** noise, **Muxer:** yamux

## Summary

When c-v0.0.1 dials jvm-v1.2 via tcp/noise/yamux, the connection and protocol negotiation succeed, but the ping fails. The JVM listener sends a FIN (flags=0x4) on the ping stream before the c dialer can complete the ping exchange, causing the c dialer's stream read to fail with error -7.

## Failing tests (1)

- `c-v0.0.1 x jvm-v1.2 (tcp, noise, yamux)`

## Error output

**c dialer:**
```
[YAMUX] read frame type=0 id=3 flags=0x4 len=0 <-- JVM closes stream 3 (FIN)
[YAMUX_PROCESS_ONE] dispatch rc=-4 type=0 id=3 flags=0x4 len=0 stop=1 goaway=0
[YAMUX_STREAM_RECV] process_one returned -4 for id=1 (fatal)
[PING] stream_read_exact error n=-7
[ERROR] ping failed (err=-3)
```

## Timeline

1. TCP connection established, noise handshake completes
2. Yamux session negotiated (stream 1 for multistream-select, stream 3 for ping)
3. Multistream-select succeeds on stream 1 (identify) and stream 3 (ping protocol)
4. c dialer sends 32-byte ping payload on stream 1
5. JVM listener sends FIN on stream 3 (flags=0x4) — **premature close**
6. c dialer receives the FIN, yamux marks the session as stopped
7. Ping read fails with error -7 (stream closed)
8. c dialer exits with code 1

## Root cause analysis

The JVM listener closes the yamux ping stream (stream 3) with a FIN frame before the ping echo response is sent back. The c dialer's yamux implementation treats receiving a FIN on any stream as a fatal event for the session (`rc=-4`, `stop=1`), which prevents reading the ping response on stream 1.

Two issues contribute:
1. **JVM side:** jvm-libp2p closes the ping stream prematurely, possibly because its ping handler completes and closes the stream before the echo response is fully transmitted
2. **c side:** c-libp2p's yamux implementation treats a FIN on one stream as fatal for the entire session, rather than just that individual stream

## Notes

- The reverse direction (`jvm-v1.2 x c-v0.0.1 tcp/noise/yamux`) shows a different failure pattern — it falls into the "listener exits first" category where the ping actually succeeds but the JVM dialer is killed.
- Both implementations work with other peers via tcp/noise/yamux, so this is specific to the c/jvm combination.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the failing `c-v0.0.1 x jvm-v1.2 (tcp, noise, yamux)` interoperability test and reviewing the referenced c-libp2p and jvm-libp2p commits. Trace the ping stream and yamux FIN handling on both sides to determine which implementation closes or treats the stream incorrectly. Done means the ping exchange completes in this test without the premature-close failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
distributed-systems, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.