Skip to content

feat(core): implement gater#486

Open
iamquang95 wants to merge 3 commits into
mainfrom
feat/core-gater
Open

feat(core): implement gater#486
iamquang95 wants to merge 3 commits into
mainfrom
feat/core-gater

Conversation

@iamquang95

Copy link
Copy Markdown
Collaborator

Fix #399

Ports the core/gater component: a DutyGater that rejects duties arriving from peers whose type is invalid or whose epoch is too far in the future.

  • crates/core/src/gater.rs — DutyGater::new(client) fetches genesis time + slot config from the beacon node and builds the gater (default budget: 2 future epochs); allows(&duty) -> bool is the predicate, and into_fn() yields the shared DutyGaterFn (Arc<dyn Fn(&Duty) -> bool + Send + Sync>) that the wire components consume.
  • Canonical type — pluto_core::gater::DutyGaterFn is now the single source of truth; parsigex, consensus, and dkg use it directly instead of each redefining an identical alias.

@iamquang95

Copy link
Copy Markdown
Collaborator Author

@claude review this PR

@claude

claude Bot commented Jun 16, 2026

Copy link
Copy Markdown

Claude finished @iamquang95's task in 2m 20s —— View job


Review — feat(core): implement gater

  • Read the new gater.rs and changed files
  • Compare against Charon Go core/gater.go
  • Analyze correctness, style, and tests
  • Post review feedback

This is a faithful, high-quality port of Charon's core/gater.go (NewDutyGater). Logic matches the Go original (type validity + future-epoch budget, no past-duty check), the consumer refactor unifying DutyGaterFn is consistent across all crates, and test coverage is excellent (async new/with_options paths, epoch boundaries, pre-genesis, into_fn parity, invalid types). No blocking issues. ✅

Correctness — verified against Go

  • is_valid() (crates/core/src/types.rs:70) = !Unknown && !DutySentinel(_), matching Go's t > unknown && t < sentinel — including InfoSync as valid. ✓
  • The expect() on checked_div(slots_per_epoch) and checked_div(slot_duration_ms) are sound: fetch_slots_config (crates/eth2api/src/extensions.rs:281) rejects zero slots_per_epoch/slot_duration, and with_options re-checks slot_duration_ms != 0. ✓
  • current_epoch() is recomputed per allows() call via the injected clock, mirroring Go calling nowFunc() on each invocation. ✓
  • Refactor: no dangling references to the removed pluto_parsigex::DutyGater / qbft::DutyGater aliases anywhere in the workspace — all consumers now use pluto_core::gater::DutyGaterFn. ✓

Intentional divergences (both improvements, worth noting)

  1. Pre-genesis clamping. Go computes uint64(currentSlot) from a negative duration, which wraps to a huge epoch and effectively allows everything before genesis. The Rust port clamps elapsed time to 0 (gater.rs:132), so a pre-genesis gater rejects future duties instead. This is more correct and documented as unreachable in practice (gater is built after genesis). Good call — just flagging that it is a behavioral difference from the Go source.
  2. Millisecond-resolution arithmetic. Go divides at nanosecond precision (Sub(genesisTime) / slotDuration); the port truncates to whole ms via num_milliseconds() before dividing. Since genesis is whole seconds and slots are seconds-long, this is functionally equivalent (both truncate toward the slot floor); the InvalidSlotDuration guard makes the sub-ms case explicit. No action needed.

Minor / optional

  • DutyGater can't derive Debug because of Box<dyn Clock>. Not required here, but if any wiring struct later wants #[derive(Debug)] it'll need a manual impl. Low priority.
  • behaviour.rs:25 / handler.rs:22 use single-line nested imports pluto_core::{gater::DutyGaterFn, types::{...}}. Assuming cargo +nightly fmt --all --check passes in CI this is fine; just confirm it's been run.
  • The gater() test helper constructs DutyGater via struct literal (fields are private but test is in-module), which nicely sidesteps needing a beacon mock for the synchronous boundary tests. Nice.

Overall: clean port, well-tested, ready to merge from a correctness standpoint. The only thing I'd suggest is a one-line note in the doc comment that the pre-genesis behavior intentionally differs from Charon (in case a future reader diffs the two).
feat/core-gater

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.

Implement core/gater

1 participant