Skip to content

feat(wireguard): per-instance MaxCount + extend to Autobahn fields#3606

Merged
wen-coding merged 9 commits into
mainfrom
wen/autobahn_wireguard_caps
Jun 18, 2026
Merged

feat(wireguard): per-instance MaxCount + extend to Autobahn fields#3606
wen-coding merged 9 commits into
mainfrom
wen/autobahn_wireguard_caps

Conversation

@wen-coding

@wen-coding wen-coding commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Extends the pre-decode wire scanner from #3458 to Autobahn messages, and generalises the wiring so adding protection to a new message type requires only a proto annotation — no per-channel or per-call plumbing.

Proto annotations

autobahn.proto gains (wireguard.max_count) on all repeated fields:

Field Cap
LaneQC.sigs, PrepareQC.sigs, CommitQC.sigs, AppQC.sigs 100
TimeoutQC.votes 100
FullProposal.lane_qcs, Proposal.lane_ranges, FullCommitQC.headers 100
Payload.txs 2 000 (matches MaxTxsPerBlock)

Caps are checked per instance of the containing message, not summed globally. A FullProposal with 10 LaneQCs each carrying 10 sigs is fine; total memory at any nesting depth is bounded by the product of the caps along the path (outer_cap × inner_cap × …). To raise a cap later, update (wireguard.max_count) in autobahn.proto and re-run:

GOWORK=off go run github.com/bufbuild/buf/cmd/buf@v1.58.0 generate --template sei-tendermint/internal/buf.gen.yaml

Automatic wiring (main change)

Previously, each channel had to set PreDecode explicitly in the reactor. Now the schema is wired directly to the proto type:

  • Plugin emits WireguardScan([]byte) error on each schema-bearing type (instead of init()/registry calls).
  • protoutils.Unmarshal[T] asserts wireguard.Scanner and scans before proto.Unmarshal — zero cost for types without a schema.
  • transport.go asserts wireguard.Scanner on the channel MessageType, replacing the PreDecode field on ChannelDescriptorT.
  • ChannelDescriptorT.PreDecode removed — all four explicit assignments (blocksync, consensus, evidence, statesync) dropped; protection follows from the type.

wireguard package changes

  • wireguard.Scanner interface exported from the wireguard package (was an unexported duplicate in protoutils/msg.go).
  • MaxCount semantics tightened to per-instance: each nested-message occurrence gets a fresh counter map, so the cap is "at most N per outer element" rather than "N total across the payload".
  • Wireguard plugin entry folded into sei-tendermint/internal/buf.gen.yaml alongside the hashable plugin (separate autobahn.wireguard.buf.gen.yaml deleted).

Plugin bug fix

Proto3 optional fields produce a synthetic oneof in the descriptor but no wrapper struct in Go. The plugin was incorrectly using the Message_Field wrapper name for them. Fixed with !f.ContainingOneof().IsSynthetic().

Test plan

  • go test ./sei-tendermint/internal/autobahn/... — all autobahn packages pass
  • go test ./sei-tendermint/internal/blocksync/... ./sei-tendermint/internal/statesync/... ./sei-tendermint/internal/consensus/... ./sei-tendermint/internal/evidence/... — wiring tests pass including no-op assertions
  • go test ./sei-tendermint/internal/protoutils/... — wireguard scanner + plugin tests pass, including per-instance cap and proto3 optional tests
  • go build ./sei-tendermint/... — clean build

🤖 Generated with Claude Code

@cursor

cursor Bot commented Jun 17, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches P2P inbound validation and consensus/Autobahn wire limits; per-instance cap semantics are a behavior change from prior global counting, though still bounds memory and drops malicious peers on violation.

Overview
Extends protobuf wireguard limits to Autobahn consensus messages and shifts enforcement from per-channel PreDecode hooks to generated WireguardScan methods on message types.

Autobahn protos now annotate repeated fields with (wireguard.max_count) (e.g. signature lists capped at 100, Payload.txs at 2000), with matching generated schemas in autobahn.wireguard.go. The wireguard plugin is wired into internal/buf.gen.yaml alongside the hashable plugin.

Enforcement path: ChannelDescriptorT.PreDecode is removed; P2P transport.go and protoutils.Unmarshal call WireguardScan when the type implements wireguard.Scanner. Reactor channel descriptors no longer set explicit pre-decode schemas. Wiring tests now assert Message.WireguardScan behavior instead of descriptor hooks.

Semantics change: MaxCount is per nested message instance (fresh counters per occurrence), not a global sum across the payload—tests and comments updated accordingly (e.g. multiple commits each at cap pass; shared-budget tests removed).

Plugin fix: proto3 optional synthetic oneofs no longer emit bogus MustFieldNum[Foo_Bar] wrapper types.

Reviewed by Cursor Bugbot for commit 5ac7d77. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedJun 18, 2026, 1:47 PM

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 43.00000% with 57 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.14%. Comparing base (8d59181) to head (5ac7d77).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...dermint/internal/autobahn/pb/autobahn.wireguard.go 43.75% 18 Missing ⚠️
...ndermint/proto/tendermint/types/types.wireguard.go 14.28% 12 Missing ⚠️
...ermint/proto/tendermint/consensus/wal.wireguard.go 0.00% 6 Missing ⚠️
...ermint/proto/tendermint/privval/types.wireguard.go 0.00% 6 Missing ⚠️
...i-tendermint/internal/p2p/giga/pb/api.wireguard.go 71.42% 4 Missing ⚠️
sei-tendermint/internal/protoutils/msg.go 33.33% 1 Missing and 1 partial ⚠️
...mint/proto/tendermint/blocksync/types.wireguard.go 50.00% 2 Missing ⚠️
...mint/proto/tendermint/consensus/types.wireguard.go 50.00% 2 Missing ⚠️
...mint/proto/tendermint/statesync/types.wireguard.go 50.00% 2 Missing ⚠️
...ndermint/proto/tendermint/types/block.wireguard.go 0.00% 2 Missing ⚠️
... and 1 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3606      +/-   ##
==========================================
- Coverage   59.02%   58.14%   -0.88%     
==========================================
  Files        2215     2150      -65     
  Lines      182513   174057    -8456     
==========================================
- Hits       107720   101212    -6508     
+ Misses      65101    63857    -1244     
+ Partials     9692     8988     -704     
Flag Coverage Δ
sei-chain-pr 73.26% <43.00%> (?)
sei-db 70.41% <ø> (ø)
sei-db-state-db ?

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

Files with missing lines Coverage Δ
sei-tendermint/internal/blocksync/reactor.go 67.83% <ø> (-0.09%) ⬇️
sei-tendermint/internal/consensus/reactor.go 71.30% <ø> (+1.17%) ⬆️
sei-tendermint/internal/evidence/reactor.go 75.24% <ø> (+3.67%) ⬆️
sei-tendermint/internal/p2p/conn/oldmux.go 93.17% <ø> (+0.77%) ⬆️
...rmint/internal/protoutils/wireguard/plugin/main.go 82.23% <100.00%> (+0.65%) ⬆️
...dermint/internal/protoutils/wireguard/wireguard.go 96.22% <100.00%> (+3.77%) ⬆️
sei-tendermint/internal/statesync/reactor.go 71.26% <ø> (+0.80%) ⬆️
sei-tendermint/internal/p2p/transport.go 83.90% <50.00%> (ø)
sei-tendermint/internal/protoutils/msg.go 80.00% <33.33%> (-11.67%) ⬇️
...mint/proto/tendermint/blocksync/types.wireguard.go 50.00% <50.00%> (ø)
... and 8 more

... and 75 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

wen-coding and others added 4 commits June 17, 2026 13:38
…c (CON-298 follow-up)

Annotates all repeated fields in autobahn.proto with (wireguard.max_count),
then generalises the wiring so no per-channel or per-call plumbing is needed:

- Plugin now emits WireguardScan([]byte) error on each schema-bearing type
  instead of init()/registry calls.
- protoutils.Unmarshal[T] asserts wireScanner and scans before proto.Unmarshal
  automatically.
- transport.go asserts wireScanner on the channel MessageType, replacing the
  explicit PreDecode field on ChannelDescriptorT.
- Removes PreDecode from ChannelDescriptorT and all four reactor call-sites
  (blocksync, consensus, evidence, statesync); protection is now derived from
  the proto type, not the channel config.
- Fixes plugin bug: proto3 optional fields were incorrectly treated as oneof
  variants, generating non-existent wrapper type names.
- Wiring tests rewritten to call WireguardScan directly; no-op tests added for
  channels whose message variants don't reach a capped field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
api.wireguard.go was generated alongside autobahn.wireguard.go but missed
from the initial commit — the giga LaneReq/LaneResp/etc types transitively
reach the annotated Autobahn fields and get WireguardScan automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The (wireguard.max_count) options added to autobahn.proto change the
embedded raw descriptor in autobahn.pb.go. Regenerated via
sei-tendermint/internal/buf.gen.yaml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sigs, votes, lane_ranges, lane_qcs, and block headers in QC messages
are bounded by the validator set size, which is far below 10000. Use
100 as a tighter, more accurate ceiling. Payload.txs stays at 2000.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wen-coding wen-coding force-pushed the wen/autobahn_wireguard_caps branch from 2d80c06 to 17b85f2 Compare June 17, 2026 20:38

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 17b85f2. Configure here.

Comment thread sei-tendermint/internal/autobahn/autobahn.proto
Add TestPlugin_Proto3OptionalDoesNotEmitWrapperType: a message with a
proto3 optional field (synthetic oneof in the descriptor, no Go wrapper
struct) alongside a capped repeated field. Asserts the plugin does not
emit a nonexistent Foo_Bar wrapper type and correctly caps items.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread autobahn.wireguard.buf.gen.yaml Outdated
@@ -0,0 +1,11 @@
version: v2

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.

this can be just added to the internal/buf.gen.yaml

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

// wireScanner is implemented by proto types whose generated *.wireguard.go
// adds a WireguardScan method. Unmarshal calls it automatically before
// proto.Unmarshal so no per-call wiring is needed.
type wireScanner interface {

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.

this should be exposed by wireguard

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

require.NoError(t, ev.WireguardScan(wgtest.Marshal(t, &ev)))
}

func init() {

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.

init wrapper is useless here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed

wen-coding and others added 3 commits June 18, 2026 06:22
- Fold autobahn.wireguard.buf.gen.yaml into sei-tendermint/internal/buf.gen.yaml;
  the wireguard plugin now lives alongside the hashable plugin in one template
- Export wireguard.Scanner interface from the wireguard package; remove the
  unexported wireScanner duplicate in protoutils/msg.go
- Remove useless init() compile-time assertion from evidence/wiring_test.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the counts map was shared across the entire Scan call, so a
cap on an inner field (e.g. LaneQC.sigs = 100) accumulated across all
occurrences of the outer message (e.g. every LaneQC in lane_qcs). A
FullProposal with 10 LaneQCs each carrying 10 sigs would hit the cap
even though no single LaneQC was over limit.

Fix: pass a fresh counts map when recursing into each nested-field
occurrence. The cap is now checked per instance of the containing
message, which is the correct local semantics.

Update two tests that asserted the old global-accumulation behaviour;
add TestScan_CountsArePerNestedInstance to cover the new contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a comment at the recursion site and in Rule.MaxCount explaining
that per-instance counting is both intuitive ("at most N per outer
element") and still bounds total memory by the product of caps along
the nesting path. Update tests to make the memory-bound reasoning
explicit in the comment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wen-coding wen-coding changed the title wireguard: extend caps to Autobahn messages; make protection automatic (CON-298 follow-up) feat(wireguard): per-instance MaxCount + extend to Autobahn fields Jun 18, 2026
Remove SharedBudgetAcrossLastCommitAndEvidence and EvidenceCommitsShareBudget
tests from blocksync, consensus, and types — these asserted global accumulation,
which no longer holds. Replace with MultipleCommitsEachAtCapPass to document the
new per-instance contract.

Update RejectsDuplicateNonRepeatedFields → RejectsDuplicateNonRepeatedFieldOverCap:
test that a single over-cap occurrence is still rejected, not that two at-cap
occurrences sum past the cap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wen-coding wen-coding added this pull request to the merge queue Jun 18, 2026
Merged via the queue into main with commit 51ce0c0 Jun 18, 2026
60 checks passed
@wen-coding wen-coding deleted the wen/autobahn_wireguard_caps branch June 18, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants