Skip to content

chore(refactor) - blind pkg#2802

Open
oleg-ssvlabs wants to merge 1 commit intostagefrom
refactor/blind-block
Open

chore(refactor) - blind pkg#2802
oleg-ssvlabs wants to merge 1 commit intostagefrom
refactor/blind-block

Conversation

@oleg-ssvlabs
Copy link
Copy Markdown
Contributor

Summary

Extract two small helpers from EnsureBlinded (in protocol/v2/blockchain/beacon/blind/blind.go) to reduce copy-paste duplication across the Capella/Deneb/Electra/Fulu branches.

Motivation

EnsureBlinded converts a full beacon block to its blinded form per fork. Each of the four fork branches was independently reimplementing:

  1. SSZ hash-tree-root computation for the transactions and withdrawals lists. The tx type ([]bellatrix.Transaction) and withdrawal type ([]*capella.Withdrawal) are identical across all four forks, but each branch repeated the same (&utilbellatrix.ExecutionPayloadTransactions{...}).HashTreeRoot() / (&utilcapella.ExecutionPayloadWithdrawals{...}).HashTreeRoot() boilerplate with its own error-wrapping.

  2. Construction of a deneb.ExecutionPayloadHeader. Deneb, Electra, and Fulu all use the exact same deneb.ExecutionPayloaddeneb.ExecutionPayloadHeader conversion, with the exact same 16 field assignments. A field added to deneb.ExecutionPayloadHeader previously required touching three call sites.

Ticket CQ-5 originally recommended extracting "common blinding logic into a generic helper parameterized by fork version." I'm intentionally not going that far — the four blinded block types (apiv1capella.BlindedBeaconBlock, apiv1deneb.BlindedBeaconBlock, apiv1electra.BlindedBeaconBlock used for both Electra and Fulu) have different body field sets and don't share a Go interface. A unified helper would require reflection or heavily-constrained generics for no readability win, and any future fork that changes the body layout would need to fork the abstraction anyway. The per-fork switch branches stay as-is; only the mechanically duplicated parts are factored out.

Changes

  • protocol/v2/blockchain/beacon/blind/blind.go:
    • Introduce computePayloadRoots(txs, withdrawals) (txRoot, wRoot, error) — used by all four fork branches.
    • Introduce buildDenebPayloadHeader(*deneb.ExecutionPayload) (*deneb.ExecutionPayloadHeader, error) — used by Deneb, Electra, and Fulu.
    • Capella branch still constructs its own capella.ExecutionPayloadHeader (different type, no blob fields) but now uses computePayloadRoots for the roots.

Net: 291 → 256 lines. Deneb/Electra/Fulu payload-header construction goes from ~20 lines of field copies each to a single helper call.

Closes https://github.com/ssvlabs/ssv-node-board/issues/560

@oleg-ssvlabs oleg-ssvlabs requested review from a team as code owners April 14, 2026 08:18
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.3%. Comparing base (41fab61) to head (c8b9bce).
⚠️ Report is 2 commits behind head on stage.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 14, 2026

Greptile Summary

This PR refactors EnsureBlinded in protocol/v2/blockchain/beacon/blind/blind.go by extracting two helpers — computePayloadRoots (used by all four fork branches) and buildDenebPayloadHeader (used by Deneb, Electra, and Fulu) — to eliminate repeated SSZ root computation and deneb.ExecutionPayloadHeader field-copy boilerplate. The behavior for all forks is preserved identically, and existing tests in blind_test.go cover all four fork paths (Capella, Deneb, Electra, Fulu).

Confidence Score: 5/5

  • This PR is safe to merge — it is a pure refactoring with no functional changes and all four fork paths are covered by existing tests.
  • Only one file is changed. The extracted helpers (computePayloadRoots and buildDenebPayloadHeader) are small, focused, and called with the same nil-guarded inputs as before. The return values for all fork cases (including FuluBlinded vs ElectraBlinded) are confirmed unchanged against the old code via git show. Existing tests in blind_test.go exercise all four fork branches. No P0 or P1 findings.
  • No files require special attention.

Important Files Changed

Filename Overview
protocol/v2/blockchain/beacon/blind/blind.go Refactors EnsureBlinded by extracting computePayloadRoots and buildDenebPayloadHeader helpers, reducing 291→256 lines with no functional changes. All fork branches preserved correctly; nil-guard checks remain before each helper call; error messages kept in computePayloadRoots.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[EnsureBlinded] --> B{Already blinded?}
    B -- yes --> C[Return as-is per fork]
    B -- no --> D{Fork version}

    D -- Capella --> E[computePayloadRoots\ntxs, withdrawals]
    E --> F[Build capella.ExecutionPayloadHeader\ninline]
    F --> G[Return CapellaBlinded]

    D -- Deneb --> H[buildDenebPayloadHeader\npayload]
    D -- Electra --> H
    D -- Fulu --> H

    H --> I[computePayloadRoots\ntxs, withdrawals]
    I --> J[Build deneb.ExecutionPayloadHeader]
    J --> K{Fork}
    K -- Deneb --> L[Return DenebBlinded]
    K -- Electra --> M[Return ElectraBlinded]
    K -- Fulu --> N[Return FuluBlinded]
Loading

Reviews (1): Last reviewed commit: "Refactor: blind pkg" | Re-trigger Greptile

// buildDenebPayloadHeader converts a Deneb-shaped ExecutionPayload into its header form.
// Deneb, Electra, and Fulu all use deneb.ExecutionPayload / deneb.ExecutionPayloadHeader,
// so one builder serves all three forks.
func buildDenebPayloadHeader(payload *deneb.ExecutionPayload) (*deneb.ExecutionPayloadHeader, error) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Might be worth adding a nil guard on payload here — all three callers already check before calling, so it's safe today, but a one-liner if payload == nil { return nil, fmt.Errorf("nil execution payload") } would be cheap insurance against future misuse.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this function is unexported(meaning it is tightly connected to local callers) and all local callers already do nil checks here, here and here, so I think it's OK not the nil guard here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants