You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Migrate go-eth2-client to stakefish fork; fix Caplin missed-slot crash
Two related changes in one session:
1. Switch github.com/attestantio/go-eth2-client to the stakefish fork
(feat/erigon-caplin-support, commit 781f0c7f) via a replace directive.
The fork's UnmarshalJSON methods on phase0.Slot/Epoch/Gwei/ValidatorIndex
and electra.DepositRequest natively tolerate Caplin's bare-number JSON,
so the regex-based caplin_compat rewriter (transport + integration test)
is now redundant and removed. caplin_parse_test.go stays as a regression
guard for the fork's native Caplin parsing. service.go's HTTP client is
built via newInstrumentedHTTPClient (same Dialer tuning + metrics wrap,
minus the body rewriter).
2. Fix the staging panic at epoch 94898 (slot 3036736 missed → Caplin 404
"block not found N" on the validators-resolve call → Must() panic).
GetValidatorIndexes now walks forward through the epoch's slots when
Caplin returns 404 on the state_id lookup. Validator set is stable
within an epoch, so any canonical slot answers the same query.
TestGetValidatorIndexes_ProbeForwardOnMissedFirstSlot locks in the
probe-forward behaviour against synthetic 404→200 routes.
Plus a CLAUDE.md sweep that removes stale references to the deleted shim
and rewrites the "Caplin returns 404 on slot-ID state queries" gotcha to
describe the fix; package-comment cleanup in monitoring/doc.go (Package
pkg → Package monitoring); subtest rename in service_e2e_test.go.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-**Beacon Chain Client:**`github.com/attestantio/go-eth2-client` v0.28.1 — redirected via `replace` directive in `go.mod` to `github.com/stakefish/go-eth2-client@feat/erigon-caplin-support` (commit `781f0c7f`) for native Caplin JSON tolerance. Imports stay as `github.com/attestantio/go-eth2-client/...`; the fork keeps the upstream module path. Revert by dropping the replace directive when upstream absorbs the fix.
opts.go -- Global CLI flag variables (package-level vars)
31
31
beaconchain/
32
-
service.go -- BeaconChain wrapper around go-eth2-client (HTTP)
33
-
caplin_compat.go -- HTTP transport that rewrites unquoted amount/index JSON fields in Caplin block responses
34
-
metrics.go -- Beacon API request CounterVec/HistogramVec instrumentation
35
-
caplin_compat_integration_test.go -- Rewriter regex tests + real-block fixture pass-through via the production transport
36
-
caplin_parse_test.go -- json.Unmarshal block_canonical.json into electra.SignedBeaconBlock (schema-drift detector)
32
+
service.go -- BeaconChain wrapper around go-eth2-client (HTTP); builds the metrics-instrumented http.Client via newInstrumentedHTTPClient
33
+
metrics.go -- Beacon API request CounterVec/HistogramVec instrumentation; defines instrumentingTransport
34
+
caplin_parse_test.go -- json.Unmarshal block_canonical.json into electra.SignedBeaconBlock (regression guard that the forked go-eth2-client still tolerates Caplin's bare-number JSON natively)
37
35
service_fixture_test.go -- Offline GetBlock canonical+missed via fixtureServer (no live endpoint)
38
36
metrics_e2e_test.go -- Live-fire metric-detection coverage for the 7 monitor endpoints (build tag: `e2e`)
39
-
service_e2e_test.go -- Live-fire tests for all six BeaconChain methods (build tag: `e2e`)
37
+
service_e2e_test.go -- Live-fire tests covering each BeaconChain wrapper method (GetValidatorIndexes, GetBlock, GetProposerDuties, GetAttesterDuties, GetCommitteeLengths) against the staging endpoint (build tag: `e2e`)
@@ -256,8 +255,7 @@ Most monitoring/beaconchain tests are now fixture-backed integration tests (see
256
255
257
256
-**GetBlock fails on pre-Fusaka slots** -- returns error `"unsupported block version"` for any slot before the Fulu fork
258
257
-**Validator cache has a 30-minute TTL** -- `internal/monitoring/cache.go` persists the `Validators` map plus `LastEpoch` to disk JSON (`$TMPDIR/stakefish-eth2-monitor-cache.json`). `CachedIndex.At` is consulted by `ResolveValidatorKeys` to refresh entries older than 30 minutes; `VALIDATOR_INDEX_INVALID` sentinel entries are also TTL-bounded so a newly-active validator becomes visible within the window. On restart `LastEpoch` gates skip-ahead so cumulative counters don't double-count re-processed epochs. Writes use atomic tmpfile + fsync + rename + dir-fsync for crash durability. Delete the file to force a clean run.
259
-
-**Caplin `amount`/`index` JSON quoting** -- `internal/beaconchain/caplin_compat.go` installs an HTTP transport that rewrites *only* the `"amount":N` and `"index":N` fields (regex `unquotedNumericField`) on `/eth/v2/beacon/blocks/` JSON responses. Other Caplin endpoints, other unquoted uint64 fields (e.g. anything under `solid/`), and SSZ responses are untouched -- those still need a fix upstream in go-eth2-client.
260
-
-**Caplin returns 404 on slot-ID state queries for missed slots** -- `GetValidatorIndexes` uses `fmt.Sprintf("%d", spec.EpochLowestSlot(epoch))` as the state ID. Caplin resolves slot-id states by first finding the block at that slot, so if the first slot of the requested epoch was missed it returns `404 block not found`. Production code has no probe-back logic, so this is a latent flake at epoch-boundary missed slots; the `service_e2e_test.go``get_validator_indexes_roundtrip` subtest works around it by walking back to an epoch whose first slot has a canonical block.
258
+
-**Caplin returns 404 on slot-ID state queries for missed slots** -- `/eth/v1/beacon/states/{slot}/validators` 404s with `block not found N` when slot N was missed; Caplin resolves a slot state_id by walking to the block AT that slot. `GetValidatorIndexes` walks forward through the epoch's slots (validator set is stable within an epoch) until one resolves, capping at the epoch's last slot. The e2e test (`get_validator_indexes_roundtrip`) and the fixture-backed regression test (`TestGetValidatorIndexes_ProbeForwardOnMissedFirstSlot`) both lock this in. Found via staging crash 2026-05-13: epoch 94898's first slot (3036736) was missed and the monitor panicked at `Must(BuildEpochContext)` before the fix.
261
259
-**Slashed validators silently excluded from monitoring** -- `GetValidatorIndexes` filters via `IsAttesting()`, which is false for `active_slashed`*and* for any post-exit state. Once a key is slashed it never reappears in duties or reports (slashed and exited are both filtered) -- surprising during incident response when "where is validator X?" has no log line.
262
260
-**Attestation dedup requires consecutive epoch processing** -- `processAttestations` keys `seenAttestations` on `(validator, slot)` and the cross-epoch lookahead window assumes E and E+1 are processed in order. Skipping an epoch (SSE jump, replay-epoch gap) produces false missed-attestation reports.
263
261
-**`vendor/` is not in git** -- `.gitignore` has `/vendor/` and the directory is genuinely untracked (`git ls-files vendor/` is empty). After a fresh clone vendor/ is absent; `go build` falls back to the module cache. Run `go mod vendor` only if you want a vendored local build. Older docs/comments that imply vendor/ is checked in are stale.
0 commit comments