Skip to content

cl, db, execution, p2p, cmd: fix copy-paste drift defects#22165

Merged
AskAlexSharov merged 13 commits into
mainfrom
yperbasis/dedup-audit-fixes
Jul 8, 2026
Merged

cl, db, execution, p2p, cmd: fix copy-paste drift defects#22165
AskAlexSharov merged 13 commits into
mainfrom
yperbasis/dedup-audit-fixes

Conversation

@yperbasis

@yperbasis yperbasis commented Jul 2, 2026

Copy link
Copy Markdown
Member

Fixes eleven defects that all share one root cause: code duplicated between siblings and then patched or mutated in only one copy. Found during a whole-repo duplication audit; each fix is surgical and independent. (A twelfth — Bytes4/48/64/96.SetBytes keeping length.Hash from the Hash.SetBytes original — was absorbed by the shared-helper refactor in #22173 and is no longer part of this diff.)

With regression tests (written first, red → green):

  • cl/cltypes: BlindedBeaconBody.EncodingSizeSSZ counted ExecutionChanges twice and never BlobKzgCommitments; NewBlindedBeaconBody dropped its version argument (Version: 0), unlike NewBeaconBodyblock_production.go was already working around it with an explicit SetVersion (TestBlindedBeaconBodyEncodingSizeTracksEncoding).
  • cl/beacon/handler: of the three cloned pending_{consolidations,deposits,partial_withdrawals} endpoints only the third set Eth-Consensus-Version/version; now all three do (TestGetPendingQueuesConsensusVersionHeader; forkchoice mock's pending getters made settable for it).

Without new tests (log/metric/dead-code corrections, or not reasonably reproducible as a unit test — noted per item):

  • db/state: IntegrityInvertedIndexAllValuesAreInRange had the StorageHistoryIdx/CodeHistoryIdx arms crossed, so a targeted erigon seg integrity check validated the other domain's inverted index. A red repro test would need a corrupt-II fixture; the fix is a label/index correction verifiable by inspection.
  • execution/exec: AA-bundle log printed startIdx-endIdx (uint64 underflow); the historical-trace copy of the same code already had the correct form.
  • execution/stagedsync: per-domain exec_domain_cache_put_rate gauges were set to raw counts while the aggregate sets a per-second rate — dashboards mixing them compared different units; also mxExex*/mxCommitmentBrancg* variable typos, and the serial executor re-inlined the extracted-and-tested shouldMarkExhaustedAtBlock instead of calling it (behavior identical: (!a || b) ≡ (a → b)).
  • p2p/sentry: getBlockHeaders66 returned the same error on both branches of its IsPeerNotFoundErr check; a peer disconnecting mid-answer now returns nil like the bodies/BAL handlers.
  • cmd/txpool: the daemon logged under the "integration" prefix, pasted from cmd/integration.
  • db/kv/mdbx: pseudo-dupsort LastDup wrapped errors as "in FirstDup".
  • db/snapshotsync/freezeblocks: deleted SegmentsCaplin, a dead near-verbatim copy of snapshotsync.SegmentsCaplin with zero callers.

Verification: affected-package tests green, scoped golangci-lint clean (×2), make erigon integration builds.

yperbasis added 11 commits July 2, 2026 21:11
The Bytes4/48/64/96 copies of Hash.SetBytes kept length.Hash (32) in the
crop and right-align arithmetic, so SetBytes panicked or misplaced bytes
for most input lengths. Use the receiver's own length, matching
Hash.SetBytes semantics (crop from the left, right-align short inputs).
…sion

Two divergences from the BeaconBody sibling: the Deneb gate in
EncodingSizeSSZ added ExecutionChanges a second time instead of
BlobKzgCommitments, and NewBlindedBeaconBody dropped its version argument
(callers had to SetVersion by hand; block_production.go does, Clone did
not).
…endpoints

pending_consolidations and pending_deposits were missing the version
header and body field that pending_partial_withdrawals already returns;
the three handlers are copies of each other. Also drop a stray tab from
two error messages and make the forkchoice mock's pending getters
settable for the test.
The StorageHistoryIdx arm checked the code domain's inverted index and
CodeHistoryIdx checked storage's, so a targeted integrity check
validated the wrong index.
startIdx-endIdx underflows; use endIdx-startIdx+1 like the historical
trace worker's copy of this code.
Both branches of the IsPeerNotFoundErr check returned the same error;
mirror the bodies handler and treat a vanished peer as success.
The four per-domain exec_domain_cache_put_rate gauges were set to raw
counts while the aggregate sets a per-second rate; divide by the
interval. Also fix mxExex*/mxCommitmentBrancg variable-name typos.
…utor

The serial loop re-inlined the condition that was extracted and
unit-tested for the parallel path.
SetupCobra was called with "integration", pasted from cmd/integration.
The pseudo-dupsort LastDup wrapped its error as "in FirstDup".
Near-verbatim copy of snapshotsync.SegmentsCaplin with no callers.

Copilot AI 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.

Pull request overview

This pull request fixes a set of “copy-paste drift” defects across multiple subsystems (execution, CL types/handlers, p2p sentry, db utilities, and CLI), bringing duplicated implementations back into alignment and adding regression tests where the issue is reproducible.

Changes:

  • Corrects several logic/behavior defects caused by diverged duplicated code (e.g., SSZ size accounting, consensus-version headers, inverted-index integrity routing, sentry peer-not-found handling, AA bundle length logging).
  • Normalizes stagedsync metrics naming/units and reduces drift by reusing shared logic (shouldMarkExhaustedAtBlock).
  • Adds targeted regression tests for the previously broken behaviors and removes dead/unused code.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated no comments.

Show a summary per file
File Description
p2p/sentry/sentry_multi_client/sentry_multi_client.go Ignore “peer not found” when sending header response (aligns with other handlers).
execution/stagedsync/exec3_serial.go Reuses shared exhaustion predicate to avoid drift from parallel executor logic.
execution/stagedsync/exec3_metrics.go Fixes metric var typos and makes per-domain put gauges consistent with rate units.
execution/exec/txtask.go Fixes AA bundle log length calculation to avoid underflow/misreporting.
db/state/integrity.go Corrects domain routing for Storage/Code history inverted-index integrity checks.
db/snapshotsync/freezeblocks/block_snapshots.go Removes dead SegmentsCaplin duplicate implementation.
db/kv/mdbx/kv_mdbx.go Fixes incorrect error context string in LastDup.
common/bytesn_test.go Adds regression coverage for BytesN.SetBytes semantics.
common/bytes96.go Fixes Bytes96.SetBytes cropping/alignment to match Hash.SetBytes semantics.
common/bytes64.go Fixes Bytes64.SetBytes cropping/alignment to match Hash.SetBytes semantics.
common/bytes48.go Fixes Bytes48.SetBytes cropping/alignment to match Hash.SetBytes semantics.
common/bytes4.go Fixes Bytes4.SetBytes cropping/alignment to match Hash.SetBytes semantics.
cmd/txpool/main.go Fixes logger prefix to txpool (was incorrectly integration).
cl/phase1/forkchoice/mock_services/forkchoice_mock.go Makes pending-queue getters mockable via stored maps for handler tests.
cl/cltypes/beacon_block_blinded.go Fixes constructor version propagation and Deneb SSZ size accounting for BlobKzgCommitments.
cl/cltypes/beacon_block_blinded_test.go Adds regression test for EncodingSizeSSZ tracking true encoding deltas.
cl/beacon/handler/states.go Ensures pending queue endpoints set Eth-Consensus-Version header and response version consistently.
cl/beacon/handler/states_test.go Adds regression test asserting consensus-version header is set across all pending queue endpoints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 3, 2026
`bytes48.go` and `bytes96.go` were byte-identical whole files modulo the
type name; `bytes4.go`, `bytes64.go`, `hash.go`, and `address.go`
repeated most of the same method bodies. Since Go can't parameterize
`[N]byte` by length and these types must stay comparable arrays, the
bodies move to unexported helpers over `[]byte` (`fixedFormat`,
`fixedSetBytes`, `fixedTerminalString`, `fixedGenerate`); each method
becomes a one-liner passing `b[:]`. Net −169 production lines; no
exported API change.

Kept bespoke on purpose: `Address`'s EIP-55 checksummed
`Hex`/`String`/`Format`; `Bytes4.TerminalString` (`%x` of the whole
array — head…tail would overlap at N=4); `Hash.Generate`
(`math/rand/v2`, incompatible with the v1 quick-check helper); all
Hash/Address-only methods.

Safety net: `TestFixedBytesGolden` (+377 lines) pins per type — every
Format verb incl. bad-verb labels, String/Hex/MarshalText/Value,
TerminalString, SetBytes placement, UnmarshalText/JSON error strings
(incl. the `BLSSignature` label), seeded `Generate`, and the EIP-55
canonical example for Address. Goldens were captured from the
pre-refactor implementation and stay green through the refactor.

First commit is a cherry-pick of the `SetBytes` fix from erigontech#22165 (this
consolidation builds on it); it will drop out on rebase once that PR
merges.

Verified: `go test ./common/...`, `go build ./...`, scoped
`golangci-lint` clean (×2), `make erigon`.

Part of a dedup series; siblings erigontech#22165erigontech#22172.

---------

Co-authored-by: awskii <artem.tsskiy@gmail.com>
@yperbasis yperbasis marked this pull request as draft July 3, 2026 08:51
…-fixes

# Conflicts:
#	common/bytes4.go
#	common/bytes48.go
#	common/bytes64.go
#	common/bytes96.go
#	common/bytesn_test.go
@yperbasis yperbasis changed the title common, cl, db, execution, p2p, cmd: fix copy-paste drift defects cl, db, execution, p2p, cmd: fix copy-paste drift defects Jul 3, 2026
@yperbasis yperbasis requested a review from Copilot July 3, 2026 09:08
@yperbasis yperbasis marked this pull request as ready for review July 3, 2026 09:09

Copilot AI 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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Sahil-4555 pushed a commit to Sahil-4555/erigon that referenced this pull request Jul 3, 2026
…ontech#22168)

The five `GetOrCreate{Histogram,Counter,Gauge,SummaryExt,GaugeVec}`
methods repeated the same ~32-line double-checked-locking block; each
copy differed only in the constructor and type assertion. They collapse
onto `getOrCreate[T prometheus.Metric]` / `getOrCreateVec[T
prometheus.Collector]`, and each method is now a 3-liner.

The GaugeVec copy is where clone mutation had gone wrong: its type guard
compared `reflect.TypeFor[*prometheus.GaugeVec]()` to itself — always
false, i.e. dead (and unreachable while `namedMetricVec.metric` was
concretely typed `*prometheus.GaugeVec`). The generic widens the field
to `prometheus.Collector` and performs a real assertion with the same
error shape as the scalar family
(`TestSetGetOrCreateGaugeVecWrongType`). `registerMetricVec` is removed:
its re-check-and-insert was a duplicate of the inline double-checked
insert.

First commit adds characterization tests (+127 lines) pinning existing
GetOrCreate* behavior — same-instance returns, wrong-type errors,
describe-once semantics — green against the old code before the
refactor.

**Update (third commit):** `getOrCreate`/`getOrCreateVec` were
themselves clones differing only in which registry (`m`/`a` vs
`vecs`/`av`) they touched. They now delegate to a single `getOrCreateIn`
over a shared `namedEntry[M]` entry type (`namedMetric`/`namedMetricVec`
become type aliases), with the wrappers widening `T` through an adapter
closure so the store stays compile-time safe. The double-checked locking
becomes one critical section: the fast path took the same mutex anyway,
and the create funcs are pure `parseMetric` + constructor calls, so
creating under the lock eliminates the create-twice-drop-loser race. The
unlock is deferred because prometheus constructors can panic on inputs
reachable through the public API (a `quantile`/`le` const label, a
negative summary window); a non-deferred unlock would strand the set's
mutex. Also drops the dead scalar `namedMetric.isAux` — never assigned,
so the `ListMetricNames` filter on it was unreachable.

Net −169 lines in set.go. Verified: package tests incl. `-race`, `make
lint` clean, full `go build ./...`.

Part of a dedup series; siblings erigontech#22165, erigontech#22166, erigontech#22167.
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 3, 2026
… resource handlers (erigontech#22169)

The package carried two parallel MCP servers — `ErigonMCPServer`
(in-process typed APIs) and `StandaloneMCPServer` (JSON-RPC proxy) —
with independently maintained copies of the tool catalog (~330 lines
each, 264 of 270 non-blank registration lines identical), the prompts
(136 lines byte-identical), the resource registration, and the log-file
handlers. The copies had already drifted the worst way possible: the
in-process resource handlers were still placeholders (raw URI used as
the address, hardcoded `"syncing": false` and `"status": "success"`)
while the standalone copies had the real logic.

Two commits:

1. **Implement the in-process resource handlers** (TDD: failing tests
first) — real address extraction via a shared `extractURIParam`, real
`eth_syncing`, receipt-status derivation, mirroring the standalone
behavior through the typed APIs.
2. **Share the registration surfaces** — one `toolSpecs()` table
consumed via per-server `toolHandlers()` maps (`registerTools` panics on
any catalog/handler mismatch); `registerPrompts`/`registerResources` as
free functions over per-server handler sets; the four log handlers
folded into a `logTools` struct embedded by both servers (also deduping
the log-file-resolution switch that was inlined 4×).
`eth_getStorageValues` stays embedded-only.
`TestEmbeddedAndStandaloneCatalogsMatch` pins both servers to identical
tool/prompt/resource catalogs so they cannot drift silently again.

Net: −248 lines despite +~130 of new tests; standalone.go alone went
1724 → 1129 lines. Verified: package tests, scoped `golangci-lint` clean
(×2), `make erigon`.

Part of a dedup series; siblings erigontech#22165erigontech#22168.
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 3, 2026
…erigontech#22171)

`cmd/downloader`, `txnprovider/txpool`, `node/privateapi`, and
`p2p/sentry` each re-inlined the interceptor/keepalive option block that
`grpcutil.NewServer` already builds — two of them complete with its dead
commented-out metrics hooks — and all four repeated the same listen +
health-server + serve-goroutine tail.

`NewServer(rateLimit, creds)` now delegates to a new
`NewServerWithOpts(creds, extraOpts...)` (nil-safe creds, no forced
`MaxConcurrentStreams`), and `StartServer`/`StartServerOnListener` own
the listen/health/serve tail. The four sites convert onto these.

Option preservation was the review focus, per site: downloader and
txpool still run **without** `MaxConcurrentStreams` (as before); txpool
keeps its explicit `ReadBufferSize(0)`/`WriteBufferSize(0)`;
privateapi/sentry keep health registration gated on their `healthCheck`
flag while downloader/txpool keep it unconditional; existing `NewServer`
callers see byte-identical behavior.

Two deliberate exceptions to strict preservation:
- Both converted `StartGrpc`s now take
`credentials.TransportCredentials` by value (as `privateapi.StartGrpc`
already does) instead of pointer-to-interface, dropping the identical
unwrap shim at each site; their sole callers pass literal `nil` and are
unchanged.
- The txpool serve-failure log now reads `txpool gRPC server fail`
instead of the `private RPC server fail` string copy-pasted from
privateapi — in the standalone txpool daemon, `--private.api.addr` names
the remote node it connects to, so the old label pointed triage at the
wrong process. Nothing greps or asserts these strings.

Otherwise no behavior change — existing tests are the safety net
(txpool, privateapi, sentry suites green). Full `make lint` clean, `make
erigon integration` build. Net −117 lines.

Part of a dedup series; siblings erigontech#22165erigontech#22170.
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 4, 2026
Helper extractions collapsing the package's most-repeated scaffolds.
Behavior-preserving with the compat quirks kept byte-identical, except
two deliberate changes called out below.

1. **Txn lookup + index derivation ×8** — two helpers rather than one
bundle, because two sites must keep their inline bor fallbacks
(`GetTransactionReceipt` runs its prune/index checks with the
pre-fallback block number — live pruned-bor-node behavior; debug
`GetRawTransaction` early-returns on `!ok` before its bor branch).
`txnLookupWithBorFallback` covers the four sites whose fallback triggers
on a missed lookup — the miss contract of both `TxnLookup`
implementations is verified `(0,0,false,nil)`, and block 0 has no txns,
so `GetTransactionByHash`'s historical `blockNum==0` trigger is
equivalent to `!ok`. `txnIndexInBlock` owns the txNum→index math at all
8 sites with the exact underflow error, called at each site's original
position so error precedence is unchanged; for bor state-sync txns it
returns the established `-1` sentinel (the value the block readers guard
and trace_filtering decodes).
2. **`getReceiptsWithBor` ×3** — returns `(receipts, borReceipt)` rather
than appending (appending would break the marshal loops'
`Transactions()[TransactionIndex]`); per-site marshal flags
(`withBlockTimestamp` etc.) stay exact. Its events→`GenerateBorReceipt`
tail is `borReceiptForBlock`, also reused by `GetTransactionReceipt`'s
bor state-sync path (nil receipt ⇔ zero events — `GenerateBorReceipt`
never returns `(nil, nil)` — mapped back to that site's "tx not found").
3. **Log range resolution** — erigon_receipts' byte-identical
latest-only pair extracted into `logRangeLatestOnly`, with its
FromBlock/ToBlock twin blocks collapsed into `resolveLogBound` (the
begin=0-on-latest quirk lives in the per-bound default); overlay's
`getBeginEnd` promoted to `BaseAPI.resolveLogsRange` with a
`checkFuture` flag (eth_getLogs keeps its inline into-the-future
rejections — deferring them would change which error surfaces). The two
variants are deliberately NOT merged.
4. **`subscribeRPC[T]` ×5** in eth_filters — the full
guard/CreateSubscription/LogPanic-goroutine/unsubscribe/select skeleton,
checking channel closure before notify; notify callbacks receive a
pre-bound `emit` func so the Notify+Warn boilerplate lives once in the
skeleton (all call sites used the identical warn message).
`NewPendingTransactionsWithBody` delegates to the same core as
`NewPendingTransactions` (the only deltas are fullTx and channel buffer
256 vs 512 — both parameterized).
5. **Replay-env helpers** — the inline BLOCKHASH closures reuse the
pre-existing `transactions.MakeBlockHashProvider` instead of gaining a
new helper (safe: both `CanonicalHash` impls return the zero hash on
miss; where a timeout applies, the provider binds the deadline-carrying
ctx so hash lookups run under the EVM deadline); `validateBundles` ×2
(pinned by `TestCallManyEmptyBundles` for both callers);
`setupEVMTimeout` ×6 (CallBundle, CallMany, overlay `replayBlock`,
`simulateCall`, trace_call, trace_rawTransaction), returning the
deadline ctx, a `store` func for the current EVM, and a cleanup that
preserves the stop-before-cancel order. The trace doCallBlock/doCall
stacks and trace_filtering's parallel per-tx tracer keep their own
shapes — different cancellation mechanisms (cooperative ctx checks; many
concurrent EVMs).

Two deliberate behavior changes:

- The canonical-hash-miss log line is unified on the shared provider at
Debug (message `[evm] canonical hash not found`): the four jsonrpc sites
keep their historical Debug level, and the `eth_simulateV1` path drops
from Error — the miss is user-triggerable (e.g. `eth_callMany` bundles
advance `BlockNumber` past head, so BLOCKHASH near the simulated
boundary legitimately misses), geth logs nothing there, and genuine read
errors still propagate to the RPC response.
- EVM-timeout cancellation is now airtight: `setupEVMTimeout`'s `store`
re-checks `ctx.Err()` after every store, so a deadline or client
disconnect firing before — or between — EVM (re)creations still cancels
the current instance (previously CallBundle/CallMany could drop a
one-shot cancellation that fired between loop iterations, leaving the
call running with no timeout). Pinned by
`TestSetupEVMTimeoutCancelsEVMStoredAfterExpiry`, verified red against a
plain store.

New tests: `TestGetRawReceipts` (previously untested debug site),
`TestOverlayGetBeginEnd` (previously untested overlay path), a
guard-behavior test across all five subscription methods,
`TestCallManyEmptyBundles`, and the timeout test above. Every pinning
test was run green against the unconverted code first.

Net ≈ −284 production lines, +174 of tests. Verified: `go test
./rpc/...` green (plus a `-race` pass over the timeout-helper tests),
scoped `golangci-lint` clean (×2), full `make lint` clean, `make
erigon`.

Part of a dedup series; siblings erigontech#22165erigontech#22179.
Sahil-4555 pushed a commit to Sahil-4555/erigon that referenced this pull request Jul 7, 2026
…rplate with generics (erigontech#22166)

The sentry multiplexer repeated the same errgroup fan-out/collect
scaffold in 16 methods (~350 duplicated lines), and `node/direct`
carried two hand-monomorphized copies of libsentry's own generic stream
adapters. This collapses both:

- `fanOut[R]` — protocol-gated concurrent call with a mutex-guarded
collect that can cancel the group; `fanOutSuccess[R]` — the GetSuccess
OR-reduction used by the four peer-admin methods; `streamFanIn[T,S]` —
the Messages/PeerEvents stream fan-in.
- `StreamReply`/`SentryStreamS[T]`/`SentryStreamC[T]` moved to
`libsentry/stream.go`; the `SentryMessagesStream*`/`SentryPeersStream*`
copies in `node/direct/sentry_client.go` are deleted (no external
references).
- Before refactoring, a characterization test pins the
any-client-success semantics of
AddPeer/RemovePeer/AddTrustedPeer/RemoveTrustedPeer (16 subtests), green
against the old code first.

Preserved deliberately: `PeerById`'s errFound early-stop,
`SetStatus`/`peersByClient` protocol gating, `HandShake`'s per-client
protocol caching and max-reduction, and `SendMessageByMinBlock` stays
serial (untouched, along with the other send-path methods).

Net −218 lines including +99 of new tests. Verified: `go test
./p2p/sentry/... ./node/direct/...` plus `-race` on the sentry packages,
scoped `golangci-lint` clean (×2), `make erigon`.

Part of a dedup series; sibling defect fixes in erigontech#22165.
@AskAlexSharov AskAlexSharov added this pull request to the merge queue Jul 8, 2026
Sahil-4555 pushed a commit to Sahil-4555/erigon that referenced this pull request Jul 8, 2026
…ities (erigontech#22170)

Four independent consolidations of copy-pasted utility code, plus review
follow-ups:

1. **bufio pools ×4 → `db/bufiopool`.** `getBufioWriter/putBufioWriter`
(+reader twins in two of them) were byte-identical — including comments
— in `db/etl`, `db/recsplit`, `db/seg`, and `db/datastruct/btindex`. One
package now owns them; 512KB size and Reset(nil)-before-Put semantics
preserved verbatim.
2. **keccak pools ×2 → one.** `common/hasher.go` and
`common/crypto/crypto.go` each pooled `keccak.NewFastKeccak()`. The
single pool of raw `KeccakState` lives in `common` (import direction
forces it); `crypto.NewKeccakState`/`ReturnToPool` delegate.
Reset-on-Get semantics identical on both old paths; no exported
signature changed. The no-longer-pooled `Hasher` wrapper is free in
practice: `NewHasher` now inlines (the old pooled version exceeded the
inlining budget) and `&Hasher{}` does not escape at any call site, so no
per-call heap allocation appeared.
3. **bytes.Buffer pools ×8 → `common/pool`.** Eight private
`sync.Pool{New: …&bytes.Buffer{}}` copies across cl/persistence,
cl/antiquary, db/snapshotsync, execution/types, and node/rpcstack (gzip
responses) replaced by `GetBuffer`/`PutBuffer`; Put drops buffers over
1MB so one oversized use can't pin memory (the rpcstack copy already
used exactly this cap; the others had none). Call sites that routinely
exceed 1MB on mainnet no longer use a pool at all: the CL read paths
(historical-states dumps/diffs, beacon-block decompression) only wrapped
an in-memory slice to read it back and now feed `bytes.NewReader`
straight to the zstd reader — zero-copy, cheaper than the old uncapped
pools, which copied the whole blob per call — and the antiquary's
multi-MB diff writer reuses the collector-owned buffer like its sibling
helpers. The pool's remaining users are typically-sub-1MB write targets.
Reset-on-get and drop-oversized semantics are unit-tested; a dead
`plainBytesBufferPool` in base_encoding removed along the way.
4. **rpc/requests: retryConnects → cenkalti/backoff/v4** (already a
dependency). Per-attempt 500ms and overall 20s budgets and dial-error
propagation preserved, pinned by characterization tests written and run
green against the old implementation first. Two error-swallowing bugs
fixed on top, pinned by red-first tests: overall-deadline expiry after
only per-attempt timeouts now returns `context.DeadlineExceeded` instead
of nil (callers used to see success with an unpopulated result); and on
overall-deadline expiry the last dial error is substituted only for the
context's own deadline error, never for a permanent error that merely
wraps `DeadlineExceeded`. Because `backoff.Retry` surfaces the overall
context's error verbatim when it gives up, the substitution keys off
`err == context.DeadlineExceeded` (identity) rather than `errors.Is`, so
a `backoff.Permanent` error that wraps `DeadlineExceeded` is left
intact.

Net −146 production lines plus +180 of tests. Re-verified after merging
latest `main`: `go test` on every touched tree, `make lint` clean, `go
build ./...`, `make erigon integration`.

Part of a dedup series; siblings erigontech#22165erigontech#22169.
Merged via the queue into main with commit 6409423 Jul 8, 2026
92 checks passed
@AskAlexSharov AskAlexSharov deleted the yperbasis/dedup-audit-fixes branch July 8, 2026 04:20
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 8, 2026
…ts (erigontech#22172)

The two caplin snapshot containers (`freezeblocks.CaplinSnapshots`,
`snapshotsync.CaplinStateSnapshots`) re-implemented `RoSnapshots`'
OpenList machinery — the find-open-segment walk, the optimistic-open
error dance (duplicated *twice more* inline across
`CaplinSnapshots.OpenList`'s two identical switch arms), and
`closeWhatNotInList`.

The copies had drifted in unsafe ways, which is the real payoff here:

- `CaplinSnapshots`' close path called the embedded
`Decompressor.Close()` instead of `DirtySegment.close()`, so **recsplit
index handles leaked** on every file-list change;
- stale unopened stubs (`Decompressor == nil`) were skipped rather than
removed; to make that safe, `CaplinStateSnapshots` now derives the
protect-list name from its `filePath` field instead of the promoted
`Decompressor.FilePath()`, which would nil-panic on such a stub (pinned
by `TestCaplinStateCloseWhatNotInListDropsUnopenedStub`);
- both caplin close paths now inherit the generic **refcount guard**
(don't close a segment a live `View`/`RoTx` still references; contract
pinned by `TestCloseAndDropNotProtected`). Caveat: the guard is inert
for the caplin containers today — they mark every segment `frozen`, and
`VisibleSegments.BeginRo` doesn't refcount frozen segments — so this is
future-proofing rather than an active fix, and the pre-existing
close-under-live-view hazard for frozen segments is unchanged by this
PR.

This extracts `FindOpenSegment` / `ClassifyOpenErr` /
`CloseSegmentsNotInList` in snapshotsync, converts `RoSnapshots` and
both caplin containers onto them, collapses the two identical switch
arms into one loop, and deletes the vestigial `processed` flag with its
stray "Only bob sidecars count" comment (semantics preserved:
`segmentsMax` still tracks BeaconBlocks only in `CaplinSnapshots`;
`CaplinStateSnapshots` tracked it for every file and still does).

Deliberately out of scope: making the caplin containers embed
`RoSnapshots` wholesale; un-freezing caplin segments so the refcount
guard actually engages for their views (a refcount-contention trade-off
deserving its own PR).

Behavior changes (all safety-directional, called out above): full
index+seg close, stub removal (incl. the nil-panic fix), refcount guard
on caplin close paths (inert today). Everything else is
behavior-preserving; snapshotsync/freezeblocks suites green, scoped
`golangci-lint` clean (×2), `make erigon` builds. Net −120 lines of
non-test code (−50 including the two new tests).

Part of a dedup series; siblings erigontech#22165erigontech#22171.
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