Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5601597
fix(yamux): match go-yamux frame types and add write serialization
asabya Mar 7, 2026
16225bc
Merge pull request #1 from asabya/fix/yamux-go-interop
asabya Mar 7, 2026
42f08ed
Merge branch 'libp2p:main' into main
asabya Mar 7, 2026
b3ec1bd
chore: remove orphaned submodule entries
asabya Mar 7, 2026
b8d5339
feat: add yamux receive window auto-tuning
asabya Mar 13, 2026
bf45717
Merge pull request #2 from asabya/feat/yamux-window-auto-tuning
asabya Mar 13, 2026
9a9a4a6
Merge branch 'libp2p:main' into main
asabya Mar 13, 2026
150c1d9
Merge branch 'main' into main
asabya Mar 19, 2026
8ee6174
fix: tests
asabya Mar 19, 2026
fb546e0
Merge branch 'main' into main
acul71 Mar 21, 2026
1ba2189
fix: yamux go-yamux interop and review fixes (#1269)
asabya Mar 23, 2026
ebb18a4
fix: accumulate both pass deltas in auto-tune window update
asabya Mar 23, 2026
112b7cf
Merge pull request #3 from asabya/fix/yamux-go-interop
asabya Mar 23, 2026
355898e
Merge branch 'main' into main
acul71 Mar 24, 2026
327a085
Merge branch 'main' into main
seetadev Mar 24, 2026
ee62999
fix: add Noise write chunking and yamux MaxMessageSize cap
asabya Mar 25, 2026
3cf56ae
fix: handle empty messages in Noise write chunking
asabya Mar 25, 2026
3fdbe1e
fix: assemble chunked Noise reads in secure session"
asabya Mar 27, 2026
7c8c0ec
Merge branch 'main' into main
acul71 Mar 29, 2026
7223cd3
Merge pull request #4 from asabya/fix/yamux-go-interop
asabya Apr 2, 2026
a01cbdd
Merge branch 'main' into main
asabya Apr 7, 2026
7200e73
Fix yamux nursery cancellation to cancel scope on handle_incoming exit
asabya Apr 7, 2026
86bc17a
Merge branch 'main' into main
acul71 Apr 11, 2026
99a2270
Merge branch 'main' into main
acul71 Apr 13, 2026
6eecd91
Merge branch 'main' into main
acul71 Apr 13, 2026
b7a5ae6
Merge branch 'main' into main
acul71 Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion extra/multihash-spec
Submodule multihash-spec deleted from b43ec1
1 change: 0 additions & 1 deletion extra/py-multihash
Submodule py-multihash deleted from dfae0d
1 change: 0 additions & 1 deletion extra/pymultihash
Submodule pymultihash deleted from 215298
31 changes: 23 additions & 8 deletions libp2p/security/noise/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
MAX_NOISE_MESSAGE_LEN = 2 ** (8 * SIZE_NOISE_MESSAGE_LEN) - 1
SIZE_NOISE_MESSAGE_BODY_LEN = 2
MAX_NOISE_MESSAGE_BODY_LEN = MAX_NOISE_MESSAGE_LEN - SIZE_NOISE_MESSAGE_BODY_LEN
# Max plaintext per Noise message: 65535 - 16 bytes Poly1305 MAC overhead.
# Matches go-libp2p's MaxPlaintextLength in p2p/security/noise/rw.go.
MAX_PLAINTEXT_LENGTH = MAX_NOISE_MESSAGE_LEN - 16
BYTE_ORDER = "big"

# | Noise packet |
Expand Down Expand Up @@ -53,14 +56,26 @@ def __init__(self, conn: IRawConnection, noise_state: NoiseState) -> None:
self.noise_state = noise_state

async def write_msg(self, msg: bytes, prefix_encoded: bool = False) -> None:
logger.debug(f"Noise write_msg: encrypting {len(msg)} bytes")
data_encrypted = self.encrypt(msg)
if prefix_encoded:
# Manually add the prefix if needed
data_encrypted = self.prefix + data_encrypted
logger.debug(f"Noise write_msg: writing {len(data_encrypted)} encrypted bytes")
await self.read_writer.write_msg(data_encrypted)
logger.debug("Noise write_msg: write completed successfully")
# Chunk large messages to stay within the Noise 65535-byte transport
# message limit, matching go-libp2p's noise/rw.go Write() approach.
if len(msg) <= MAX_PLAINTEXT_LENGTH:
# Fast path: single message (covers handshake and small writes)
data_encrypted = self.encrypt(msg)
if prefix_encoded:
data_encrypted = self.prefix + data_encrypted
await self.read_writer.write_msg(data_encrypted)
else:
# Slow path: chunk into multiple Noise messages
total = len(msg)
written = 0
while written < total:
end = min(written + MAX_PLAINTEXT_LENGTH, total)
chunk = msg[written:end]
data_encrypted = self.encrypt(chunk)
if prefix_encoded and written == 0:
data_encrypted = self.prefix + data_encrypted
await self.read_writer.write_msg(data_encrypted)
written = end

async def read_msg(self, prefix_encoded: bool = False) -> bytes:
logger.debug("Noise read_msg: reading encrypted message")
Expand Down
42 changes: 30 additions & 12 deletions libp2p/security/secure_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,42 @@ async def read(self, n: int | None = None) -> bytes:
return b""

data_from_buffer = self._drain(n)
if len(data_from_buffer) > 0:
if n is None and len(data_from_buffer) > 0:
return data_from_buffer

msg = await self.conn.read_msg()
if n is None:
msg = await self.conn.read_msg()

# If underlying connection returned empty bytes, treat as closed
# and raise to signal that reads after close are invalid.
if msg == b"":
raise Exception("Connection closed")
# If underlying connection returned empty bytes, treat as closed
# and raise to signal that reads after close are invalid.
if msg == b"":
raise Exception("Connection closed")

if n is None:
return msg

if n < len(msg):
self._fill(msg)
return self._drain(n)
else:
return msg
if len(data_from_buffer) == n:
return data_from_buffer

result = bytearray(data_from_buffer)
while len(result) < n:
msg = await self.conn.read_msg()

# If the connection closes after a partial read, return the bytes
# we already assembled. This preserves the stream-read behavior
# expected by higher layers.
if msg == b"":
if result:
return bytes(result)
raise Exception("Connection closed")

remaining = n - len(result)
if len(msg) <= remaining:
result.extend(msg)
else:
result.extend(msg[:remaining])
self._fill(msg[remaining:])

return bytes(result)

async def write(self, data: bytes) -> None:
await self.conn.write_msg(data)
Expand Down
Loading
Loading