Skip to content

Implement fast confirmation rule - #17122

Merged
terencechain merged 28 commits into
developfrom
fcr
Sep 7, 2026
Merged

Implement fast confirmation rule #17122
terencechain merged 28 commits into
developfrom
fcr

Conversation

@terencechain

Copy link
Copy Markdown
Collaborator

Implements the fast confirmation rule behind the --enable-fast-confirmation feature flag.

  • Adds a beacon-chain/confirmation package implementing the FCR algorithm: LMD-GHOST safety checks, FFG justification gates, epoch-boundary reconfirmation, and vote support aggregation
  • Runs once per slot at slot start after attestations are applied, under the forkchoice lock
  • Tracks the unrealized justified root per forkchoice node and adds read accessors for ancestry, voting source, and vote snapshots
  • Uses the confirmed root as the engine API safe block hash when the flag is enabled, falling back to unrealized justified otherwise
  • Wires up the fast_confirmation spec tests for altair through gloas (minimal preset), all 1190 vectors pass
  • Honors bls_setting: 2 in forkchoice spec test vectors, which ship unsigned blocks

@terencechain
terencechain force-pushed the fcr branch 4 times, most recently from 3e840d2 to 4e64fd7 Compare July 7, 2026 23:29
@terencechain terencechain changed the title Implement fast confirmation rule (FCR) Implement fast confirmation rule Jul 7, 2026
Comment thread beacon-chain/blockchain/service.go Outdated
Comment thread beacon-chain/confirmation/confirmation.go Outdated
Comment thread beacon-chain/blockchain/fcr_accessors.go Outdated
Comment thread beacon-chain/blockchain/fcr_accessors.go
Comment thread beacon-chain/confirmation/confirmation.go
Comment thread beacon-chain/blockchain/fcr_accessors.go Outdated
Comment thread beacon-chain/confirmation/support.go Outdated
@syjn99
syjn99 self-requested a review July 21, 2026 06:57
syjn99
syjn99 previously approved these changes Jul 21, 2026

@syjn99 syjn99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM from multiple rounds of review - I believe the implementation is correct as per spec. We might optimize this more after merging this because the feature is gated by a flag and other changes in our production path apart from FCR looks safe.

Copilot AI review requested due to automatic review settings July 21, 2026 17:53

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

Implements the Ethereum consensus “fast confirmation rule” (FCR) behind --enable-fast-confirmation, integrates it into beacon-node slot processing, exposes new forkchoice read APIs needed by the algorithm, and wires up minimal-preset spec tests.

Changes:

  • Add beacon-chain/confirmation package implementing FCR (safety thresholding + FFG gates + committee/vote aggregation) with unit tests.
  • Extend forkchoice with new read accessors (vote snapshots, ancestry queries, unrealized justification, slashed indices, confirmed payload hash) and track unrealized justified roots per node.
  • Add/enable FCR spec test runner + minimal spectests for Altair→Gloas, and wire FCR confirmed root into Engine API SafeBlockHash when enabled.

Reviewed changes

Copilot reviewed 54 out of 55 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
testing/spectest/shared/common/forkchoice/type.go Extend forkchoice spectest check schema with FCR fields + meta bls_setting.
testing/spectest/shared/common/forkchoice/runner.go Add fast-confirmation test runner path; refactor step handling helpers; honor bls_setting: 2.
testing/spectest/shared/common/forkchoice/builder.go Add FCR-enabled builder variant; run FCR per-slot in spectest harness; adjust attestation disparity for FCR vectors.
testing/spectest/shared/common/forkchoice/BUILD.bazel Add features dependency for FCR-enabled spectest builder.
testing/spectest/minimal/altair__fast_confirmation__fast_confirmation_test.go Add minimal-preset Altair FCR spec test entrypoint.
testing/spectest/minimal/bellatrix__fast_confirmation__fast_confirmation_test.go Add minimal-preset Bellatrix FCR spec test entrypoint.
testing/spectest/minimal/capella__fast_confirmation__fast_confirmation_test.go Add minimal-preset Capella FCR spec test entrypoint.
testing/spectest/minimal/deneb__fast_confirmation__fast_confirmation_test.go Add minimal-preset Deneb FCR spec test entrypoint.
testing/spectest/minimal/electra__fast_confirmation__fast_confirmation_test.go Add minimal-preset Electra FCR spec test entrypoint.
testing/spectest/minimal/fulu__fast_confirmation__fast_confirmation_test.go Add minimal-preset Fulu FCR spec test entrypoint.
testing/spectest/minimal/gloas__fast_confirmation__fast_confirmation_test.go Add minimal-preset Gloas FCR spec test entrypoint.
testing/spectest/minimal/BUILD.bazel Mark minimal spectest target large; include FCR spec test sources.
config/params/mainnet_config.go Add ConfirmationByzantineThreshold forkchoice/FCR constant to mainnet config.
config/params/config.go Add config field + YAML key for CONFIRMATION_BYZANTINE_THRESHOLD.
config/params/loader_test.go Update placeholder spec field list for new config param.
config/features/flags.go Add --enable-fast-confirmation feature flag.
config/features/config.go Wire CLI flag into feature config.
changelog/terence_fcr.md Add changelog entry for FCR feature.
beacon-chain/rpc/eth/config/handlers_test.go Update spec endpoint test expectations for new config param exposure.
beacon-chain/forkchoice/types/types.go Add VoteData type for exposing public vote snapshot fields to FCR.
beacon-chain/forkchoice/interfaces.go Extend forkchoice getter interfaces with ancestry/vote snapshot/justification/slashed/confirmed-hash APIs.
beacon-chain/forkchoice/ro.go Add locked RO delegations for newly-exposed forkchoice methods.
beacon-chain/forkchoice/ro_test.go Extend RO locking tests/mocks for new RO forkchoice API surface.
beacon-chain/forkchoice/doubly-linked-tree/types.go Track unrealizedJustifiedRoot per node for FCR justification queries.
beacon-chain/forkchoice/doubly-linked-tree/store.go Store unrealized justified root on insert.
beacon-chain/forkchoice/doubly-linked-tree/store_test.go Update tests for new store insert signature.
beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification.go Persist unrealized justified root during unrealized checkpoint updates.
beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go Update tests for new unrealized justified checkpoint setter.
beacon-chain/forkchoice/doubly-linked-tree/gloas.go Fix vote node resolution when full-node entry is absent (pre-Gloas paths).
beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go Add new forkchoice read APIs required by FCR; adjust latest-message semantics; expose slashed/vote snapshot/confirmed payload hash.
beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go Add tests for new forkchoice helper APIs and updated vote semantics.
beacon-chain/core/transition/trailing_slot_state_cache.go Add ClearNextSlotCache helper for isolating spectest runs.
beacon-chain/confirmation/types.go Add shared types for FCR (equivocation scorer + balance info container).
beacon-chain/confirmation/interfaces.go Define forkchoice/committee/balance accessor interfaces for FCR integration.
beacon-chain/confirmation/helpers.go Implement committee weight estimation + adversarial/proposer scoring helpers.
beacon-chain/confirmation/helpers_test.go Unit tests for committee weight/adversarial/proposer helper logic.
beacon-chain/confirmation/support.go Implement vote support aggregation and equivocation scoring helpers for FCR queries.
beacon-chain/confirmation/support_test.go Unit tests for support aggregation, equivocation, and table rebuild behavior.
beacon-chain/confirmation/safety.go Implement LMD-GHOST safety thresholding + is_one_confirmed.
beacon-chain/confirmation/safety_test.go Unit tests covering safety threshold, epoch crossing, optimistic blocks, and slot-0 behavior.
beacon-chain/confirmation/ffg.go Implement FFG target support estimation + justification gate helpers.
beacon-chain/confirmation/ffg_test.go Unit tests for FFG target scoring + honest-support computations.
beacon-chain/confirmation/confirmation.go Implement full FCR store + per-slot update/advance/revert algorithm.
beacon-chain/confirmation/confirmation_test.go Unit tests for key FCR store rotation + phase behaviors.
beacon-chain/confirmation/BUILD.bazel Add Bazel targets for FCR library and tests.
beacon-chain/blockchain/service.go Initialize FCR when flag enabled; compute engine SafeBlockHash from confirmed root when available.
beacon-chain/blockchain/receive_attestation.go Run FCR at slot start after applying attestations.
beacon-chain/blockchain/receive_execution_payload_envelope.go Use new safe hash selection (FCR confirmed root when enabled).
beacon-chain/blockchain/execution_engine.go Use new safe hash selection (FCR confirmed root when enabled).
beacon-chain/blockchain/receive_block.go Allow disabling block signature verification for spectests with unsigned blocks; add test helper method.
beacon-chain/blockchain/chain_info_forkchoice.go Expose Service.FCR() accessor for spectest builder/harness.
beacon-chain/blockchain/fcr_accessors.go Implement committee/balance accessors bridging Service state to FCR interfaces.
beacon-chain/blockchain/fcr_accessors_test.go Add test ensuring checkpoint balance source handles skipped epoch boundary correctly.
beacon-chain/blockchain/execution_engine_test.go Minor formatting fix in test setup call.
beacon-chain/blockchain/BUILD.bazel Wire new FCR accessors + confirmation dep into blockchain build and tests.

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

Comment on lines +49 to +56
if uint64(v) >= uint64(len(offs)) {
grown := make([]uint8, v+1)
copy(grown, offs)
for i := len(offs); i < len(grown); i++ {
grown[i] = noAssignment
}
offs = grown
}
currentSlot := uint64(tick) / params.BeaconConfig().SecondsPerSlot
for lastSlot < currentSlot {
lastSlot++
bb.service.SetForkChoiceGenesisTime(time.Now().Add(-1 * time.Duration(params.BeaconConfig().SecondsPerSlot*lastSlot) * time.Second))
}
}
if tick > int64(params.BeaconConfig().SecondsPerSlot*lastSlot) {
bb.service.SetForkChoiceGenesisTime(time.Now().Add(-1 * time.Duration(tick) * time.Second))
syjn99
syjn99 previously approved these changes Jul 22, 2026
Copilot AI review requested due to automatic review settings July 22, 2026 22:07

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 59 out of 60 changed files in this pull request and generated 3 comments.

Comment on lines +261 to +265
confirmedRoot := f.confirmedRoot
currentEpoch := slots.ToEpoch(currentSlot)
// The slot start snapshot keeps every phase on one head even if forkchoice moves mid run.
head := f.currentSlotHead

Comment on lines +641 to +644
if slots.IsEpochStart(currentSlot + 1) {
ujc := f.fc.UnrealizedJustifiedCheckpoint()
f.previousEpochGreatestUnrealizedCheckpoint = *ujc
}
Comment on lines +592 to +595
// DisableBlockSignatureVerificationForTesting supports spectest vectors generated with bls_setting 2, their blocks are unsigned.
func (s *Service) DisableBlockSignatureVerificationForTesting() {
s.skipBlockSignaturesForTesting = true
}

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.

Ohhh came here to write essentially the same :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@potuz what do you think of this way:

so we don't have to add skipBlockSignaturesForTesting field like this PR, and skip only for testing purpose via build flag.

syjn99 and others added 2 commits August 25, 2026 06:15
# Conflicts:
#	beacon-chain/forkchoice/ro_test.go
#	beacon-chain/rpc/eth/config/handlers_test.go
#	testing/spectest/minimal/BUILD.bazel

@potuz potuz 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.

Another quick pass, everything seems fine so far, a couple more of error handling that may result in a mistake, mostly nits this time.

}
balances := make([]uint64, st.NumValidators())
epoch := coreTime.CurrentEpoch(st)
for idx, val := range st.ValidatorsReadOnlySeq() {

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.

Here we do a whole validator sweep and it seems to me that the balances are not cached on PulledUpHeadState. BalanceInfoByCheckpoint does cache, should we cache here at least on the pair (head, epoch)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return nil, 0, err
}
if a.byCheckpoint == nil {
a.byCheckpoint = make(map[forkchoicetypes.Checkpoint]*confirmation.FFGStateInfo)

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.

This map is written under no lock. I think this is fine as FCR is single threaded, but then my comment on multiple calls to getFFG in the same slot can actually be a race if we every hit that path.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread beacon-chain/blockchain/service.go Outdated
if s.fcr != nil {
root := s.fcr.ConfirmedRoot()
if root != ([32]byte{}) {
return s.cfg.ForkChoiceStore.ConfirmedPayloadBlockHash(root)

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.

Can't the payloadBlockHash here return zero as well? is this a problem? shouldn't in this case return the uj one?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@potuz potuz 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.

This time I went over locks, there is no race now because there's a single thread that reads/writes I believe, but still there are some read locks being used to write and some fields read without any lock.

Those perhaps are not that important, but the O(n) under a forkchoice lock seems like a bad idea and can be avoided I think

Comment on lines +193 to +194
f.fc.RLock()
defer f.fc.RUnlock()

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.

This lock is taking over forkchoice and can stall block import. It does a full validator's sweep and it seems to me that the expensive part can be taken out of the lock, will mark in the relevant places.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +196 to +198
equivocating := f.fc.SlashedIndices()
f.votesBuf = f.fc.VoteSnapshot(f.votesBuf[:0])
votes := f.votesBuf

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.

Here we snapshot the votes (still taking about the fc.Lock)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equivocating := f.fc.SlashedIndices()
f.votesBuf = f.fc.VoteSnapshot(f.votesBuf[:0])
votes := f.votesBuf
f.support.Build(votes, balances, f.tables, equivocating, f.fc)

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.

Then we call this function that makes an O(n) loop on validators, but all the arguments are already copied from forkchoice, so this loop can be taken out of the lock itself, is there a race by doing so?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there is a race, the vote loop itself reads nothing from forkchoice but Build also walks the ancestry with ParentRoot to push support up, that part races with inserts. Split in b02664e, the O(n) aggregation runs off the lock and only the ancestor walk stays under it.

}

// Checkpoint state loads can replay from disk, keep them off the forkchoice lock.
balances, totalActiveBalance, err := f.balances.BalanceInfoByCheckpoint(ctx, f.currentEpochObservedJustifiedCheckpoint)

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.

This is a read without a lock 1/3

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var prevBalances []uint64
prevTotalActive := uint64(0)
if slots.IsEpochStart(currentSlot) {
prevBalances, prevTotalActive, err = f.balances.BalanceInfoByCheckpoint(ctx, f.previousEpochObservedJustifiedCheckpoint)

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.

This is a read without a lock 2/3

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// honest() needs the pulled up head state only on boundary, catch-up, or revert runs.
getFFG := sync.OnceValue(func() *FFGStateInfo {
ffg, err := f.balances.PulledUpHeadState(ctx, f.currentSlotHead)

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.

This is a read without a lock 3/3

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fastConfirmationDuration.WithLabelValues(path).Observe(float64(time.Since(start).Milliseconds()))
}(time.Now())

f.fc.RLock()

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.

This takes a read lock, but writes some

previousSlotHead                          [fieldparams.RootLength]byte  
currentSlotHead                           [fieldparams.RootLength]byte  
currentEpochObservedJustifiedCheckpoint   forkchoicetypes.Checkpoint   
previousEpochObservedJustifiedCheckpoint  forkchoicetypes.Checkpoint  
previousEpochGreatestUnrealizedCheckpoint forkchoicetypes.Checkpoint  

That are read unguarded in a few places marked below.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@potuz potuz 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.

Pointing that we seem to be not even reading the confirmed head from the forkchoice tests vectors.

CurrentSlotHead *string `json:"current_slot_head"`
PreviousEpochObservedJustifiedCheckpoint *EpochRoot `json:"previous_epoch_observed_justified_checkpoint"`
CurrentEpochObservedJustifiedCheckpoint *EpochRoot `json:"current_epoch_observed_justified_checkpoint"`
PreviousEpochGreatestUnrealizedCheckpoint *EpochRoot `json:"previous_epoch_greatest_unrealized_checkpoint"`

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.

it seems to me we are not checking the fast confirmed head in tests

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirmed_root was already checked in the builder, but you're right there was a gap, we were silently dropping safe_execution_block_hash from the checks. Added in 206559f

@potuz potuz 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.

Last round, still have the dominant lock of O(n) and the in-state computation of unrealized justification to cover, but other than that it seems good to go.

Comment thread beacon-chain/confirmation/support.go Outdated
}
for _, t := range s.tables {
if sl, ok := t.assignedSlot(idx); ok && sl >= startSlot && sl <= endSlot {
total += s.balances[idx]

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.

I think here we are counting equivocators that are onchain with balance zero and the spec would count this balance 1/2

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

balances := make([]uint64, st.NumValidators())
epoch := coreTime.CurrentEpoch(st)
for idx, val := range st.ValidatorsReadOnlySeq() {
if helpers.IsActiveValidatorUsingTrie(val, epoch) && !val.Slashed() {

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.

Here we do not count slashed validators in the balance 2/2.

I believe in the previous message (marked 1/2) we needed to count the slashed balance. This is the whole mechanism to recover fast confirmation even in the presence of slashing.

@terencechain terencechain Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 059783f. Checked all the other consumers, attestation score, block support between slots, and current target score all want unslashed per spec

potuz
potuz previously approved these changes Sep 4, 2026

@potuz potuz 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.

I did my best on reviewing this one thoroughly, it's such a massive change that probably more eyes would be good anyway. I did find a series of bugs, and one is remaining which is the issue of the unrealized justification not being computed before slot 21, I think it's safe anyway and fine to ship right now, but we should open an issue to fix that

@terencechain
terencechain added this pull request to the merge queue Sep 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 5, 2026
pull Bot pushed a commit to wsnchristopher/prysm that referenced this pull request Sep 5, 2026
…s#17281)

**What type of PR is this?**

> Other: Spectest harness

**What does this PR do? Why is it needed?**

- OffchainLabs#17122 

The PR above says "Honors `bls_setting: 2` in forkchoice spec test
vectors, which ship unsigned blocks". `bls_setting` is not yet widely
used, but in the FCR context, ALL FCR tests are based on `bls_setting:
2` for faster test duration. OffchainLabs#17122 solves this problem ad-hoc like
adding `skipBlockSignaturesForTesting` field and skips the signature
verification like:

```go
	if s.skipBlockSignaturesForTesting {
		_, postState, err = transition.ExecuteStateTransitionNoVerifyAnySig(ctx, preState, signed)
	} else {
		postState, err = transition.ExecuteStateTransition(ctx, preState, signed)
	}
```

I think this can be much improved by enforcing so-called "fake" BLS
backend that returns "yes" for every signature verification request.
This compile-time approach has several benefits:

- We don't have to touch the production path like above.
- Some tests like
`test_is_one_confirmed_fails_recently_activated_validator_voting_in_empty_slot`
assumes a deposit process. We should also add some field like
`skipDepositSignatures` in order to include the new deposit request
(with stub signature).

**Which issue(s) does this PR fix?**

N/A

**Other notes for review**

Verified with the latest spectest fixtures + merge this work with `fcr`
branch and test.

Here's a matrix for every cases regarding `meta.yaml` and `bls_seting`:

| Case's `meta.yaml` | Meaning in the spec | `bls=real` (default) |
`bls=fake` |
| --- | --- | --- | --- |
| no `meta.yaml` | no metadata at all | ✅ run | ✅ run |
| no `bls_setting` key (only `blocks_count`, etc.) | unspecified = same
as `0` | ✅ run | ✅ run |
| `bls_setting: 0` | BLS optional — valid either way | ✅ run | ✅ run |
| `bls_setting: 1` | BLS required — only valid with BLS on | ✅ run | ⏭️
skip |
| `bls_setting: 2` | BLS ignored — only valid with BLS off | ⏭️ skip | ✅
run |
| any other value (e.g. `3`) | unknown | ❌ hard fail | ❌ hard fail |
| `realOnlyCases`, 3 entries (`utils.go:78`) | no metadata, but needs
real verification | ✅ run | ⏭️ skip |


**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description with sufficient context for reviewers
to understand this PR.
- [x] I have tested that my changes work as expected and I added a
testing plan to the PR description (if applicable).
@terencechain
terencechain added this pull request to the merge queue Sep 7, 2026
Merged via the queue into develop with commit e038e27 Sep 7, 2026
36 of 37 checks passed
@terencechain
terencechain deleted the fcr branch September 7, 2026 05:59
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.

4 participants