Skip to content

fix: increase P2P pubsub max message size to match blob size limit#3344

Merged
chatton merged 1 commit into
mainfrom
cian/fix-p2p-message-size-limit
Jun 8, 2026
Merged

fix: increase P2P pubsub max message size to match blob size limit#3344
chatton merged 1 commit into
mainfrom
cian/fix-p2p-message-size-limit

Conversation

@chatton

@chatton chatton commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR correctly initializes the p2p system to use the configured blob size (previously it defaulted to 1Mb and could cause failures for large blocks)

I extracted it out into a separate package so it is readable from both pkg/p2p and block/internal

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Increased P2P pubsub maximum message size to align with the default maximum blob size configuration, preventing fullnode desynchronization when processing large blocks.
  • Tests

    • Added test to validate that PubSub can successfully publish and deliver large message payloads.

Move DefaultMaxBlobSize from block/internal/common to pkg/blobsize so
the P2P layer can import it. Pass it to pubsub.WithMaxMessageSize when
creating FloodSub, preventing fullnode desync when blocks exceed the
default 1 MB libp2p limit.
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR relocates blob-size configuration from an internal package (block/internal/common) to a new public package (pkg/blobsize), updates all internal layer usages to reference the new location, integrates the constant with P2P pubsub gossip messaging to enforce message-size limits, and validates the change via a new integration test.

Changes

Blob Size Configuration Migration

Layer / File(s) Summary
New blobsize package definition
pkg/blobsize/blobsize.go
Introduces a new public DefaultMaxBlobSize variable (default 5 MB) with link-time override support via defaultMaxBlobSizeStr.
Remove old definition and update build configuration
block/internal/common/consts.go, apps/testapp/Dockerfile, CHANGELOG.md
Deletes the old DefaultMaxBlobSize definition and initialization from internal common package, updates Dockerfile linker flag to point to pkg/blobsize, and documents the change.
Migrate DA client and executor to new package
block/internal/da/client.go, block/internal/executing/executor.go
Updates blob submission/filtering and transaction batch-request logic to use blobsize.DefaultMaxBlobSize.
Migrate submission and batching logic to new package
block/internal/submitting/da_submitter.go, block/internal/submitting/submitter.go, block/internal/submitting/batching_strategy_test.go
Updates retry policy and batching-strategy size limits, and updates all batching test cases to reference the new constant.
Migrate syncing logic to new package
block/internal/syncing/syncer.go, block/internal/syncing/syncer_forced_inclusion_test.go
Updates grace-period and forced-inclusion transaction filtering to use blobsize.DefaultMaxBlobSize; updates test block-byte scenarios.
Configure P2P pubsub limits and validate
pkg/p2p/client.go, pkg/p2p/client_test.go
Configures FloodSub with max message size equal to blobsize.DefaultMaxBlobSize, and adds integration test verifying large payloads can be published and delivered.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • evstack/ev-node#3235: Both PRs address -ldflags configuration of DefaultMaxBlobSize and modify apps/testapp/Dockerfile to set the blob-size linker flag, so this migration directly updates the previous PR's linker target.

Suggested reviewers

  • julienrbrt
  • tac0turtle

Poem

🐰 A blob-size wanderer hops to the light,
From depths of common to blobsize bright!
P2P gossip now knows its size,
No more fullnode desync—smooth syncs arise! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: increasing P2P pubsub max message size to align with blob size limit, which directly matches the core objective.
Description check ✅ Passed The description provides context for the change, explaining the problem (P2P defaulted to 1MB causing failures), the solution (extract blob size to separate package), and the rationale.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cian/fix-p2p-message-size-limit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Claude finished @chatton's task in 2m 46s —— View job


Code Review

  • Read AGENTS.md and CLAUDE.md for repo guidelines
  • Analyze all changed files
  • Review new pkg/blobsize package
  • Review P2P client changes
  • Review block/internal package updates
  • Check test coverage
  • Post final review

Summary

This PR fixes a real production bug: the libp2p pubsub layer was hard-capped at 1 MB (its default), while the actual blob size limit could be configured to 120 MB via ldflags. Any full block exceeding 1 MB would silently fail to propagate over P2P, causing full-node desync. The fix is correct and the extraction into pkg/blobsize is the right architectural move — it avoids importing block/internal from pkg/p2p, which would violate the clean package hierarchy.


Issues

1. Unsafe int() cast in WithMaxMessageSizepkg/p2p/client.go:469

c.ps, err = pubsub.NewFloodSub(ctx, c.host, pubsub.WithMaxMessageSize(int(blobsize.DefaultMaxBlobSize)))

blobsize.DefaultMaxBlobSize is uint64. On a 64-bit system the current maximum (120 MB = 125,829,120) fits safely in int, but the cast is unchecked. If the blob size is ever set to a value exceeding math.MaxInt32 (~2 GB) or math.MaxInt on a 32-bit target, the cast silently wraps to a negative number and pubsub rejects all messages. At minimum, add a guard:

if blobsize.DefaultMaxBlobSize > math.MaxInt {
    return fmt.Errorf("configured blob size %d exceeds max int size", blobsize.DefaultMaxBlobSize)
}

Fix this →

2. Empty block/internal/common/consts.goblock/internal/common/consts.go

After the migration the file contains only package common — nothing else. This is dead weight. The file should either be deleted (git rm block/internal/common/consts.go) or, if it was kept deliberately as a placeholder, a comment should explain why.

Fix this →

3. Flaky time.Sleep in TestPubSubMaxMessageSizepkg/p2p/client_test.go

// allow subscription to propagate
time.Sleep(200 * time.Millisecond)

A fixed sleep is a CI flakiness risk — under load the subscription may not be ready in 200 ms. The rest of the test already uses a 5-second receive timeout; a simple poll loop is more robust:

require.Eventually(t, func() bool {
    _ = tp.Publish(ctx, []byte("ping"))
    recvCtx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
    defer cancel()
    _, err := sub.Next(recvCtx)
    return err == nil
}, 5*time.Second, 100*time.Millisecond, "subscription did not become ready")

Or at minimum raise the sleep to 500 ms.

4. No unit tests for pkg/blobsizepkg/blobsize/blobsize.go

The init() function has two distinct paths: the normal parse path and the error/zero fallback. Neither is tested. The fallback is the critical one — if someone supplies a malformed ldflags value, the node silently falls back to 5 MB and the blob size mismatch goes unnoticed. A small table-driven test that overrides defaultMaxBlobSizeStr directly (since the var is package-internal) or tests the parse logic would be valuable here.


Minor Observations

  • DefaultMaxBlobSize is a mutable package-level var: Since it is exported and settable post-init, callers could accidentally modify it. The existing pattern in block/internal/common had the same issue, so this is not a regression — just worth noting.

  • CHANGELOG placement: The new entry is placed under ### Fixed above ### Changes. The ### Changes section exists but the PR properly categorises this as a bug fix. Looks good.

  • Dockerfile ldflags updated correctly (pkg/blobsize.defaultMaxBlobSizeStr). No other scripts or CI files reference the old block/internal/common.defaultMaxBlobSizeStr path — migration is complete.

  • All remaining block/internal/common imports (in components.go, public.go, various syncing files) are for other symbols (RaftNode, BlockOptions, DAHeightEvent) — none of them reference the removed DefaultMaxBlobSize. Compilation is clean.


Verdict

The core fix is correct and the package extraction is the right design. The two issues worth addressing before merge are the unchecked int() cast (#1) and the empty consts.go file (#2). The test sleep (#3) and missing unit tests (#4) are lower priority but worth fixing for long-term reliability.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedJun 8, 2026, 2:27 PM

@chatton chatton marked this pull request as ready for review June 8, 2026 14:27

@tac0turtle tac0turtle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@chatton chatton enabled auto-merge June 8, 2026 14:30
Comment thread pkg/p2p/client.go
func (c *Client) setupGossiping(ctx context.Context) error {
var err error
c.ps, err = pubsub.NewFloodSub(ctx, c.host)
c.ps, err = pubsub.NewFloodSub(ctx, c.host, pubsub.WithMaxMessageSize(int(blobsize.DefaultMaxBlobSize)))
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 46.66667% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.73%. Comparing base (7344e09) to head (4dfb500).

Files with missing lines Patch % Lines
pkg/blobsize/blobsize.go 0.00% 6 Missing ⚠️
block/internal/da/client.go 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3344      +/-   ##
==========================================
+ Coverage   60.71%   60.73%   +0.02%     
==========================================
  Files         127      127              
  Lines       13781    13781              
==========================================
+ Hits         8367     8370       +3     
  Misses       4500     4500              
+ Partials      914      911       -3     
Flag Coverage Δ
combined 60.73% <46.66%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/p2p/client_test.go (2)

85-125: ⚖️ Poor tradeoff

Consider testing the actual configured limit.

The test name suggests it validates the maximum message size, but it only tests that a 1.5 MB payload can be delivered. While this proves the limit exceeds the old 1 MB default, it doesn't verify:

  • Messages at the configured blobsize.DefaultMaxBlobSize (5 MB) are accepted
  • Messages exceeding the limit are rejected

This would provide stronger validation that the pubsub configuration matches the blob size limit as intended.

💡 Suggested enhancement

Consider adding test cases that:

  1. Publish a message at exactly blobsize.DefaultMaxBlobSize and verify delivery
  2. Publish a message slightly exceeding the limit and verify it's rejected (if rejection behavior is predictable and observable)

Alternatively, rename the test to TestPubSubLargeMessage to better reflect what it actually validates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/p2p/client_test.go` around lines 85 - 125, The test
TestPubSubMaxMessageSize currently only verifies a 1.5MB payload; extend it to
assert the actual configured limit by using blobsize.DefaultMaxBlobSize: add a
case that publishes a payload of exactly blobsize.DefaultMaxBlobSize and assert
tp.Publish + sub.Next deliver successfully, and add a second case that publishes
a payload slightly larger than blobsize.DefaultMaxBlobSize and assert tp.Publish
or sub.Next fails (timeout or error) to verify rejection behavior; keep reuse of
tp.Publish and sub.Next and the existing ctx/recvCtx setup and ensure payload
sizes are computed from blobsize.DefaultMaxBlobSize rather than hard-coded
values.

104-104: ⚡ Quick win

Replace fixed sleep with deterministic wait.

The fixed time.Sleep(200 * time.Millisecond) can cause test flakiness on slow CI systems or be unnecessarily slow on fast systems. Consider using the waitForCondition helper (already defined at line 298) to poll for subscription readiness instead.

♻️ Proposed fix using waitForCondition
 	sub, err := subTopic.Subscribe()
 	require.NoError(err)
 
-	// allow subscription to propagate
-	time.Sleep(200 * time.Millisecond)
+	// Wait for subscription to propagate
+	err = waitForCondition(2*time.Second, func() bool {
+		return len(subTopic.ListPeers()) > 0
+	})
+	require.NoError(err, "subscription did not propagate")
 
 	tp, err := clients[0].PubSub().Join(topic)

Note: You may need to verify the correct API for checking subscription state (e.g., ListPeers() or topic mesh membership).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/p2p/client_test.go` at line 104, Replace the brittle time.Sleep(200 *
time.Millisecond) in the test with a deterministic poll using the existing
waitForCondition helper (defined as waitForCondition at line ~298) to wait until
the subscription/peer state is ready; call the subscription readiness check
(e.g., use the client's ListPeers(), topic mesh membership, or the specific
subscription object's method) inside the waitForCondition predicate and fail the
test on timeout so the test proceeds as soon as readiness is observed and is
robust on slow CI.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/blobsize/blobsize.go`:
- Around line 14-21: The init() in pkg/blobsize/blobsize.go must guard
DefaultMaxBlobSize against uint64→int overflow: after parsing
defaultMaxBlobSizeStr into v, verify v is non-zero and v <= uint64(math.MaxInt)
(or equivalent maxInt) before assigning DefaultMaxBlobSize; if it exceeds max
int, set DefaultMaxBlobSize to a safe fallback or clamp to int(maxInt) so that
later cast in pkg/p2p/client.go where
pubsub.WithMaxMessageSize(int(blobsize.DefaultMaxBlobSize)) cannot
wrap/truncate. Ensure you reference DefaultMaxBlobSize, init(), and
defaultMaxBlobSizeStr when making the change.

---

Nitpick comments:
In `@pkg/p2p/client_test.go`:
- Around line 85-125: The test TestPubSubMaxMessageSize currently only verifies
a 1.5MB payload; extend it to assert the actual configured limit by using
blobsize.DefaultMaxBlobSize: add a case that publishes a payload of exactly
blobsize.DefaultMaxBlobSize and assert tp.Publish + sub.Next deliver
successfully, and add a second case that publishes a payload slightly larger
than blobsize.DefaultMaxBlobSize and assert tp.Publish or sub.Next fails
(timeout or error) to verify rejection behavior; keep reuse of tp.Publish and
sub.Next and the existing ctx/recvCtx setup and ensure payload sizes are
computed from blobsize.DefaultMaxBlobSize rather than hard-coded values.
- Line 104: Replace the brittle time.Sleep(200 * time.Millisecond) in the test
with a deterministic poll using the existing waitForCondition helper (defined as
waitForCondition at line ~298) to wait until the subscription/peer state is
ready; call the subscription readiness check (e.g., use the client's
ListPeers(), topic mesh membership, or the specific subscription object's
method) inside the waitForCondition predicate and fail the test on timeout so
the test proceeds as soon as readiness is observed and is robust on slow CI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34b3bf94-3e44-4285-bcd5-15762c52c7bf

📥 Commits

Reviewing files that changed from the base of the PR and between 7344e09 and 4dfb500.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • apps/testapp/Dockerfile
  • block/internal/common/consts.go
  • block/internal/da/client.go
  • block/internal/executing/executor.go
  • block/internal/submitting/batching_strategy_test.go
  • block/internal/submitting/da_submitter.go
  • block/internal/submitting/submitter.go
  • block/internal/syncing/syncer.go
  • block/internal/syncing/syncer_forced_inclusion_test.go
  • pkg/blobsize/blobsize.go
  • pkg/p2p/client.go
  • pkg/p2p/client_test.go
💤 Files with no reviewable changes (1)
  • block/internal/common/consts.go

Comment thread pkg/blobsize/blobsize.go
Comment on lines +14 to +21
func init() {
v, err := strconv.ParseUint(defaultMaxBlobSizeStr, 10, 64)
if err != nil || v == 0 {
DefaultMaxBlobSize = 5 * 1024 * 1024 // 5 MB fallback
return
}
DefaultMaxBlobSize = v
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify where DefaultMaxBlobSize is cast to int and confirm no other guards exist.
rg -nP 'WithMaxMessageSize\s*\(\s*int\s*\(\s*blobsize\.DefaultMaxBlobSize\s*\)\s*\)' -C 2
rg -nP '\bDefaultMaxBlobSize\b' --type=go -C 2
rg -nP 'defaultMaxBlobSizeStr' -C 2

Repository: evstack/ev-node

Length of output: 13381


Guard DefaultMaxBlobSize against uint64int overflow before pubsub.WithMaxMessageSize.

pkg/blobsize/blobsize.go parses defaultMaxBlobSizeStr into DefaultMaxBlobSize and only checks err != nil || v == 0, while pkg/p2p/client.go later does pubsub.WithMaxMessageSize(int(blobsize.DefaultMaxBlobSize)). If defaultMaxBlobSizeStr is set to a value > maxInt, the uint64int cast can wrap/truncate and produce an invalid pubsub max message size.

Suggested fix
 package blobsize
 
 import "strconv"
 
+const fallbackMaxBlobSize uint64 = 5 * 1024 * 1024 // 5 MB
+
 // defaultMaxBlobSizeStr holds the string representation of the default blob
 // size limit. Override at link time via:
 //
 //	go build -ldflags "-X github.com/evstack/ev-node/pkg/blobsize.defaultMaxBlobSizeStr=125829120"
 //
 var defaultMaxBlobSizeStr = "5242880" // 5 MB
 
 // DefaultMaxBlobSize is the max blob size limit used for blob submission.
 var DefaultMaxBlobSize uint64
 
 func init() {
 	v, err := strconv.ParseUint(defaultMaxBlobSizeStr, 10, 64)
 	if err != nil || v == 0 {
-		DefaultMaxBlobSize = 5 * 1024 * 1024 // 5 MB fallback
+		DefaultMaxBlobSize = fallbackMaxBlobSize
 		return
 	}
+	maxInt := uint64(^uint(0) >> 1)
+	if v > maxInt {
+		DefaultMaxBlobSize = maxInt
+		return
+	}
 	DefaultMaxBlobSize = v
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/blobsize/blobsize.go` around lines 14 - 21, The init() in
pkg/blobsize/blobsize.go must guard DefaultMaxBlobSize against uint64→int
overflow: after parsing defaultMaxBlobSizeStr into v, verify v is non-zero and v
<= uint64(math.MaxInt) (or equivalent maxInt) before assigning
DefaultMaxBlobSize; if it exceeds max int, set DefaultMaxBlobSize to a safe
fallback or clamp to int(maxInt) so that later cast in pkg/p2p/client.go where
pubsub.WithMaxMessageSize(int(blobsize.DefaultMaxBlobSize)) cannot
wrap/truncate. Ensure you reference DefaultMaxBlobSize, init(), and
defaultMaxBlobSizeStr when making the change.

Source: Coding guidelines

@chatton chatton added this pull request to the merge queue Jun 8, 2026
Merged via the queue into main with commit 8846ed3 Jun 8, 2026
45 of 47 checks passed
@chatton chatton deleted the cian/fix-p2p-message-size-limit branch June 8, 2026 14:58
@claude claude Bot mentioned this pull request Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants