polygon: delete the Polygon tree, tables, protos and chain-config hooks - #23497
Conversation
…ocs and QA workflow
…e from the Polygon cleanup
…emoval-db-storage
…removal-chain-config
…n-removal-delete-tree # Conflicts: # cmd/utils/app/init_cmd.go
# Conflicts: # cmd/downloader/main.go # cmd/integration/commands/stages.go # node/eth/backend.go
# Conflicts: # cmd/integration/commands/stages.go
…ng' into HEAD # Conflicts: # cmd/integration/commands/stages.go # cmd/rpcdaemon/cli/config.go # cmd/utils/app/init_cmd.go # cmd/utils/app/snapshots_cmd.go
…removal-chain-config
…ee' into awskii/polygon-removal-delete-tree
…n-removal-delete-tree
…ng them behind the witness rename
|
4-agent review over the incremental range ( Major — the renamed witness tables had no schema migration.
Fixed by Worth stating explicitly, since it is the obvious question on a PR that removes Polygon: the tables are renamed rather than deleted because they belong to the WIT side-protocol, not to Bor. Minor, fixed — Minor, fixed — Minor, open — the Also swept the tip for anything the series missed and dropped five stale references in a161860: the |
…ee' into awskii/polygon-removal-delete-tree
Resolves the fallout of the Polygon removal series (#23492..#23497) on this branch: the Bor-specific gating added in earlier review rounds goes away with the code it gated. borReceiptForBlock, borStateSyncLogs and txnLookupWithBorFallback are gone with the bridge, GetTransactionReceipt is back on the plain txnLookup, the Bor clamp in Capabilities is dropped with chain.Config.Bor, and the Bor gating tests go with their production paths.
) Polygon has not been officially supported since 3.1, but the 3.6 docs still offer it as a choice. This scopes it without removing the reference material, because the bor code does still ship in this branch — the `bor-mainnet` and `amoy` tags are selectable, the `bor` namespace is servable, and all seven `--bor.*` / `--polygon.*` flags are registered in `erigon --help`. **Presented as a choice, now not:** - Polygon sat in the Mainnets table beside Ethereum and Gnosis, with its caveat a footnote 25 lines below. It moves to its own "Polygon (not supported)" section, which absorbs the Amoy tag. - Three cards advertised a one-command Polygon easy-node setup. That guide was removed from this branch and `/get-started/easy-nodes/how-to-run-a-polygon-node` is now a redirect, so the cards promised something that no longer exists. - The FAQ answered "supports networks such as Gnosis and Polygon". That string is in `faqSchema`, so it ships as JSON-LD. **Kept, and scoped instead:** the `bor_` method reference, the Polygon gRPC services, and the seven flags — all accurate for 3.6. The flags move under a "Legacy Polygon flags" heading so the CLI reference stays complete against `erigon --help`, and the bor namespace entry and the gRPC section point at Supported Networks. **Two fixes along the way:** the help center cited `--bor.heimdall.url`, which is not a flag (the real one is `--bor.heimdall`); that example now uses `--externalcl`. The Layer 2 page description advertised "Polygon PoS, Bor" on a page whose body is entirely about running an op-node. `main` already dropped all of this along with the code (#23492, #23497), so nothing here forward-ports. `llms.txt` / `llms-full.txt` regenerated; `--check` and `npm run build` pass. --------- Co-authored-by: Bloxster <gianni.morselli@erigon.tech>
…23322) Defects in the same family — how receipt- and log-serving endpoints decide availability. > **Note:** while this PR was under review, main removed Polygon support entirely (#23492..#23497). The Bor-specific fixes made in earlier review rounds are therefore no longer part of this PR — the history gate inside `borReceiptForBlock` and `borStateSyncLogs`, the `txnLookupWithBorFallback` resolution in `eth_getTransactionReceipt`, the Bor clamp on the receipts capability, and the four Bor gating tests were retired by the merge (34f460e) together with the code they gated. Non-Bor behaviour is untouched: those helpers were wrappers, and their callers now use the generic functions they wrapped (`getReceipts`, `txnLookup`). **1. The receipts gate consulted the wrong setting.** It read `kvcfg.PersistReceipts`, a boolean saying the receipt cache exists on disk, and concluded receipts were available for every block. Retention is a separate setting: `RCacheDomain` is retired on its own `--prune.receipts.distance` window when one is set, and alongside state history otherwise, which is the default. It now consults `prune.Mode.ReceiptsAmount()`, distinguishing the same three shapes `historyRetireCutoffs` already distinguishes. Where the cache stops covering a block, availability falls back to state history, because a missing cache entry is not fatal: `ReadReceiptCacheV2` reports it as absent and `GetReceipt` re-executes the block from `ReceiptDomain` plus state history, retired at the history cutoff rather than the RCacheDomain one. So a `--prune.receipts.distance` narrower than the history window costs the caller nothing. **2. Four endpoints were gated on receipts alone, though matching logs can need more.** `eth_getLogs` and `erigon_getLogs` search `LogAddrIdx` and `LogTopicIdx` when the query filters by address or topic; those are standalone inverted indices retired at the history cutoff whatever the receipt retention is, so gated on receipts they answered with an empty log array instead of `PrunedError`. They now take the history gate on top, but only when the filter actually reaches the indices: `usesLogIndex` mirrors `applyFiltersV3`, which skips topic positions that are empty because those match any topic — so `"topics": [null]` searches nothing and stays on the receipts path, exactly like the unfiltered form. `erigon_getLatestLogs` re-executes through a `TraceWorker` and `overlay_getLogs` re-executes with overridden code, so both take the history gate unconditionally. **3. Ten endpoints serving the receipts of one block were gated on history alone**, which both rejects blocks whose body and receipts are present and never checks the body at all. New `checkBlockReceiptsAvailable` composes the two boundaries: reading a stored receipt needs the block body anyway, since the receipt carries no `TxHash` and it is derived from the transaction. The two GraphQL block-detail entry points had no gate; theirs is placed before the body read, because after it a pruned body reads back as nil and the endpoint answers "not found" before any gate could fire. The GraphQL and Otterscan block-detail paths also select one overlay view per request and thread it through resolution, gate, block and receipt reads, so a background commit cannot gate one generation while another answers. **4. The log path needs the block body for the same reason.** `getLogsV3` reads the transaction through `TxnByIdxInBlock` for every txNum it does not find in the in-memory cache, because `GetReceipt` derives the receipt from it. With bodies pruned and receipts kept — `minimal` with `--prune.receipts.distance=keep-all` — the lookup came back empty and the loop skipped the block silently, answering with an empty array. New `checkLogsAvailable` composes the three boundaries and replaces the duplicated gate block in the two `getLogs`, so the rationale is stated once. **5. A block with no transactions could not have its receipts read at all.** `GetReceipts` decided whether the cache had answered by testing `len(receiptsFromDB) > 0`, which cannot tell "the cache holds nothing for this block" from "this block has no transactions", so an empty block fell through to `PrepareEnv` and its state-history read. Where history is pruned, `eth_getBlockReceipts`, `debug_getRawReceipts`, `ots_getBlockDetails` and `ots_getBlockTransactions` answered with a leaked `ReceiptsGen: PrepareEnv: ... old data not available` instead of an empty list. The fix belongs in the generator rather than the gate: the gate decides availability from retention, and a block it lets through must not leak an execution error. Availability itself is a separate question, and the gate makes no exception for these blocks — a transaction-free block below the cutoff is refused like any other, and the caller gets `PrunedError` rather than an empty list that cannot be told apart from "no receipts". So this fix covers the blocks the gate serves: receipts retained, archive, or above the cutoff. The two boundaries are the whole gate, so `oldestBlock` in `eth_capabilities` is the exact boundary the endpoints honour rather than a floor with holes below it. **6. The blocks gate read the chain-history-expiry sentinel as "nothing is pruned".** `KeepPostMergeBlocksPruneMode` is a policy, not a window: on a chain declaring a merge point, pre-merge transaction segments are never downloaded, so `checkPruneBlocks` now refuses below `MergeHeight`, and `eth_capabilities` resolves its `blocks` boundary through the same function, so the two cannot diverge. A legacy archive datadir persists the same blocks sentinel, so the datadir decides rather than the stored retention — and neither a pre-merge body nor the oldest available block can: expiry keeps pre-merge headers and bodies, and the transaction segment spanning the merge point reaches below it. The gate reads a pre-merge transaction, sampled by halving the range so the candidates stay clear of the transaction segment spanning the merge point; where the chain carries no pre-merge transaction at all, the cumulative txnum position of the last pre-merge body says so and nothing is left to be missing. The verdict is availability rather than policy, so it is cached for a short TTL in both directions instead of being settled once, and availability widening while segments arrive reopens the gate within that window. The remote rpcdaemon's block reader answers `FrozenBlocks` through a new ethbackend RPC instead of panicking, so the receipt gates, `eth_capabilities` and the receipt generator keep consulting the real value everywhere. **7. `erigon_getLogsByHash` consulted its receipt cache before any gate.** A receipt set cached while its block was inside the retention window stayed servable after the boundary moved past it — holes below the advertised `oldestBlock`, the defect class this PR removes. The gate now runs before the cache, so a hit is gated like a miss, at the cost of one read-only tx on cache hits. This covers the audit asked for in point 2 of #22260 for `erigon_receipts.go` and `otterscan_block_details.go`: migrated to the correct boundary rather than documented as genuinely needing history. ## Coverage `prune_gating_test.go` pins **34 endpoints across the nine prune mode shapes** on an old and a recent block: 612 cells. `check_prune_gates_test.go` covers the gates directly — the boundary block itself, the boundary named in each error, the archive short circuit, each receipt retention shape including a window wider and a window narrower than history, each leg of both composed gates, and `usesLogIndex` over the criteria shapes. `checkTxFee` gains the unit test it never had, so every `check*` in the package now has one. It also pins the datadir shapes behind the blocks sentinel — aligned segments, bodies without transactions, a chain with no pre-merge user transaction, a sampled block without transactions, blocks arriving later, and a stored history retention that does not change the verdict. The remote block reader's frozen-block refresh is covered directly: a failed fetch is retried instead of being served as fresh, and a slow one delays only the goroutine that fetches. ## Verified on a live node Sepolia at tip with `--prune.mode=minimal --prune.include-receipts` — receipts on disk, block bodies pruned. Head 11500055, both boundaries at 11400054, old block probed 5750027. **35 checks, all passing.** | Family | Endpoints | Old block | Why | |---|---|---|---| | headers | `erigon_getHeaderByNumber`, `debug_getRawHeader` | served | headers are never pruned | | block data | `eth_getBlockByNumber`, `eth_getBlockTransactionCountByNumber`, `eth_getUncleCountByBlockNumber`, `debug_getRawBlock` | refused | bodies are pruned in `minimal` | | receipts of one block | `eth_getBlockReceipts`, `debug_getRawReceipts`, `erigon_getBlockReceiptsByBlockHash`, `erigon_getLogsByHash`, `ots_getBlockDetails`, `ots_getBlockTransactions` | refused, naming `blocks are available` | the blocks leg this PR adds — receipts are present, the body is not | | logs by filter | `eth_getLogs`, `erigon_getLogs` | refused, naming `history is available` | log indices follow history, not receipts | | state / trace | `eth_getBalance`, `trace_block`, `debug_traceBlockByNumber`, `ots_hasCode` | refused | history is pruned | This table was measured before the log gate took the blocks leg described in point 4. On the current revision its two log rows refuse on the blocks boundary rather than the history one, because `minimal` prunes bodies and that leg is checked first; the rows above and below are unaffected. The blocks leg is covered end to end by the unit tests, not by a live probe. Every recent-block probe is served. The two middle rows name **different boundaries** for the same block, which is what this configuration shows: before the change both said `history is available`. Note that in `minimal` the receipt endpoints refuse either way, so only the named boundary changes. The behavioural flip — refused becoming served — happens where bodies are kept and receipts outlive history (`blocks` with `--prune.receipts.distance=keep-all`), which is the largest group of the 26 red cells above. ## Second live node: `blocks` with receipts kept Sepolia at tip with `--prune.mode=blocks --prune.include-receipts --prune.receipts.distance=keep-all` — every body and every receipt on disk, state history pruned below block 11238830. Old block probed: 5750484, some 5.5M blocks below the history boundary. | Family | Endpoints | Old block | Why | |---|---|---|---| | headers | `erigon_getHeaderByNumber`, `debug_getRawHeader` | served | never pruned | | block data | `eth_getBlockByNumber`, `eth_getBlockTransactionCountByNumber`, `eth_getUncleCountByBlockNumber`, `eth_getTransactionByHash` | served | bodies are kept | | **receipts of one block** | `eth_getBlockReceipts`, `debug_getRawReceipts`, `eth_getTransactionReceipt`, `erigon_getBlockReceiptsByBlockHash`, `erigon_getLogsByHash`, `ots_getBlockDetails`, `ots_getBlockTransactions` | **served** | body and receipts are both there — on `main` all seven refuse | | **logs, unfiltered** | `eth_getLogs`, `erigon_getLogs` | **served**, 125 logs | read straight from receipts; no index is consulted | | **logs, filtered by address** | `eth_getLogs`, `erigon_getLogs` | **refused**, naming `history is available` | the index search needs history — on `main` both return an empty array | | state / trace | `eth_getBalance`, `trace_block`, `debug_traceBlockByNumber`, `ots_hasCode` | refused | history is pruned | | block data, follow-up | `debug_getRawBlock`, `debug_getRawTransaction` | refused | still gated on history; see Follow-up | This is the configuration where the change is behavioural rather than a difference in wording. `eth_getBlockReceipts` returns 101 receipts for a block whose state history is long gone, `ots_getBlockDetails` computes its `totalFees`, and `erigon_getLogsByHash` returns all 101 log positions — none of which `main` will answer. In the other direction a filtered `eth_getLogs` now refuses instead of quietly returning `[]`, while the unfiltered form still answers with its 125 logs. 36 of 38 checks pass; the two failures are `debug_getRawBlock` and `debug_getRawTransaction`, whose fix is in the follow-up PR. ### Topic shapes on the same node The empty-topic-position case, probed on block 5750932 of the same datadir with an rpcdaemon opened read-only: | filter | before | after | |---|---|---| | no `topics` | 1117 logs | 1117 logs | | `"topics": []` | 1117 logs | 1117 logs | | `"topics": [null]` | `PrunedError` | 1117 logs | | `"topics": [[]]` | `PrunedError` | 1117 logs | | `"topics": [null, null]` | `PrunedError` | 1113 logs | The last row keeps 1113 rather than 1117 because that shape requires two topic positions to match, so it drops the four logs carrying a single topic; the filter is working, the gate is not involved. A query filtered by address still refuses naming `history is available`, and the single-block receipt endpoints stay served, so the blocks leg added in point 4 introduces no false refusal where bodies are kept. ## Gate boundaries measured against the data Each gate names a boundary; a second `rpcdaemon` built with `checkPruneField` short-circuited to `nil` says where the data actually starts. Both were opened read-only on the same Sepolia `blocks` datadir — head 11501094, declared boundary 11238950 — and the first block that answers correctly was found by bisection, with a block's own receipts as the ground truth for what its log endpoints must reproduce. | gate | endpoints | declared | first block that answers | verdict | |---|---|---|---|---| | `checkPruneHistory` | `eth_getBalance`, `eth_getCode` | 11238950 | 11163680 | conservative by 75270 | | `checkPruneHistory` | `trace_block`, `debug_traceBlockByNumber` | 11238950 | 11163681 | conservative by 75269 | | index leg of `checkLogsAvailable` | `eth_getLogs`, `erigon_getLogs` filtered | 11238950 | 11163681 | conservative by 75269 | | `checkBlockReceiptsAvailable` | `eth_getBlockReceipts`, `debug_getRawReceipts`, `ots_getBlockDetails`, `ots_getBlockTransactions` | never refuses | complete at every block with transactions from 800000 to the tip | exact | | receipts leg of `checkLogsAvailable` | `eth_getLogs`, `erigon_getLogs` unfiltered | never refuses | correct at every block with logs | exact | The 75k-block gap is not a defect: file retirement is floored to the file step, so data survives below the declared boundary until the next prune pass removes it. The gate promises the boundary the configuration guarantees rather than the physical one that moves at every retire, which is the pre-existing behaviour of `checkPruneField`. The index leg is where the change earns its place. Filtering the same block by an address taken from its own logs: | block | gate removed | gate in place | |---|---|---| | 5750932 | 0 of 1 expected log | refused | | 11163680 | 0 of 1 expected log | refused | | 11163679 | 0 of 1 expected log | refused | | 11238949 | 35 logs, correct | refused | | 11238950 | 116 logs, correct | 116 logs | Below the physical floor the filtered query answers **silently zero**, which is what the history leg turns into `PrunedError`. The physical floor of the log index, 11163681, coincides with that of state history, 11163680 — the premise this PR rests on, measured rather than assumed. The unfiltered form is correct at every block probed: 1117, 1016, 1360, 525, 443, 1237 and 216 logs, each matching its receipts, identical with and without the gate. Point 5 was found the same way: blocks 1000, 1000000, 1510087 and 2000000 go from the `PrepareEnv` error to an empty list, while blocks carrying transactions are unchanged — 800000 keeps 1 receipt, 1511431 keeps 4, 5750932 keeps 99, 11501058 keeps 94. The blocks leg is the one this datadir cannot exercise, since it keeps every body; it stays covered by the unit tests. ## Follow-up The endpoints that need the block body but not the receipts are left to a second PR: `debug_getRawBlock`, `debug_getRawTransaction` and `erigon_getBlockByTimestamp` still gate on history, so under `--prune.mode=blocks` they refuse bodies that were never pruned — the defect class #21965 reported. That PR also gates `eth_feeHistory`, which has none today, and completes the table with the header, trace and Otterscan search endpoints.
Eight docs changes landed on release/3.6 between 2026-08-20 and 08-28 and were never forward-ported. Each claim here was re-derived from main's own source rather than carried over, and three of them turned out to be false on main. Corrected against main rather than ported: - eth_getFilterLogs no longer resets a filter's eviction deadline. #23296 rewrote it to read the stored criteria and serve them through the eth_getLogs path, which dropped the TouchSubscription call that release/3.6 still makes. On main only eth_getFilterChanges counts as a poll, so a client polling exclusively with eth_getFilterLogs loses its filter after five idle minutes. - BlockNumberOrHash.UnmarshalJSON has a top-level "latestExecuted" case on main, so the bare string works on eth_call and friends. On release/3.6 only the object-wrapped form does. "null" is still top-level-only on plain block-number parameters. - eth_fillTransaction rejects three inputs on main that release/3.6 accepts: gasPrice together with authorizationList, an empty authorizationList, and a derived maxFeePerGas or maxFeePerBlobGas that overflows 256 bits. Also adapted: the gasBailOut burn-contract note drops the Bor chains and the replay-path note drops Bor state-sync transactions, both gone with the Polygon tree in #23497. Ported after verifying the behaviour is unchanged on main: - The state-cache environment variables. The four STATE_CACHE_* budgets, USE_STATE_CACHE and DISABLE_ADAPTIVE_PIN carry the same defaults here, and the "new in v3.6" comparisons check out against release/3.5 (accounts 1GB -> 150MB, code index 16MB -> 32MB, no pin controller). - eth_getWitness and eth_getTxWitness, including the genesis and empty-access-set early returns that skip the root self-check, and the int(txIndex) narrowing. - trace_block and trace_replayBlockTransactions withdrawals, and what gasBailOut does to replayed balances. - Caplin block production since v3.6: payload preparation one slot ahead, the head published before the head-state copy, and the default client-pair graffiti. payload_preparation.go is identical on the two branches. - Caplin no longer claimed to remove disk storage -- it keeps an indexing DB, its own snapshots and further subdirectories under the same datadir. What it removes is the second process and second datadir. - --prune.include-receipts is sticky per datadir, and on its own does not reach genesis: the receipt cache follows the state-history window, and keep-all retains the cache domain but not the log address and topic indexes a filtered eth_getLogs needs. - --externalcl is a switch, not an address. It disables Caplin, after which the external client dials in to the Engine API. The llms.txt cross-link on why-using-erigon is deliberately left out -- it belongs to #23336.
…rigontech#23595, erigontech#23625 to main (erigontech#23676) Ports five docs PRs merged to `release/3.6` between 2026-08-20 and 08-28. Every claim was re-derived against `main`'s own source rather than copied across — three did not survive that check, and two needed adapting. | Ported | Brings | | --- | --- | | erigontech#23359 | The state-cache environment variables | | erigontech#23587 | Caplin block production since v3.6, and the disk-storage claim in the Caplin intro | | erigontech#23593 | Pruning Modes: receipt-cache stickiness, `keep-all`, snapshot reclaim | | erigontech#23595 | `eth.md` JSON-RPC deviations and `trace.md` withdrawals / `gasBailOut` | | erigontech#23625 | The `--externalcl` correction and the Layer 2 page description | ### Written differently here, because release/3.6 is wrong for main - **`eth_getFilterLogs` no longer resets a filter's eviction deadline.** erigontech#23296 rewrote it to read the stored criteria and serve them through the `eth_getLogs` path, dropping the `TouchSubscription` call `release/3.6` still makes. A client polling only with `eth_getFilterLogs` loses its filter after five idle minutes. This documents it as it is, but it reads like an unintended side effect of erigontech#23296 rather than a deliberate change. - **`BlockNumberOrHash.UnmarshalJSON` has a top-level `"latestExecuted"` case on `main`**, so the bare string works on `eth_call` and friends. On `release/3.6` only the object-wrapped form does. - **`eth_fillTransaction` rejects three inputs on `main`** that `release/3.6` accepts: `gasPrice` with `authorizationList`, an empty `authorizationList`, and a derived `maxFeePerGas` or `maxFeePerBlobGas` that overflows 256 bits. - **The state caches do not start at a flat 1024 entries.** Start is `max(1024, shards × 16)` bounded by the ceiling, with the shard count following `min(ceiling/64, GOMAXPROCS × 16)` rounded to powers of two. `minShardStart` does not exist on `release/3.6` at all. ### Adapted for the Polygon removal (erigontech#23497) The `gasBailOut` burn-contract note drops the Bor chains, and the replay-path note drops Bor state-sync transactions. ### Verified unchanged, then ported as written `eth_getWitness` / `eth_getTxWitness` including the genesis and empty-access-set early returns; the withdrawals `stateDiff` shapes; Caplin payload preparation, head publication and default graffiti (`payload_preparation.go` is byte-identical across the branches); the `--prune.include-receipts` stickiness warning; and the `STATE_CACHE_*` defaults, whose "new in v3.6" comparisons check out against `release/3.5`. The llms.txt cross-link on `why-using-erigon` is deliberately left out — it belongs to erigontech#23336. Follows erigontech#23675. Gate: `npm ci && npm run build` green, `generate-llms.py --check` OK (72 pages), `render-disk-sizes.py --check` OK, editorial scan and `sidebar_position` lint clean. --------- Co-authored-by: Bloxster <gianni.morselli@erigon.tech>

Nothing outside
polygon/referenced it after #23495, so this deletes the tree and the hooks that existed to serve it. Last in the Polygon removal series, branched off #23495 — review the last commit only.erigonno longer builds, reads or serves anything Polygon. Existing Polygon chaindata is rejected with a pointer to0xPolygon/erigonrather than loaded with its consensus settings silently dropped.Changes
polygon/— 17 packages, ~20k lines of Go and 25.7 MB of Heimdall test fixturesexecution/chain: removeConfig.Bor,Config.BorJSON, theBorConfiginterface,BorRules, and the Bor arms ofString(),getEngine()andSecondsPerSlot()db/kv: remove the 13 remainingBor*tables, the deadBorTablesCfg, and theHeimdallDB/PolygonBridgeDBlabels with their table configskv.BorWitnesses/kv.BorWitnessSizestokv.Witnesses/kv.WitnessSizesand gate the witness buffer on--wit-protocolinstead of the chainsnaptype.MinBorEnumbecomesMaxCaplinEnum, same value, so the on-disk enum numbering is unchangedremote/bor.proto, its generated files, and theBorTxnLookup/BorEventsmethods onETHBACKEND; regenerate; bumpEthBackendAPIVersionto 4.0.0MimetypeBor,BorValidateHeaderTime, the Bor block-end receipt recovery in the serial and custom-trace executors, and the last Bor branches inSysCallContractandgenesiswritego mod tidydropsgo-merkle,ttlcacheandarc/v2, which only Bor usedbor.*/polygon.pos.*flag reference; regeneratellms*.txtThe witness protocol
The
wit/0protocol read and wrotekv.BorWitnesses, so deleting those tables would have disabled it outright rather than removing something Polygon-specific. The protocol itself has no Polygon logic — only its table names and its buffer'schainConfig.Bor != nilgate did. The tables are renamed and the buffer now follows--wit-protocol, which is what the flag's name already implied.That gate change is the one behaviour change here, and it is forced: there is no Bor config left to gate on. Witness ingestion is now reachable on any chain when the flag is set. No supported chain ever wrote those tables, so the rename orphans no data. If witness ingestion should stay off by default on Ethereum, that is a follow-up on the flag, not on this deletion.
Left for a follow-up
rules.EngineReader.GetPostApplyMessageFuncnow returns nil from every implementation — Bor's fee-transfer logging was the only real one. Removing the hook touches four engines and two call sites in the parallel and serial executors, which does not belong in a deletion this size..github/workflows/scripts/test_report/generate-test-report.tsanddashboards/keep their Polygon handling: both render historical QA results, and stripping the mappings would break display of past runs.