chore(scripts): #2247 add a regenerable trace-method availability probe - #2277
chore(scripts): #2247 add a regenerable trace-method availability probe#2277edycutjong wants to merge 2 commits into
Conversation
…ility probe Refs KeeperHub#2247. Complements KeeperHub#2273, which is the survey; this is the tool that keeps its table checkable. Deliberately does not duplicate that document. KeeperHub#2273 and this probe were written independently and in parallel, reached the same headline conclusion (plasma and tempo serve traces; the established EVM mainnets do not), and disagree on one number in a way worth keeping: plasma-mainnet, 5-tx block debug_traceBlockByNumber trace_block KeeperHub#2273 63 KiB 101 KiB this probe 348 KB 562 KB The ots_getBlockTransactions figures match almost exactly (7.5 KB both), which is the tell: ots_ scales with transaction count, while trace size scales with internal call depth, which varies several-fold between blocks holding the same number of transactions. Neither measurement is wrong. A single sampled block under-determines the cost envelope KeeperHub#2241 is trying to estimate, and re-running is the cheap way to bound it. What the tool does: parses the chain list out of lib/rpc/rpc-config.ts at run time rather than hand-copying it, then probes debug_traceBlockByNumber, trace_block and ots_getBlockTransactions against both the publicDefault and the publicFallback of every entry -- 41 endpoints, 102 probes -- recording the verbatim response body, raw and gzipped size, fetch and parse time. Classification is the load-bearing part, because HTTP status alone decides nothing. mainnet.base.org answers an unsupported method with HTTP 403 + -32601 "rpc method is unsupported" (absent), while ethereum-rpc.publicnode.com answers trace_block with HTTP 403 + -32602 "Archive requests require a personal token" (purchasable). A structured JSON-RPC error therefore outranks the status, and a tier signal outranks absent-method wording -- reading "not available on the free plan" as "never implemented" would tell KeeperHub#2241 a chain cannot trace when it can be paid for. Every observed shape is pinned as a regression test. Placed in scripts/ deliberately: biome.jsonc excludes that directory and tsconfig.json includes .ts/.tsx/.mts but not .mjs, so this adds nothing to either gate. No dependencies, no app or database access, no credentials. Tests run against the repository's own rpc-config.ts, so config drift fails the test rather than silently skewing a future run.
About the
|
suisuss
left a comment
There was a problem hiding this comment.
Welcome, and thanks for this - the contributing guide is in CONTRIBUTING.md, and ISSUES.md covers when a change needs an issue first.
What this changes
Two new files, nothing else touched. scripts/trace-method-probe.mjs is a dependency-free Node CLI that regex-parses PUBLIC_RPCS and CHAIN_CONFIG out of lib/rpc/rpc-config.ts, probes each endpoint's liveness and head block, walks back to find a block with transactions, then tries debug_traceBlockByNumber, trace_block and ots_getBlockTransactions, classifying each result into one of twelve buckets and writing results.json and report.md. scripts/trace-method-probe.test.mjs adds 23 node:test cases over classify and parseChainConfig.
On the questions worth answering up front: it reads no process.env at all, sends no headers beyond content-type, and writes nothing that could be a secret. The default --out ./out is gitignored, so no generated output is checked in. It runs nowhere in CI.
Does it match the description
Matches, with two consequences the description does not draw out. CONFIG_URL defaults to raw.githubusercontent.com/.../staging/lib/rpc/rpc-config.ts, so a no-argument run surveys staging's config rather than the working tree. And the POST targets are 39 third-party hosts taken verbatim from that remotely fetched file.
Blocking
-
scripts/trace-method-probe.mjs:266, 268-/not found/and/not available/sit inNOT_SUPPORTED_PATTERNSwith no code constraint, so a block-availability error is bucketedmethod-not-found, which the report's own legend renders as "no plan lifts it". ->rpc.plasma.tois load-balanced; a backend a block or two behind answers-32001 "block not found"for the head-5 block. Plasma, which does implementdebug_traceBlockByNumberand which the survey's headline cites as a trace success, is recorded as not supporting it, andmethod-not-foundis not inRETRYABLEso nothing re-tries. The same applies to-32000 "header not found"and geth's"missing trie node ... state is not available". -> Drop both patterns - geth's genuine absent-method text always carries-32601, which:328already handles - and add ablock-unavailablebucket ahead of it, made retryable so the walk-back can pick another block. -
scripts/trace-method-probe.mjs:327- the tier check precedes the rate-limit check, so a throttle worded with "plan" or "upgrade" is classifiedtier-gated. -> A full run makes roughly four requests per endpoint across 39 hosts at a fixed 750 ms spacing with no backoff; drpc and ankr free tiers throttle with exactly that vocabulary. A transient throttle is written into the report's most decision-relevant column as "traces are purchasable here", with no retry and nothing distinguishing it from a real gate. -> Hoist the rate-limit test above the tier test, or makeshouldRetryreturn true for atier-gatedresult whose message matches a rate-limit pattern. -
scripts/trace-method-probe.mjs:587-meta.limits.BLOCK_SCAN_LIMITis not in thelimitsobject built at:787-792, which carries onlyREQUEST_DELAY_MS,REQUEST_TIMEOUT_MS,MAX_RESPONSE_BYTESandBLOCKS_BEHIND_HEAD. -> Every generated report reads "walked back up to undefined blocks". -> AddBLOCK_SCAN_LIMITto that object. -
scripts/trace-method-probe.mjs:576- the provenance line saysscripts/trace-probe/probe.mjs; the file isscripts/trace-method-probe.mjsand that directory does not exist. -> A reader following the line to re-run the survey cannot find it, which is the point of a regenerable table. The test file's header comment has the same stale name. -> Use the real path in both. -
scripts/trace-method-probe.mjs:425-436- when the firsteth_getBlockByNumberis notokthe scan loop breaks withtxCountat 0 andblockNumnever advanced, and control falls through to probe all three trace methods against a block that was never read. -> An endpoint 429s the block fetch; the probe then traces an unverified block number and records the answer as a capability finding, which with the first item above reads asmethod-not-found. -> Track whether the scan confirmed a readable block, and recordskippedif it did not.
Mechanical - actionable as-is
- The script embeds an emoji legend at
:512-529and writes emoji into everyreport.md.AGENTS.md:42andCLAUDE.mdboth forbid emoji in code and generated content without exception. Not CI-enforced, but it is a stated rule. Use text markers. :590claims "Transient failures are retried once", but only the trace-method loop at:470consultsshouldRetry. Theeth_chainIdliveness call at:391and theeth_blockNumbercall at:412are not retried, and a blip on either discards all three method findings for that endpoint. Route both through the retry helper, or narrow the claim.:443-blocksScanned: scanned + 1records 13 when the loop exhausts a 12-block limit, and 1 when it breaks on the first unreadable block despite zero successful reads.- The test file reads a new env var
RPC_CONFIG_PATHthat is not in.env.example, and.github/workflows/maintainability.yml'senv-syncgreps.mjsfiles. That produces a new warning annotation on every PR from here on. Add it to.env.exampleor read it differently.
Verdict
Changes requested - two classification rules turn routine node behaviour into permanent capability verdicts, which is the one thing this survey's output is used for.
One structural note. Nothing runs the test file: vitest.config.mts matches only .ts/.tsx, no package.json script runs node --test, and no workflow invokes it. biome.jsonc:27 excludes scripts/ from lint and tsconfig.json does not type-check .mjs. So the 23 cases are documentation with assertions that a human has to remember to run - worth either wiring into CI or saying explicitly in the file that it is run by hand.
Two classification rules turned routine node behaviour into permanent capability verdicts, which is the one thing this survey's output is used for. - `not found` / `not available` are gone from NOT_SUPPORTED_PATTERNS. geth reports a block it cannot serve in exactly those words, so a lagging backend on a load-balanced endpoint had plasma-mainnet recorded as not supporting `debug_traceBlockByNumber` while it was answering it. A new retryable `block-unavailable` bucket sits ahead of the not-supported patterns and catches `block not found`, `header not found`, `missing trie node` and `state is not available`. Every pattern in it is anchored on `\bblock\b` so a method name ending in `_block` cannot be read as a missing block. Genuine absence still arrives as -32601, tested before any of this. - The rate-limit test is hoisted above the tier test. drpc and ankr throttle in the tier vocabulary, and a full run is roughly four requests per endpoint across 39 hosts, so a transient throttle was being written into the report's most decision-relevant column as "traces are purchasable here". shouldRetry also now retries a tier-gated result whose message reads as a throttle, for the case where an HTTP 403 carries no body to classify. - `BLOCK_SCAN_LIMIT` added to `meta.limits`; every report said "walked back up to undefined blocks". - The provenance line and the test header named `scripts/trace-probe/probe.mjs`, a path that does not exist. Both now name the real file. - The block scan records `skipped` when it never confirmed a readable block. Previously a 429 on the first `eth_getBlockByNumber` broke the loop with the block number never advanced, and all three trace methods were then probed against a block that was never read. Also, from the mechanical list: - No emoji in generated content, per AGENTS.md. The status name carried the meaning already, so the marker column is gone rather than replaced with a second severity vocabulary that could disagree with it. - One `rpcWithRetry` helper backs every call. The liveness and head-block calls were not retried while the report claimed transient failures were, so a blip on either discarded all three findings for that endpoint. - `blocksScanned: scanned + 1` recorded 13 for an exhausted 12-block limit and 1 for zero successful reads. It is now `blocksRead`, counting requests made. - `RPC_CONFIG_PATH` documented in .env.example, so env-sync stops annotating. On the structural note: `pnpm test:trace-probe` now runs the file, and the header says how and why it sits outside vitest. 29 cases, up from 23 - the six new ones pin both directions of each ordering change, and two of them caught a missing word boundary in the block patterns while being written.
|
Thank you for this - the plasma finding in particular. You are right that it is All five blocking items are addressed in the new commit.
One thing I want to flag, because writing the tests for it changed the patterns.
Mechanical list: emoji removed - the status name already carried the meaning, On your structural note - agreed, and it was the fair reading. What I have not run: Unrelated to the review, but it is evidence for your "single run is a snapshot" |
Issue
Refs #2247. Not a competing survey — #2273 is the survey, and I am not
duplicating it. This is only the probe that keeps its table checkable.
How this happened
@tenk-earn and I picked up #2247 within five minutes of each other and, without
knowing it, probed overlapping windows the same morning. Their survey landed
first and it is the better document on the axis I explicitly punted: the
commercial provider tier matrix (Alchemy / Infura / QuickNode / Ankr / dRPC with
actual CU and credit costs) is exactly what scope item 2 asked for, and I had
deferred it to a maintainer. Their Aetherlay check is also more thorough than
mine — they ran a recursive tree scan where I checked two paths.
So rather than file a second table, this PR is the part that does not overlap.
If you would rather fold it into #2273, or not take it at all, say so and I will
close it — the survey question is answered either way.
Why a tool rather than a second table
Our two independent runs agree on the headline (plasma and tempo serve traces,
the established EVM mainnets do not) and disagree on one number in a way that is
worth keeping:
debug_traceBlockByNumbertrace_blockots_getBlockTransactionsThe
ots_figures match almost exactly, and that is the tell.ots_scales withtransaction count; trace size scales with a block's internal call depth,
which varies several-fold between blocks holding the same number of
transactions.
Neither measurement is wrong. The conclusion is that a single sampled block
under-determines the cost envelope #2241 is trying to estimate — which is
precisely the number that decides whether full-block tracing is affordable.
Re-running is the cheap way to bound it, and that needs a tool.
What it does
Parses the chain list out of
lib/rpc/rpc-config.tsat run time instead ofhand-copying it, then probes
debug_traceBlockByNumber,trace_blockandots_getBlockTransactionsagainst both thepublicDefaultand thepublicFallbackof every entry — 41 endpoints, 102 probes — recording theverbatim response body, raw and gzipped size, fetch and parse time.
Probing fallbacks systematically turned up cases the primary alone would miss:
op-sepolia,0g-mainnetandrobinhood-testneteach answered a trace methodon their fallback while refusing it on their primary.
Classification is the load-bearing part
HTTP status alone decides nothing — two endpoints return HTTP 403 meaning
opposite things:
mainnet.base.org-32601 "rpc method is unsupported"ethereum-rpc.publicnode.com-32602 "Archive requests require a personal token"So a structured JSON-RPC error outranks the HTTP status, and within the body a
tier signal outranks absent-method wording. Reading "not available on the free
plan" as "never implemented" would tell #2241 a chain cannot trace when it can
be paid for — the costliest mistake available here. Every shape above was
observed live and is pinned as a regression test.
Four classifier defects were found this way, by live responses contradicting the
code, and fixed before this PR: status read before body,
-32600 "not allowed"falling through unclassified, a non-JSON 401 policy refusal reported as
malformed, and empty blocks measured as though they were trace sizes.
Placement and cost to you
scripts/is excluded bybiome.jsonc, andtsconfig.jsonincludes.ts/.tsx/.mtsbut not.mjs— so this adds nothing to either gate. Nodependencies, no app or database access, no credentials, no runtime code path
touched.
Tests run against the repository's own
lib/rpc/rpc-config.tsrather than avendored snapshot, so config drift fails the test instead of quietly skewing a
future run. That matters here:
CHAIN_CONFIGhas 24 entries today, while #2239says "22 entries / 11 mainnets" and #2240 says "10 EVM mainnets" — it has
already drifted once.
Politeness, since these are free third-party endpoints: strictly sequential,
750 ms between every request, one block sampled per endpoint, and a single retry
reserved for failures that describe themselves as transient.
How it was verified
node --test scripts/trace-method-probe.test.mjs— 23 passed, against therepo's own config
I have not run
pnpm checkorpnpm type-check. This repo needs Node 24+,Postgres 16+ and Docker per
CONTRIBUTING.md, which I have not set up. Bothfiles sit outside those gates by construction as described above, but I would
rather say so than tick two boxes I did not verify.
I have the full
results.json(verbatim body for all 102 probes) and thefallback-coverage rows if either is useful to #2273 — happy to post them there
instead.