feat: add fork choice compliance test for pre-gloas forks - #9670
Conversation
The fork-choice compliance suite (consensus-specs#3831) introduces a `viable_for_head_roots_and_weights` check that asserts the leaves and weights of the internal `filter_block_tree(store, justified.root)`. Head equivalence alone is too weak to catch filtered-tree regressions. Add `IForkChoice.getViableHeads()` that returns the viable-for-head leaves of the filtered tree paired with their weights: - A node is a filter_block_tree leaf iff it is viable AND has no viable descendants. `maybeUpdateBestChildAndDescendant` only sets `bestChild` to a child that `nodeLeadsToViableHead`, so `bestChild === undefined` iff no viable descendant. Combined with `nodeIsViableForHead`, this matches the spec exactly. Same approach as Teku's `ForkChoiceStrategy.getChainHeads`. - Weight is converted from internal increment units to Gwei on read (compliance fixtures expect Gwei). Note the proposer-boost score contribution is also stored in increments and may round down on minimal preset (102 vs 102.4 ETH); attestation weight is exact, the rounding only surfaces while boost is active. Mainnet boost values are integer ETH so this is a non-issue there.
Wire the consensus-specs Fork Choice Compliance suite (#3831) into the existing `forkChoiceTest` runner. The on-disk layout matches the standard spec-test layout (`tests/<preset>/<fork>/fork_choice_compliance/<handler>/<suite>/<case>/`), so it slots in alongside `fork_choice` and `sync` runners. Three test-only accommodations the compliance fixtures require: 1. `bls_setting: 2` — every compliance fixture uses placeholder signatures. Pass `validSignatures: testcase.meta?.bls_setting !== BigInt(1)` to `chain.processBlock` so verification short-circuits. Standard `fork_choice` fixtures use `bls_setting: 1` so behavior there is unchanged. 2. `BLOCK_ERROR_ALREADY_KNOWN` — compliance fixtures intentionally re-import the same block (`dup_shift` mutations in their `meta.yaml`). Spec semantics for `on_block(store, known_block)` is a no-op success. Production block import correctly rejects with ALREADY_KNOWN; this runner treats that case as success only when the step is `valid: true`. 3. Cross-epoch attestation shuffling — `on_attestation` decodes aggregation_bits using the state at the attestation's target checkpoint, not the head state. The runner now resolves the right shuffling via ShufflingCache + regen (mirroring the production validation path) instead of `headState.epochCtx.getIndexedAttestation`, which only worked when the attestation's epoch happened to be in the head's epoch cache (±1 epoch) and broke on cross-epoch fork attestations surfaced by the compliance suite. Adds support for two compliance-only check fields: - `viable_for_head_roots_and_weights` (consensus-specs#3831): compared via `getViableHeads()`. Both sides are sorted by root before comparison since the spec doesn't fix order. - `head_payload_status` (gloas): mapped between our internal enum ordering (PENDING=0, EMPTY=1, FULL=2) and spec ordering (EMPTY=0, FULL=1, PENDING=2). Pass rate against the latest comptests workflow `small.tar.gz` artifact: fulu/fork_choice_compliance: 253/1472 cases pass (17.2%) Top remaining failures: - ~80% `Invalid proposer boost root` — consensus-specs#4807 introduced a `block.proposer_index == get_beacon_proposer_index(head_state)` guard in `update_proposer_boost_root` that we do not yet implement; affects all forks (not just gloas equivocation handling). Tracked for follow-up alongside #9233. - ~1% `Invalid viable heads` — proposer-boost rounding on minimal preset (see `getViableHeads()` weight note).
…e gloas variants Three corrections to the compliance-only getViableHeads getter, plus an API-surface trim: - Restrict leaves to descendants of the justified checkpoint block: the spec's get_filtered_block_tree is rooted at store.justified_checkpoint, so iterating all proto-array nodes over-included FFG-viable leaves hanging off the finalized checkpoint on branches not under the current justified checkpoint (~87 of 100 'Invalid viable heads' compliance failures were this, not proposer-boost rounding as previously assumed). The descendant walk follows parent links to the justified root — a slot-based getAncestor lookup returns the node itself for leaves whose own slot equals the justified epoch start slot — and stops early once it passes the justified block's slot (parent slots are non-increasing). - Emit each block root at most once: gloas payload-status variants share a blockRoot and more than one can be a leaf (EMPTY and FULL are both parented under PENDING), which produced duplicate roots that broke the index-paired comparison in the compliance check. Which variant's weight the spec expects is part of the open gloas vote-attribution divergence; keep the heaviest qualifying variant deterministically. - Drop getViableHeads from IForkChoice: it is compliance-test-only and its single caller accesses the ForkChoice class directly. Verified against consensus-specs compliance vectors (run 28413328593, minimal fulu): viable-head root-set failures 100 -> 0; standard minimal fork_choice+sync spectests unaffected (397 passed).
Changes to the shared fork-choice runner, each anchored to a test-format or spec contract (not compliance-suite identity), so they apply uniformly to fork_choice/sync/fork_choice_compliance: - Register fork_choice_compliance in coveredTestRunners: without it every other spec suite iterating the same fixture tree (ssz_static, operations, sanity, ...) throws 'No test runner for fork_choice_compliance' once compliance fixtures are downloaded. - Attestation timing precondition: spec validate_on_attestation requires get_current_slot(store) >= data.slot + 1. Lodestar enforces this 1-slot delay in the gossip/attestation-pool layer, not forkChoice.onAttestation; the runner calls onAttestation directly, so replicate the precondition (Lighthouse's runner excuses the same case post-hoc: DidntFail tolerated when data.slot >= current_slot). Clears all 298 negative-attestation compliance failures. - Attestation step hardening: an unknown beacon block root on a valid:true step now fails loudly instead of being silently skipped, and the shuffling resolution runs inside the negative-test try/catch so that errors on valid:false steps (e.g. attesting to a future block) count as the expected rejection instead of crashing the case. - Skip gloas ePBS signature verification unless bls_setting == 1: the test format mandates skipping verification for placeholder signatures (bls_setting: 2), which the block-import path already honors via validSignatures. Extend the same handling to the execution_payload and payload_attestation_message steps (Teku disables BLS at spec level for bls_setting IGNORED; Lighthouse compiles compliance under fake_crypto). - Split viable_for_head_roots_and_weights into exact root-set equality plus a 1.2 ETH weight tolerance: the root set is epoch-determined and must match exactly (this strictness caught the getViableHeads justified-subtree bug; Teku's containsAll would not), while weights are stored in EFFECTIVE_BALANCE_INCREMENT units so the proposer-boost score quantizes to whole ETH; the divergence is bounded by (100 - gcd(P,100) + P)/100 = 1.2 ETH independent of preset. Compliance (run 28413328593, minimal): fulu 1459/1472 (99.1%), remaining 11 proposer-boost-root divergences + 1-2 timeouts; gloas 200/1472, blocked by processPayloadAttestation verifying signatures unconditionally (ignores verifySignatures; not batched in getBlockSignatureSets) and a payload-status vote-attribution divergence (weight diffs are exact multiples of 32 ETH between EMPTY/FULL variants of the same root) — both documented follow-ups, out of scope here. Known accommodation limit: ALREADY_KNOWN treated as no-op success cannot re-apply proposer boost for a timely duplicate the way spec on_block re-processing would. Standard minimal fork_choice+sync: 397 passed, unchanged.
Use the single getDefaultNodeIndex + getNodeByIndex idiom already used elsewhere in protoArray instead of the two-hop getDefaultVariant + getNode lookup. Semantically identical for all variant cases (pre-gloas FULL, gloas PENDING, absent root); drops one redundant indices map lookup and removes an unreachable throw path.
- getViableHeads(): emit one entry per gloas payload-status variant (root, payload_status, weight) per consensus-specs #5393; drop the dedupe-by-root heuristic; return increment-unit weights - runner: read nested checks.head.payload_status (alpha.12 shape), replacing the dead flat head_payload_status key - runner: exact viable-head weight comparison via proposer-boost emulation (bigint) instead of the 1.2 ETH tolerance; normalizes the documented boost-quantization divergence using the vector's expected boost root and the production getCommitteeFraction - runner: payload-attestation steps now reject slot mismatch and non-PTC validators, and fail on unexpectedly-successful negative tests - specTestIterator: compose SkipOpts.skippedTests with runner-local shouldSkip (was silently ignored for fork_choice); gate fork_choice_compliance behind RUN_FORK_CHOICE_COMPLIANCE=1; defer gloas compliance suite Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `pnpm download-comptests` and `pnpm test:comptest` (root + beacon-node), fully outside the regular download-spec-tests/test:spec flow. The comptests.tar.gz release asset exists since consensus-specs #5334; vectors are fetched pinned to v1.7.0-alpha.12 (valid against the alpha.11 pin: the pre-gloas fork-choice spec delta is rename-only). The standard downloader deletes its entire output directory on a cache miss, so it cannot be reused for a second asset sharing spec-tests/. downloadComptests() is asset-scoped instead: extracts to a temp dir, replaces only tests/<preset>/<fork>/fork_choice_compliance/ subtrees, and writes its own comptests-version.json marker only after a successful merge — a failed download leaves existing fixtures and marker untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers: additive install alongside standard fixtures, refresh replacing comptest paths + removing stale forks, cache hit, failed extraction leaving fixtures and marker untouched, legacy marker treated as stale. Also fixes the ordering bug the tests caught: validate the temp extraction is non-empty BEFORE removing existing comptest paths — tar can exit 0 on a truncated archive, which would have wiped the existing vectors against an empty extraction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers justified-subtree leaf selection with increment-unit weights, and gloas payload-status variants of one root reported as separate entries (EMPTY-only before the envelope, EMPTY+FULL after onExecutionPayload). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… gating Compliance registration moves out of fork_choice.test.ts into its own test/spec/comptest/ file backed by a dedicated `comptest` vitest project; the shared runner is extracted to utils/forkChoiceTestRunner.ts. The regular spec projects exclude test/spec/comptest/** so test:spec/test:spec:minimal can never pick the suite up, which removes the need for RUN_FORK_CHOICE_COMPLIANCE: `pnpm test:comptest` simply runs the comptest project. Zero-test guard (workspace config has passWithNoTests: true) moves into the comptest entry file, now unconditional. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
block_tree_test_16_201284350_1 fails identically on every pre-gloas fork: the dependent-root gate compares against the cached head, which is stale when the preceding tick crosses an epoch boundary and pulls up the justified checkpoint. Tracked in #9666. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spec on_payload_attestation_message returns early when the message slot
does not match the attested block's slot ('PTC votes can only change the
vote for their assigned beacon block, return early otherwise') — it is
not a rejection. Restore conditional processing; the PTC-membership
assert and the negative-test assertion remain.
https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.12/specs/gloas/fork-choice.md#on_payload_attestation_message
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
comptests-version.txt, matching the standard downloader's version.txt convention; a single version string needs no JSON. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compliance vectors now live in spec-tests-comptests/ with their own version.txt, downloaded via the plain generic downloader — a flow fully parallel to the standard spec tests, sharing only the fork-choice test runner. download-spec-tests and download-comptests are order-independent (previously, a standard-tests version change wiped the shared directory including the merged compliance vectors). Deletes the asset-scoped merge downloader and its tests — no longer needed without a shared directory. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scheduled (03:00 UTC) rather than per-PR: most PRs don't touch fork choice, and the vectors are pinned — nightly on unstable is the right cadence to catch regressions. Same pattern as Kurtosis sim tests. Manual runs via workflow_dispatch. Also documents download-comptests/test:comptest in AGENTS.md/CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Own outputDirBase and testsToDownload like the other entries; version and repo URL shared with the standard spec tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Gemini encountered an error creating the review. You can try again by commenting |
|
|
||
| const logger = testLogger("spec-test"); | ||
|
|
||
| export const forkChoiceTestRunner = |
There was a problem hiding this comment.
Note for reviewers: a lot of the code here is migrated from fork_choice.test.ts so both spec test and compliance test can share a single runner.
Would be easier to view the diff by:
git diff unstable:packages/beacon-node/test/spec/presets/fork_choice.test.ts nc/fork-choice-compliance:packages/beacon-node/test/spec/utils/forkChoiceTestRunner.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48f1f5f479
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
tickTime starts at 0, so an attestation step before the first tick computed slot 0 regardless of the anchor state slot. clock.currentSlot is initialized from the anchor slot and advanced by tick steps, matching get_current_slot(store). Also document the ALREADY_KNOWN duplicate-block accommodation honestly (spec re-processes duplicates; it is not a no-op). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a standalone fork-choice compliance test suite (pnpm test:comptest) parallel to the standard spec tests, extracting the fork-choice test runner into a reusable utility. It also implements helper methods in ProtoArray and ForkChoice to retrieve viable heads and justified balances for compliance assertions. Feedback from the review highlights opportunities to improve type safety in the test runner by adding type guards before casting blockState to IBeaconStateViewGloas, and to optimize performance in protoArray.ts by returning early when justifiedSlot is undefined to avoid unnecessary tree traversals.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
| Benchmark suite | Current: af8503e | Previous: d16fa2b | Ratio |
|---|---|---|---|
| send data - 1000 2048B messages | 67.344 ms/op | 12.055 ms/op | 5.59 |
| Full columns - reconstruct all 6 blobs | 376.85 us/op | 104.39 us/op | 3.61 |
Full benchmark results
| Benchmark suite | Current: af8503e | Previous: d16fa2b | Ratio |
|---|---|---|---|
| getPubkeys - index2pubkey - req 1000 vs - 250000 vc | 934.02 us/op | 879.50 us/op | 1.06 |
| getPubkeys - validatorsArr - req 1000 vs - 250000 vc | 39.181 us/op | 37.426 us/op | 1.05 |
| BLS verify - blst | 641.14 us/op | 760.80 us/op | 0.84 |
| BLS verifyMultipleSignatures 3 - blst | 1.3465 ms/op | 1.3673 ms/op | 0.98 |
| BLS verifyMultipleSignatures 8 - blst | 2.1544 ms/op | 2.1594 ms/op | 1.00 |
| BLS verifyMultipleSignatures 32 - blst | 6.8022 ms/op | 6.8778 ms/op | 0.99 |
| BLS verifyMultipleSignatures 64 - blst | 13.219 ms/op | 13.103 ms/op | 1.01 |
| BLS verifyMultipleSignatures 128 - blst | 25.575 ms/op | 25.786 ms/op | 0.99 |
| BLS deserializing 10000 signatures | 608.05 ms/op | 623.27 ms/op | 0.98 |
| BLS deserializing 100000 signatures | 6.1246 s/op | 6.4625 s/op | 0.95 |
| BLS verifyMultipleSignatures - same message - 3 - blst | 797.00 us/op | 813.12 us/op | 0.98 |
| BLS verifyMultipleSignatures - same message - 8 - blst | 880.98 us/op | 980.90 us/op | 0.90 |
| BLS verifyMultipleSignatures - same message - 32 - blst | 1.4704 ms/op | 1.6260 ms/op | 0.90 |
| BLS verifyMultipleSignatures - same message - 64 - blst | 2.2789 ms/op | 2.4886 ms/op | 0.92 |
| BLS verifyMultipleSignatures - same message - 128 - blst | 3.8999 ms/op | 4.2456 ms/op | 0.92 |
| BLS aggregatePubkeys 32 - blst | 17.553 us/op | 18.305 us/op | 0.96 |
| BLS aggregatePubkeys 128 - blst | 62.408 us/op | 65.206 us/op | 0.96 |
| getSlashingsAndExits - default max | 47.355 us/op | 50.784 us/op | 0.93 |
| getSlashingsAndExits - 2k | 348.70 us/op | 362.11 us/op | 0.96 |
| proposeBlockBody type=full, size=empty | 599.88 us/op | 608.87 us/op | 0.99 |
| isKnown best case - 1 super set check | 171.00 ns/op | 165.00 ns/op | 1.04 |
| isKnown normal case - 2 super set checks | 158.00 ns/op | 167.00 ns/op | 0.95 |
| isKnown worse case - 16 super set checks | 164.00 ns/op | 169.00 ns/op | 0.97 |
| validate api signedAggregateAndProof - struct | 1.5229 ms/op | 1.5763 ms/op | 0.97 |
| validate gossip signedAggregateAndProof - struct | 1.5174 ms/op | 1.5710 ms/op | 0.97 |
| batch validate gossip attestation - vc 640000 - chunk 32 | 104.19 us/op | 112.78 us/op | 0.92 |
| batch validate gossip attestation - vc 640000 - chunk 64 | 90.878 us/op | 99.585 us/op | 0.91 |
| batch validate gossip attestation - vc 640000 - chunk 128 | 84.486 us/op | 93.002 us/op | 0.91 |
| batch validate gossip attestation - vc 640000 - chunk 256 | 80.843 us/op | 89.437 us/op | 0.90 |
| bytes32 toHexString | 296.00 ns/op | 294.00 ns/op | 1.01 |
| bytes32 Buffer.toString(hex) | 170.00 ns/op | 163.00 ns/op | 1.04 |
| bytes32 Buffer.toString(hex) from Uint8Array | 242.00 ns/op | 241.00 ns/op | 1.00 |
| bytes32 Buffer.toString(hex) + 0x | 171.00 ns/op | 166.00 ns/op | 1.03 |
| Return object 10000 times | 0.20430 ns/op | 0.21950 ns/op | 0.93 |
| Throw Error 10000 times | 3.1793 us/op | 3.4292 us/op | 0.93 |
| toHex | 98.301 ns/op | 98.201 ns/op | 1.00 |
| Buffer.from | 92.644 ns/op | 91.479 ns/op | 1.01 |
| shared Buffer | 63.376 ns/op | 61.829 ns/op | 1.03 |
| fastMsgIdFn sha256 / 200 bytes | 1.5440 us/op | 1.5100 us/op | 1.02 |
| fastMsgIdFn h32 xxhash / 200 bytes | 158.00 ns/op | 155.00 ns/op | 1.02 |
| fastMsgIdFn h64 xxhash / 200 bytes | 204.00 ns/op | 200.00 ns/op | 1.02 |
| fastMsgIdFn sha256 / 1000 bytes | 4.5510 us/op | 4.8900 us/op | 0.93 |
| fastMsgIdFn h32 xxhash / 1000 bytes | 246.00 ns/op | 248.00 ns/op | 0.99 |
| fastMsgIdFn h64 xxhash / 1000 bytes | 248.00 ns/op | 254.00 ns/op | 0.98 |
| fastMsgIdFn sha256 / 10000 bytes | 39.482 us/op | 43.277 us/op | 0.91 |
| fastMsgIdFn h32 xxhash / 10000 bytes | 1.2140 us/op | 1.3170 us/op | 0.92 |
| fastMsgIdFn h64 xxhash / 10000 bytes | 806.00 ns/op | 853.00 ns/op | 0.94 |
| send data - 1000 256B messages | 4.2640 ms/op | 4.4865 ms/op | 0.95 |
| send data - 1000 512B messages | 5.2454 ms/op | 5.0310 ms/op | 1.04 |
| send data - 1000 1024B messages | 5.4967 ms/op | 6.0003 ms/op | 0.92 |
| send data - 1000 1200B messages | 6.8882 ms/op | 6.4023 ms/op | 1.08 |
| send data - 1000 2048B messages | 67.344 ms/op | 12.055 ms/op | 5.59 |
| send data - 1000 4096B messages | 58.658 ms/op | 86.159 ms/op | 0.68 |
| send data - 1000 16384B messages | 378.83 ms/op | 382.08 ms/op | 0.99 |
| send data - 1000 65536B messages | 1.5639 s/op | 1.4968 s/op | 1.04 |
| enrSubnets - fastDeserialize 64 bits | 755.00 ns/op | 774.00 ns/op | 0.98 |
| enrSubnets - ssz BitVector 64 bits | 275.00 ns/op | 257.00 ns/op | 1.07 |
| enrSubnets - fastDeserialize 4 bits | 112.00 ns/op | 97.000 ns/op | 1.15 |
| enrSubnets - ssz BitVector 4 bits | 266.00 ns/op | 254.00 ns/op | 1.05 |
| prioritizePeers score -10:0 att 32-0.1 sync 2-0 | 208.03 us/op | 197.80 us/op | 1.05 |
| prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 | 229.83 us/op | 218.52 us/op | 1.05 |
| prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 | 320.41 us/op | 331.14 us/op | 0.97 |
| prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 | 572.95 us/op | 582.10 us/op | 0.98 |
| prioritizePeers score 0:0 att 64-1 sync 4-1 | 694.66 us/op | 678.87 us/op | 1.02 |
| array of 16000 items push then shift | 1.3055 us/op | 1.2910 us/op | 1.01 |
| LinkedList of 16000 items push then shift | 6.9470 ns/op | 6.9440 ns/op | 1.00 |
| array of 16000 items push then pop | 64.669 ns/op | 64.880 ns/op | 1.00 |
| LinkedList of 16000 items push then pop | 6.0310 ns/op | 5.9410 ns/op | 1.02 |
| array of 24000 items push then shift | 1.9305 us/op | 1.9006 us/op | 1.02 |
| LinkedList of 24000 items push then shift | 6.4270 ns/op | 6.4990 ns/op | 0.99 |
| array of 24000 items push then pop | 90.477 ns/op | 90.776 ns/op | 1.00 |
| LinkedList of 24000 items push then pop | 5.9750 ns/op | 5.9470 ns/op | 1.00 |
| intersect bitArray bitLen 8 | 4.7780 ns/op | 4.7840 ns/op | 1.00 |
| intersect array and set length 8 | 29.748 ns/op | 29.557 ns/op | 1.01 |
| intersect bitArray bitLen 128 | 24.043 ns/op | 24.123 ns/op | 1.00 |
| intersect array and set length 128 | 503.20 ns/op | 512.23 ns/op | 0.98 |
| bitArray.getTrueBitIndexes() bitLen 128 | 914.00 ns/op | 907.00 ns/op | 1.01 |
| bitArray.getTrueBitIndexes() bitLen 248 | 1.6480 us/op | 1.6380 us/op | 1.01 |
| bitArray.getTrueBitIndexes() bitLen 512 | 3.4330 us/op | 3.4060 us/op | 1.01 |
| Full columns - reconstruct all 6 blobs | 376.85 us/op | 104.39 us/op | 3.61 |
| Full columns - reconstruct half of the blobs out of 6 | 61.765 us/op | 64.703 us/op | 0.95 |
| Full columns - reconstruct single blob out of 6 | 31.783 us/op | 32.127 us/op | 0.99 |
| Half columns - reconstruct all 6 blobs | 389.13 ms/op | 384.41 ms/op | 1.01 |
| Half columns - reconstruct half of the blobs out of 6 | 192.78 ms/op | 191.46 ms/op | 1.01 |
| Half columns - reconstruct single blob out of 6 | 69.395 ms/op | 68.268 ms/op | 1.02 |
| Set add up to 64 items then delete first | 1.5722 us/op | 1.5270 us/op | 1.03 |
| OrderedSet add up to 64 items then delete first | 2.4321 us/op | 2.3804 us/op | 1.02 |
| Set add up to 64 items then delete last | 1.7916 us/op | 1.7615 us/op | 1.02 |
| OrderedSet add up to 64 items then delete last | 2.8293 us/op | 2.7996 us/op | 1.01 |
| Set add up to 64 items then delete middle | 1.9058 us/op | 1.8921 us/op | 1.01 |
| OrderedSet add up to 64 items then delete middle | 4.3164 us/op | 4.2673 us/op | 1.01 |
| Set add up to 128 items then delete first | 3.8472 us/op | 3.7696 us/op | 1.02 |
| OrderedSet add up to 128 items then delete first | 5.8580 us/op | 5.8078 us/op | 1.01 |
| Set add up to 128 items then delete last | 3.7410 us/op | 3.6738 us/op | 1.02 |
| OrderedSet add up to 128 items then delete last | 5.4806 us/op | 5.4024 us/op | 1.01 |
| Set add up to 128 items then delete middle | 3.7277 us/op | 3.6450 us/op | 1.02 |
| OrderedSet add up to 128 items then delete middle | 11.566 us/op | 10.753 us/op | 1.08 |
| Set add up to 256 items then delete first | 7.8027 us/op | 7.1089 us/op | 1.10 |
| OrderedSet add up to 256 items then delete first | 11.913 us/op | 11.189 us/op | 1.06 |
| Set add up to 256 items then delete last | 7.4119 us/op | 6.7707 us/op | 1.09 |
| OrderedSet add up to 256 items then delete last | 11.186 us/op | 10.630 us/op | 1.05 |
| Set add up to 256 items then delete middle | 7.2774 us/op | 6.8529 us/op | 1.06 |
| OrderedSet add up to 256 items then delete middle | 34.498 us/op | 32.936 us/op | 1.05 |
| runFastConfirmationRules vc:100000 bc:96 eq:0 | 4.7415 ms/op | 4.2283 ms/op | 1.12 |
| runFastConfirmationRules vc:600000 bc:96 eq:0 | 34.046 ms/op | 33.389 ms/op | 1.02 |
| runFastConfirmationRules vc:1000000 bc:96 eq:0 | 58.486 ms/op | 54.638 ms/op | 1.07 |
| runFastConfirmationRules vc:600000 bc:320 eq:0 | 33.864 ms/op | 33.271 ms/op | 1.02 |
| runFastConfirmationRules vc:100000 bc:96 eq:1000 | 1.0982 s/op | 1.0479 s/op | 1.05 |
| pass gossip attestations to forkchoice per slot | 2.5927 ms/op | 2.5840 ms/op | 1.00 |
| forkChoice updateHead vc 100000 bc 64 eq 0 | 409.90 us/op | 423.55 us/op | 0.97 |
| forkChoice updateHead vc 600000 bc 64 eq 0 | 2.5124 ms/op | 2.5761 ms/op | 0.98 |
| forkChoice updateHead vc 1000000 bc 64 eq 0 | 4.0932 ms/op | 4.2651 ms/op | 0.96 |
| forkChoice updateHead vc 600000 bc 320 eq 0 | 2.4586 ms/op | 2.6098 ms/op | 0.94 |
| forkChoice updateHead vc 600000 bc 1200 eq 0 | 2.5186 ms/op | 2.5832 ms/op | 0.98 |
| forkChoice updateHead vc 600000 bc 7200 eq 0 | 2.7674 ms/op | 2.9399 ms/op | 0.94 |
| forkChoice updateHead vc 600000 bc 64 eq 1000 | 2.4529 ms/op | 2.3400 ms/op | 1.05 |
| forkChoice updateHead vc 600000 bc 64 eq 10000 | 2.5288 ms/op | 2.4424 ms/op | 1.04 |
| forkChoice updateHead vc 600000 bc 64 eq 300000 | 6.5263 ms/op | 6.9825 ms/op | 0.93 |
| computeDeltas 1400000 validators 0% inactive | 12.319 ms/op | 11.532 ms/op | 1.07 |
| computeDeltas 1400000 validators 10% inactive | 11.611 ms/op | 10.885 ms/op | 1.07 |
| computeDeltas 1400000 validators 20% inactive | 10.972 ms/op | 10.379 ms/op | 1.06 |
| computeDeltas 1400000 validators 50% inactive | 8.9757 ms/op | 8.4444 ms/op | 1.06 |
| computeDeltas 2100000 validators 0% inactive | 18.411 ms/op | 17.283 ms/op | 1.07 |
| computeDeltas 2100000 validators 10% inactive | 17.466 ms/op | 16.481 ms/op | 1.06 |
| computeDeltas 2100000 validators 20% inactive | 16.483 ms/op | 15.355 ms/op | 1.07 |
| computeDeltas 2100000 validators 50% inactive | 13.406 ms/op | 10.158 ms/op | 1.32 |
| altair processAttestation - 250000 vs - 7PWei normalcase | 1.6379 ms/op | 1.6857 ms/op | 0.97 |
| altair processAttestation - 250000 vs - 7PWei worstcase | 2.3254 ms/op | 2.4996 ms/op | 0.93 |
| altair processAttestation - setStatus - 1/6 committees join | 102.61 us/op | 106.28 us/op | 0.97 |
| altair processAttestation - setStatus - 1/3 committees join | 190.96 us/op | 194.76 us/op | 0.98 |
| altair processAttestation - setStatus - 1/2 committees join | 280.40 us/op | 284.75 us/op | 0.98 |
| altair processAttestation - setStatus - 2/3 committees join | 370.88 us/op | 375.34 us/op | 0.99 |
| altair processAttestation - setStatus - 4/5 committees join | 496.93 us/op | 502.62 us/op | 0.99 |
| altair processAttestation - setStatus - 100% committees join | 588.89 us/op | 598.21 us/op | 0.98 |
| altair processBlock - 250000 vs - 7PWei normalcase | 3.8404 ms/op | 3.1501 ms/op | 1.22 |
| altair processBlock - 250000 vs - 7PWei normalcase hashState | 14.217 ms/op | 16.906 ms/op | 0.84 |
| altair processBlock - 250000 vs - 7PWei worstcase | 19.677 ms/op | 20.100 ms/op | 0.98 |
| altair processBlock - 250000 vs - 7PWei worstcase hashState | 42.497 ms/op | 38.699 ms/op | 1.10 |
| phase0 processBlock - 250000 vs - 7PWei normalcase | 1.4779 ms/op | 1.4428 ms/op | 1.02 |
| phase0 processBlock - 250000 vs - 7PWei worstcase | 17.319 ms/op | 16.738 ms/op | 1.03 |
| altair processEth1Data - 250000 vs - 7PWei normalcase | 291.87 us/op | 283.06 us/op | 1.03 |
| getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 | 3.6640 us/op | 3.2690 us/op | 1.12 |
| getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 | 20.762 us/op | 21.708 us/op | 0.96 |
| getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 | 5.5750 us/op | 5.8990 us/op | 0.95 |
| getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 | 3.4530 us/op | 3.4770 us/op | 0.99 |
| getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 | 90.521 us/op | 96.985 us/op | 0.93 |
| getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 | 1.3547 ms/op | 1.3586 ms/op | 1.00 |
| getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 | 1.7662 ms/op | 1.7933 ms/op | 0.98 |
| getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 | 1.7532 ms/op | 1.8122 ms/op | 0.97 |
| getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 | 3.5525 ms/op | 3.6681 ms/op | 0.97 |
| getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 | 2.0071 ms/op | 2.0403 ms/op | 0.98 |
| getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 | 3.8678 ms/op | 3.9844 ms/op | 0.97 |
| Tree 40 250000 create | 356.21 ms/op | 355.87 ms/op | 1.00 |
| Tree 40 250000 get(125000) | 94.509 ns/op | 90.689 ns/op | 1.04 |
| Tree 40 250000 set(125000) | 998.11 ns/op | 972.40 ns/op | 1.03 |
| Tree 40 250000 toArray() | 9.9212 ms/op | 15.840 ms/op | 0.63 |
| Tree 40 250000 iterate all - toArray() + loop | 10.031 ms/op | 16.004 ms/op | 0.63 |
| Tree 40 250000 iterate all - get(i) | 32.821 ms/op | 38.234 ms/op | 0.86 |
| Array 250000 create | 2.0674 ms/op | 2.0884 ms/op | 0.99 |
| Array 250000 clone - spread | 659.79 us/op | 642.27 us/op | 1.03 |
| Array 250000 get(125000) | 0.30700 ns/op | 0.28600 ns/op | 1.07 |
| Array 250000 set(125000) | 0.30600 ns/op | 0.29300 ns/op | 1.04 |
| Array 250000 iterate all - loop | 58.218 us/op | 56.119 us/op | 1.04 |
| phase0 afterProcessEpoch - 250000 vs - 7PWei | 53.039 ms/op | 51.001 ms/op | 1.04 |
| Array.fill - length 1000000 | 2.1718 ms/op | 2.1067 ms/op | 1.03 |
| Array push - length 1000000 | 7.3921 ms/op | 8.7343 ms/op | 0.85 |
| Array.get | 0.20916 ns/op | 0.19886 ns/op | 1.05 |
| Uint8Array.get | 0.23095 ns/op | 0.24208 ns/op | 0.95 |
| phase0 beforeProcessEpoch - 250000 vs - 7PWei | 13.925 ms/op | 18.999 ms/op | 0.73 |
| altair processEpoch - mainnet_e81889 | 325.63 ms/op | 334.05 ms/op | 0.97 |
| mainnet_e81889 - altair beforeProcessEpoch | 33.489 ms/op | 19.704 ms/op | 1.70 |
| mainnet_e81889 - altair processJustificationAndFinalization | 6.0940 us/op | 6.4660 us/op | 0.94 |
| mainnet_e81889 - altair processInactivityUpdates | 3.5377 ms/op | 3.4645 ms/op | 1.02 |
| mainnet_e81889 - altair processRewardsAndPenalties | 16.890 ms/op | 19.323 ms/op | 0.87 |
| mainnet_e81889 - altair processRegistryUpdates | 544.00 ns/op | 531.00 ns/op | 1.02 |
| mainnet_e81889 - altair processSlashings | 148.00 ns/op | 136.00 ns/op | 1.09 |
| mainnet_e81889 - altair processEth1DataReset | 141.00 ns/op | 142.00 ns/op | 0.99 |
| mainnet_e81889 - altair processEffectiveBalanceUpdates | 1.6425 ms/op | 3.6018 ms/op | 0.46 |
| mainnet_e81889 - altair processSlashingsReset | 702.00 ns/op | 674.00 ns/op | 1.04 |
| mainnet_e81889 - altair processRandaoMixesReset | 1.3110 us/op | 1.3840 us/op | 0.95 |
| mainnet_e81889 - altair processHistoricalRootsUpdate | 146.00 ns/op | 131.00 ns/op | 1.11 |
| mainnet_e81889 - altair processParticipationFlagUpdates | 439.00 ns/op | 426.00 ns/op | 1.03 |
| mainnet_e81889 - altair processSyncCommitteeUpdates | 117.00 ns/op | 107.00 ns/op | 1.09 |
| mainnet_e81889 - altair afterProcessEpoch | 40.305 ms/op | 41.292 ms/op | 0.98 |
| capella processEpoch - mainnet_e217614 | 942.53 ms/op | 988.56 ms/op | 0.95 |
| mainnet_e217614 - capella beforeProcessEpoch | 56.482 ms/op | 56.440 ms/op | 1.00 |
| mainnet_e217614 - capella processJustificationAndFinalization | 6.5450 us/op | 8.0110 us/op | 0.82 |
| mainnet_e217614 - capella processInactivityUpdates | 16.348 ms/op | 10.980 ms/op | 1.49 |
| mainnet_e217614 - capella processRewardsAndPenalties | 97.283 ms/op | 105.01 ms/op | 0.93 |
| mainnet_e217614 - capella processRegistryUpdates | 4.4480 us/op | 4.5490 us/op | 0.98 |
| mainnet_e217614 - capella processSlashings | 139.00 ns/op | 134.00 ns/op | 1.04 |
| mainnet_e217614 - capella processEth1DataReset | 141.00 ns/op | 127.00 ns/op | 1.11 |
| mainnet_e217614 - capella processEffectiveBalanceUpdates | 5.6111 ms/op | 5.5332 ms/op | 1.01 |
| mainnet_e217614 - capella processSlashingsReset | 668.00 ns/op | 679.00 ns/op | 0.98 |
| mainnet_e217614 - capella processRandaoMixesReset | 1.1110 us/op | 1.3270 us/op | 0.84 |
| mainnet_e217614 - capella processHistoricalRootsUpdate | 140.00 ns/op | 130.00 ns/op | 1.08 |
| mainnet_e217614 - capella processParticipationFlagUpdates | 454.00 ns/op | 426.00 ns/op | 1.07 |
| mainnet_e217614 - capella afterProcessEpoch | 103.92 ms/op | 110.00 ms/op | 0.94 |
| phase0 processEpoch - mainnet_e58758 | 296.89 ms/op | 310.88 ms/op | 0.95 |
| mainnet_e58758 - phase0 beforeProcessEpoch | 64.022 ms/op | 63.984 ms/op | 1.00 |
| mainnet_e58758 - phase0 processJustificationAndFinalization | 6.5360 us/op | 5.9160 us/op | 1.10 |
| mainnet_e58758 - phase0 processRewardsAndPenalties | 16.488 ms/op | 15.455 ms/op | 1.07 |
| mainnet_e58758 - phase0 processRegistryUpdates | 2.1360 us/op | 2.2400 us/op | 0.95 |
| mainnet_e58758 - phase0 processSlashings | 136.00 ns/op | 134.00 ns/op | 1.01 |
| mainnet_e58758 - phase0 processEth1DataReset | 136.00 ns/op | 131.00 ns/op | 1.04 |
| mainnet_e58758 - phase0 processEffectiveBalanceUpdates | 776.16 us/op | 832.59 us/op | 0.93 |
| mainnet_e58758 - phase0 processSlashingsReset | 836.00 ns/op | 933.00 ns/op | 0.90 |
| mainnet_e58758 - phase0 processRandaoMixesReset | 1.3520 us/op | 1.4890 us/op | 0.91 |
| mainnet_e58758 - phase0 processHistoricalRootsUpdate | 250.00 ns/op | 139.00 ns/op | 1.80 |
| mainnet_e58758 - phase0 processParticipationRecordUpdates | 1.2060 us/op | 1.2420 us/op | 0.97 |
| mainnet_e58758 - phase0 afterProcessEpoch | 31.858 ms/op | 32.930 ms/op | 0.97 |
| phase0 processEffectiveBalanceUpdates - 250000 normalcase | 938.77 us/op | 1.0719 ms/op | 0.88 |
| phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 | 1.5165 ms/op | 1.2024 ms/op | 1.26 |
| altair processInactivityUpdates - 250000 normalcase | 9.7847 ms/op | 11.959 ms/op | 0.82 |
| altair processInactivityUpdates - 250000 worstcase | 10.347 ms/op | 13.015 ms/op | 0.80 |
| phase0 processRegistryUpdates - 250000 normalcase | 2.1800 us/op | 2.2280 us/op | 0.98 |
| phase0 processRegistryUpdates - 250000 badcase_full_deposits | 150.11 us/op | 147.20 us/op | 1.02 |
| phase0 processRegistryUpdates - 250000 worstcase 0.5 | 64.364 ms/op | 75.991 ms/op | 0.85 |
| altair processRewardsAndPenalties - 250000 normalcase | 15.941 ms/op | 13.267 ms/op | 1.20 |
| altair processRewardsAndPenalties - 250000 worstcase | 15.516 ms/op | 12.924 ms/op | 1.20 |
| phase0 getAttestationDeltas - 250000 normalcase | 5.1319 ms/op | 5.4135 ms/op | 0.95 |
| phase0 getAttestationDeltas - 250000 worstcase | 5.1159 ms/op | 5.5036 ms/op | 0.93 |
| phase0 processSlashings - 250000 worstcase | 56.490 us/op | 59.929 us/op | 0.94 |
| altair processSyncCommitteeUpdates - 250000 | 9.7155 ms/op | 10.172 ms/op | 0.96 |
| BeaconState.hashTreeRoot - No change | 177.00 ns/op | 176.00 ns/op | 1.01 |
| BeaconState.hashTreeRoot - 1 full validator | 81.383 us/op | 74.673 us/op | 1.09 |
| BeaconState.hashTreeRoot - 32 full validator | 863.94 us/op | 859.50 us/op | 1.01 |
| BeaconState.hashTreeRoot - 512 full validator | 7.7641 ms/op | 6.2519 ms/op | 1.24 |
| BeaconState.hashTreeRoot - 1 validator.effectiveBalance | 112.70 us/op | 84.407 us/op | 1.34 |
| BeaconState.hashTreeRoot - 32 validator.effectiveBalance | 1.4778 ms/op | 1.1494 ms/op | 1.29 |
| BeaconState.hashTreeRoot - 512 validator.effectiveBalance | 21.757 ms/op | 21.191 ms/op | 1.03 |
| BeaconState.hashTreeRoot - 1 balances | 84.229 us/op | 77.926 us/op | 1.08 |
| BeaconState.hashTreeRoot - 32 balances | 897.54 us/op | 724.71 us/op | 1.24 |
| BeaconState.hashTreeRoot - 512 balances | 5.4359 ms/op | 6.3702 ms/op | 0.85 |
| BeaconState.hashTreeRoot - 250000 balances | 162.50 ms/op | 158.92 ms/op | 1.02 |
| aggregationBits - 2048 els - zipIndexesInBitList | 19.614 us/op | 19.203 us/op | 1.02 |
| regular array get 100000 times | 22.708 us/op | 22.459 us/op | 1.01 |
| wrappedArray get 100000 times | 22.843 us/op | 22.363 us/op | 1.02 |
| arrayWithProxy get 100000 times | 10.588 ms/op | 9.7161 ms/op | 1.09 |
| ssz.Root.equals | 21.385 ns/op | 20.847 ns/op | 1.03 |
| byteArrayEquals | 21.139 ns/op | 20.841 ns/op | 1.01 |
| Buffer.compare | 8.8780 ns/op | 8.5820 ns/op | 1.03 |
| processSlot - 1 slots | 9.8460 us/op | 9.4430 us/op | 1.04 |
| processSlot - 32 slots | 2.3273 ms/op | 2.3350 ms/op | 1.00 |
| getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei | 3.7266 ms/op | 7.5089 ms/op | 0.50 |
| getCommitteeAssignments - req 1 vs - 250000 vc | 1.6475 ms/op | 1.6978 ms/op | 0.97 |
| getCommitteeAssignments - req 100 vs - 250000 vc | 3.3985 ms/op | 3.4683 ms/op | 0.98 |
| getCommitteeAssignments - req 1000 vs - 250000 vc | 3.6317 ms/op | 3.7234 ms/op | 0.98 |
| findModifiedValidators - 10000 modified validators | 821.33 ms/op | 749.51 ms/op | 1.10 |
| findModifiedValidators - 1000 modified validators | 444.16 ms/op | 450.34 ms/op | 0.99 |
| findModifiedValidators - 100 modified validators | 281.70 ms/op | 296.16 ms/op | 0.95 |
| findModifiedValidators - 10 modified validators | 214.98 ms/op | 190.69 ms/op | 1.13 |
| findModifiedValidators - 1 modified validators | 190.38 ms/op | 152.16 ms/op | 1.25 |
| findModifiedValidators - no difference | 153.72 ms/op | 148.42 ms/op | 1.04 |
| migrate state 1500000 validators, 3400 modified, 2000 new | 3.7595 s/op | 3.4185 s/op | 1.10 |
| RootCache.getBlockRootAtSlot - 250000 vs - 7PWei | 3.8000 ns/op | 3.6500 ns/op | 1.04 |
| state getBlockRootAtSlot - 250000 vs - 7PWei | 421.83 ns/op | 451.67 ns/op | 0.93 |
| computeProposerIndex 100000 validators | 1.3046 ms/op | 1.3191 ms/op | 0.99 |
| getNextSyncCommitteeIndices 1000 validators | 2.7600 ms/op | 2.8066 ms/op | 0.98 |
| getNextSyncCommitteeIndices 10000 validators | 24.298 ms/op | 24.680 ms/op | 0.98 |
| getNextSyncCommitteeIndices 100000 validators | 85.088 ms/op | 84.662 ms/op | 1.01 |
| computeProposers - vc 250000 | 525.80 us/op | 535.71 us/op | 0.98 |
| computeEpochShuffling - vc 250000 | 37.293 ms/op | 38.133 ms/op | 0.98 |
| getNextSyncCommittee - vc 250000 | 8.9888 ms/op | 9.3689 ms/op | 0.96 |
| nodejs block root to RootHex using toHex | 95.719 ns/op | 90.842 ns/op | 1.05 |
| nodejs block root to RootHex using toRootHex | 60.055 ns/op | 59.099 ns/op | 1.02 |
| nodejs fromHex(blob) | 821.59 us/op | 815.74 us/op | 1.01 |
| nodejs fromHexInto(blob) | 649.65 us/op | 601.65 us/op | 1.08 |
| nodejs block root to RootHex using the deprecated toHexString | 489.68 ns/op | 449.88 ns/op | 1.09 |
| nodejs byteArrayEquals 32 bytes (block root) | 25.938 ns/op | 25.053 ns/op | 1.04 |
| nodejs byteArrayEquals 48 bytes (pubkey) | 37.574 ns/op | 36.044 ns/op | 1.04 |
| nodejs byteArrayEquals 96 bytes (signature) | 38.913 ns/op | 33.627 ns/op | 1.16 |
| nodejs byteArrayEquals 1024 bytes | 45.028 ns/op | 39.331 ns/op | 1.14 |
| nodejs byteArrayEquals 131072 bytes (blob) | 1.7431 us/op | 1.7147 us/op | 1.02 |
| browser block root to RootHex using toHex | 146.12 ns/op | 139.04 ns/op | 1.05 |
| browser block root to RootHex using toRootHex | 130.78 ns/op | 126.34 ns/op | 1.04 |
| browser fromHex(blob) | 1.6754 ms/op | 1.6720 ms/op | 1.00 |
| browser fromHexInto(blob) | 601.58 us/op | 604.24 us/op | 1.00 |
| browser block root to RootHex using the deprecated toHexString | 310.70 ns/op | 318.00 ns/op | 0.98 |
| browser byteArrayEquals 32 bytes (block root) | 27.234 ns/op | 27.271 ns/op | 1.00 |
| browser byteArrayEquals 48 bytes (pubkey) | 37.944 ns/op | 38.499 ns/op | 0.99 |
| browser byteArrayEquals 96 bytes (signature) | 71.257 ns/op | 71.490 ns/op | 1.00 |
| browser byteArrayEquals 1024 bytes | 725.39 ns/op | 728.10 ns/op | 1.00 |
| browser byteArrayEquals 131072 bytes (blob) | 91.119 us/op | 92.057 us/op | 0.99 |
by benchmarkbot/action
twoeths
left a comment
There was a problem hiding this comment.
looks good to me as an infrastructure for compliance test
right now the getCommitteeFranction() doesn't follow forkchoice spec because we store weight by ETH, not by gwei. I suspect we did that even before proposer boost was landed, and we did not revisit.
one way to do that is to let forkchoice store weight in ETH by a scale of SLOTS_PER_EPOCH * 100, then we can keep it as integer without an overflow issue. But this is a separate issue.
… for getCommitteeFraction divergence Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mpliance # Conflicts: # packages/beacon-node/test/spec/utils/specTestIterator.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🎉 This PR is included in v1.46.0 🎉 |
Description
The suite is a standalone flow parallel to the standard spec tests, sharing only the fork-choice test runner:
pnpm download-comptests— fetches thecomptests.tar.gzrelease asset (same version pin and repo as the standard spec tests, configured inspec-tests-version.json) into its ownspec-tests-comptests/directory, fully order-independent fromdownload-spec-tests.pnpm test:comptest— runs the suite via a dedicatedcomptestvitest project. The regular spec projects exclude it, sotest:spec/test:spec:minimalnever pick it up. A zero-test guard fails the run loudly when fixtures are absent (the workspace vitest config setspassWithNoTests: true).presets/fork_choice.test.tsintoutils/forkChoiceTestRunner.ts(mechanical move); the preset file and the new comptest entry file only register runners.comptests.yml, 03:00 UTC +workflow_dispatch), following the Kurtosis sim-tests pattern — most PRs don't touch fork choice, so per-PR runs are not worth the cost (~10 min per fork, single vitest worker).Runner/fork-choice changes needed for the new checks:
ForkChoice.getViableHeads()(test-only, not onIForkChoice): leaves of the spec's filtered block tree with weights, backing the suite'sviable_for_head_roots_and_weightscheck — head equivalence alone can't catch filtered-tree/weight regressions. Gloas payload-status variants are reported per(root, payload_status, weight)EFFECTIVE_BALANCE_INCREMENTunits and double-floors the boost score (up to 1.2 ETH below the spec's Gwei-precision value). Instead of comparing within a tolerance (which would mask small weight bugs), the runner normalizes the expected weight of the boosted entry using the productiongetCommitteeFractionand compares exactly.checks.head.payload_status(current vector shape),on_attestationone-slot-delay precondition, target-checkpoint shuffling for attestation decoding,bls_setting: 2(placeholder signatures) handling, payload-attestation negative-test completeness (slot mismatch is a spec no-op success; missing PTC membership is a rejection).specTestIterator: centralSkipOpts.skippedTestsnow composes with runner-localshouldSkip(previously silently ignored for runners defining their own, e.g. fork_choice).Results
Supersedes #9314 — the runner and fork-choice work from that PR is carried forward as cherry-picked commits with authorship preserved. Coauthored by @GrapeBaBa
Part of #7637