Skip to content

Differentiate between Consensus and Cluster Headers storage#8222

Open
tim-barry wants to merge 73 commits intomasterfrom
tim/4204-split-header-storage-by-chainid
Open

Differentiate between Consensus and Cluster Headers storage#8222
tim-barry wants to merge 73 commits intomasterfrom
tim/4204-split-header-storage-by-chainid

Conversation

@tim-barry
Copy link
Contributor

@tim-barry tim-barry commented Dec 4, 2025

A ChainID must now be provided to Headers storage instance on creation.
That storage instance will then only be able to successfully store or retrieve headers corresponding to the correct ChainID. In addition, the height-based index will also be specific to that ChainID.

Also adds 3 new error sentinels:

  • storage.ErrWrongChain for storing or retrieving headers with an unexpected/mismatched ChainID
  • storage.ErrNotAvailableForClusterConsensus for methods not implemented by cluster headers storage (ByView)
  • operation.IncompleteStateError for failure to load protocol-mandated info from the database - should be treated as an exception, except when determining whether or not to bootstrap a node at startup

and fixes a bug where cluster blocks from the previous epoch+chain would be unnecessarily checked for duplicate transactions.

Closes: #4204

Summary by CodeRabbit

  • New Features

    • Chain-aware header constructors and runtime chain ID detection to support cluster vs consensus chains.
  • Improvements

    • Storage initialization now derives/accepts chain context and surfaces initialization errors earlier.
    • Storage components are chain-aware to prevent cross-chain mixing and clarify behavior.
  • Bug Fixes

    • New sentinel errors for wrong-chain, unavailable cluster operations, and incomplete bootstrapping.
  • Tests

    • Expanded coverage for chain-aware behavior, cluster scenarios, and related error cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 4, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@tim-barry tim-barry marked this pull request as ready for review December 8, 2025 18:03
@tim-barry tim-barry requested a review from a team as a code owner December 8, 2025 18:03

for _, blockID := range clusterBlockIDs {
header, err := b.clusterHeaders.ByBlockID(blockID)
header, err := b.clusterHeaders.ByBlockID(blockID) // TODO(4204) transaction deduplication crosses clusterHeaders epoch boundary
Copy link
Member

Choose a reason for hiding this comment

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

Transaction de-duplication actually does not occur across cluster and epoch boundaries.

  • Each transaction is uniquely assigned to one cluster in one epoch, based on the transaction's reference block (see ingestion logic)
  • Therefore, each cluster has a range of reference block heights they can accept. These ranges are equivalent to the height range of blocks within an epoch ($[FirstBlockInEpoch.Height, LastBlockInEpoch.Height]$. These ranges are consecutive and do not overlap.
    • In short, if we are considering a cluster block with reference block height $FirstBlockInEpoch.Height$, then minRefHeight is actually $FirstBlockInEpoch.Height$ (we don't need to search further back).
  • We already take this into account when determining the lowest possible reference block

So I think we can remove this TODO, and remove the special-case logic in storage.Headers meant to work around this. I would also suggest adding some documentation here explaining why there is no overlap between clusters and epochs.

Copy link
Contributor Author

@tim-barry tim-barry Dec 12, 2025

Choose a reason for hiding this comment

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

Thank you for clarifying this explicitly. Now that I understand better what's going on, I can say that while you are right that that's what should be happening, it is not, due to two bugs 🐞🐞. The existing behaviour did actually look across the epoch boundary (now causing clustover_switchover_test to fail), because:

I believe this means that this particular lookup was always checking approximately the past ~1200 reference heights (flow.DefaultTransactionExpiry * 2), regardless of which epoch those heights were in.

While the first bug also affects payload construction, I don't think it has impacted correctness (essentially transactions from the previous epoch would not immediately be considered expired, but they are already split into pools by epoch anyways as you have noted, so transactions from a different epoch will not be encountered.)

Copy link
Member

Choose a reason for hiding this comment

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

You're right. Oops, that was introduced by me 🫣.

Here's what I think we should do:

  • bring back the logic that populates refEpochFirstHeight
    • Check for ErrNotFound -- if we see that error, then set refEpochFirstHeight to zero. This means that we have just joined the network, and that our local state cutoff (our root block) is newer than refEpochFirstHeight.

Copy link
Contributor Author

@tim-barry tim-barry Dec 12, 2025

Choose a reason for hiding this comment

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

done in e397124 - does the comment on expected behaviour from setting refEpochFirstHeight = 0 match with what you expect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To double-check: the minimum reference height to check for a duplicated transaction is approximately finalizedHeight - 2*DefaultTransactionExpiry, because of the following situation:

Collection                    C1           C2
                  ┏━━━━━━━━━━━┫
Transaction       T1          T2
                  ┊           ┊
Reference Block   R1──────────R2──────────Head
                    <-Expiry->  <-Expiry->

The new collection C2 could barely include transaction T2; to ensure we deduplicate correctly, we need to check collection C1, which has reference block R1 (because it could just barely include transaction T1).

Copy link
Member

Choose a reason for hiding this comment

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

@tim-barry I would go off this function and this documentation.

When we are inserting C2, the range we need to check is [C2MinRefHeight-Expiry, C2MaxRefHeight]

C2MinRefHeight is both the reference block height of C2, and the smallest reference block height of all transactions in C2. C2MaxRefHeight is the largest reference block height of all transactions in C2.

@tim-barry tim-barry requested review from zhangchiqing and removed request for zhangchiqing January 12, 2026 17:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
storage/store/headers.go (1)

312-323: Missing documentation for cluster error case.

For cluster headers, BlockIDByView will return ErrNotAvailableForClusterConsensus via the viewCache, but this isn't documented. The doc only mentions ErrNotFound.

📝 Suggested doc update
 // BlockIDByView returns the block ID that is certified at the given view. It is an optimized
 // version of `ByView` that skips retrieving the block. Expected errors during normal operations:
 //   - [storage.ErrNotFound] if no certified block is known at given view.
+//   - [storage.ErrNotAvailableForClusterConsensus] if called on a cluster Headers instance
 //
 // NOTE: this method is not available until next spork (mainnet27) or a migration that builds the index.
🤖 Fix all issues with AI agents
In @storage/store/headers.go:
- Around line 175-180: The doc comment above the method retrieveProposalTx
incorrectly names the function "retrieveTx"; update the comment to reference the
correct method name retrieveProposalTx and ensure the description still matches
this method's behavior (returning a proposal header and proposer signature) and
lists the same expected errors; adjust any other comment occurrences around the
Headers type or this function to use retrieveProposalTx consistently.
🧹 Nitpick comments (3)
storage/store/headers.go (3)

43-46: Duplicate lock validation with operation.InsertHeader.

The lock check here duplicates the validation already performed inside operation.InsertHeader (which returns an error if the lock isn't held). While defensive coding is reasonable, this adds maintenance overhead if lock requirements change.

Consider removing the redundant check here, or document why the pre-check is intentional (e.g., to fail faster before any other operations).


84-87: Misleading parameter name: height should be view.

The parameter is named height but represents a view value. This is a no-op function returning an error, but the parameter name should match semantics for clarity and consistency with retrieveView type signature.

♻️ Suggested fix
-	retrieveView := func(r storage.Reader, height uint64) (flow.Identifier, error) {
+	retrieveView := func(r storage.Reader, view uint64) (flow.Identifier, error) {
 		var id flow.Identifier
 		return id, storage.ErrNotAvailableForClusterConsensus
 	}

99-110: Minor: Unnecessary error variable in return.

On line 109, err is guaranteed to be nil at this point since line 103 returns early on error. Returning nil explicitly would be clearer.

♻️ Suggested fix
 		if header.ChainID != chainID {
 			return nil, fmt.Errorf("expected chain ID %v, got %v: %w", chainID, header.ChainID, storage.ErrWrongChain)
 		}
-		return &header, err
+		return &header, nil
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7796d9a and e9eedc4.

📒 Files selected for processing (1)
  • storage/store/headers.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • storage/store/headers.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/store/headers.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/store/headers.go
🧠 Learnings (5)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/headers.go
🧬 Code graph analysis (1)
storage/store/headers.go (5)
storage/headers.go (1)
  • Headers (15-65)
state/cluster/root_block.go (1)
  • IsCanonicalClusterID (26-28)
storage/errors.go (2)
  • ErrWrongChain (37-37)
  • ErrNotAvailableForClusterConsensus (41-41)
storage/locks.go (2)
  • LockInsertBlock (14-14)
  • LockInsertOrFinalizeClusterBlock (22-22)
storage/operation/headers.go (5)
  • InsertHeader (26-42)
  • LookupBlockHeight (142-144)
  • LookupCertifiedBlockByView (149-151)
  • InsertClusterHeader (57-73)
  • RetrieveHeader (79-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Lint (./)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (9)
storage/store/headers.go (9)

18-27: LGTM!

The chainID field addition to the Headers struct is appropriate for binding header storage to a specific chain.


134-161: LGTM!

The storeTx method correctly delegates chain ID validation to the storeWithLock closure, and the documentation is properly updated to reflect the new ErrWrongChain error.


163-173: LGTM!

Chain ID validation is correctly performed via the retrieve closure in the cache layer.


201-207: LGTM!

Documentation correctly updated to reflect chain ID validation.


209-216: LGTM!

Documentation correctly updated.


218-228: LGTM!

Height lookup correctly uses chain-specific index, and chain ID validation occurs during header retrieval.


230-244: LGTM!

The ByView method correctly returns ErrNotAvailableForClusterConsensus for cluster headers via the retrieveView closure.


246-260: Consider implications of Exists not validating chain ID.

The documentation explicitly states this method doesn't distinguish between cluster and consensus headers. While this is clearly documented, callers using Exists followed by ByBlockID could get inconsistent results:

  • Exists(blockID) returns true (header exists)
  • ByBlockID(blockID) returns ErrWrongChain (wrong chain)

If this is intentional for performance reasons (avoiding header deserialization), the current documentation is sufficient. Otherwise, consider whether this asymmetry could cause subtle bugs in calling code.


273-310: LGTM!

Children headers are correctly validated via ByBlockID, which enforces chain ID matching.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
storage/store/headers.go (1)

251-265: Use irrecoverable.NewExceptionf instead of fmt.Errorf at line 262.

The method documents "No errors are expected during normal operation" (line 254), which per Flow's coding conventions indicates an exception scenario. Exceptions must be wrapped with irrecoverable.NewExceptionf, not fmt.Errorf. This is consistent with how callers handle this method's errors—for example, receipt_validator.go:248 wraps the result with irrecoverable.NewExceptionf.

Regarding the cross-chain visibility: The documented behavior is intentional. Call sites (receipt_validator.go:246, compliance/core.go:205,212, state.go:910) appropriately handle the lack of chain distinction by checking existence and treating the result as a binary fact independent of which chain the header came from.

🤖 Fix all issues with AI agents
In @storage/store/headers.go:
- Around line 99-110: In the retrieve closure (function retrieve) change the
final return to explicitly return nil for the error instead of reusing the
variable err; locate the function named retrieve (and the call to
operation.RetrieveHeader) and replace the trailing "return &header, err" with
"return &header, nil" so the success path clearly returns a nil error.
- Around line 175-180: The doc comment above retrieveProposalTx refers to the
wrong method name "retrieveTx"; update the comment to correctly reference
"retrieveProposalTx" and ensure the description matches the method (e.g.,
"retrieveProposalTx returns a proposal header..."), leaving the rest of the
comment (expected errors and behavior) unchanged so it accurately documents the
retrieveProposalTx function.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9eedc4 and ad56c4f.

📒 Files selected for processing (1)
  • storage/store/headers.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • storage/store/headers.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/store/headers.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/store/headers.go
🧠 Learnings (5)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/headers.go
🧬 Code graph analysis (1)
storage/store/headers.go (5)
storage/headers.go (1)
  • Headers (15-65)
state/cluster/root_block.go (1)
  • IsCanonicalClusterID (26-28)
storage/errors.go (2)
  • ErrWrongChain (37-37)
  • ErrNotAvailableForClusterConsensus (41-41)
storage/locks.go (2)
  • LockInsertBlock (14-14)
  • LockInsertOrFinalizeClusterBlock (22-22)
storage/operation/headers.go (3)
  • InsertHeader (26-42)
  • InsertClusterHeader (57-73)
  • RetrieveHeader (79-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (7)
storage/store/headers.go (7)

18-27: LGTM!

The chainID field is appropriately added as an immutable field set at construction time, ensuring thread-safe access without additional synchronization.


35-58: LGTM!

The constructor correctly validates that non-cluster chain IDs are used, and the storeWithLock closure properly validates both chain ID consistency and lock ownership before delegating to operation.InsertHeader. The lock validation at lines 43-45 provides defense-in-depth alongside the check in InsertHeader.


61-89: LGTM!

The cluster constructor correctly validates cluster chain IDs, uses the appropriate cluster-specific operations (InsertClusterHeader, LookupClusterBlockHeight), and properly returns ErrNotAvailableForClusterConsensus for view-based lookups, which aligns with the interface contract.


134-161: LGTM!

The documentation correctly documents the new ErrWrongChain error condition, and the implementation properly delegates chain validation to the storeWithLock closure configured at construction.


192-204: LGTM!

The documentation clearly explains the chain-safety guarantee: the height cache is populated using a chain-specific database index, so only blocks of the respective chain can be returned. This is a valuable clarification.


337-369: No action needed. The executed block index is intentionally designed to store potentially non-canonical blocks (including fork blocks), as documented in storage/operation/executed.go. Chain validation in RollbackExecutedBlock would be incorrect here; the lack of validation is proper by design.

Likely an incorrect or invalid review comment.


330-335: Deprecation of FindHeaders is appropriate and safe.

Marking this method as deprecated with the reason "Undocumented, hence unsafe for public use" is justified. This method is not part of the storage.Headers interface and correctly bypasses chain validation—operation.FindHeaders iterates through all headers in the database without checking ChainID, unlike all interface methods (ByBlockID, ByHeight, ByView, etc.) which explicitly validate chain membership. The method has no usages in the codebase and provides unrestricted cross-chain header access, making it unsafe for public consumption.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
storage/store/blocks.go (1)

127-127: Minor grammar nit: "Expected errors returns" is awkward.

The phrase appears on multiple lines (127, 138, 149, 162, 174, 187, 204, 224). Consider using "Expected error returns" (consistent with line 58) or "Expected errors returned" for grammatical consistency.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad56c4f and 187dbf7.

📒 Files selected for processing (3)
  • storage/cluster_blocks.go
  • storage/operation/heights_test.go
  • storage/store/blocks.go
🧰 Additional context used
📓 Path-based instructions (4)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • storage/cluster_blocks.go
  • storage/operation/heights_test.go
  • storage/store/blocks.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/cluster_blocks.go
  • storage/operation/heights_test.go
  • storage/store/blocks.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/cluster_blocks.go
  • storage/operation/heights_test.go
  • storage/store/blocks.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests should be co-located with the code they test
Follow the existing pattern of *_test.go files for test naming
Use fixtures for realistic test data as defined in /utils/unittest/

Files:

  • storage/operation/heights_test.go
🧬 Code graph analysis (1)
storage/operation/heights_test.go (2)
storage/operation/heights.go (3)
  • RetrieveFinalizedHeight (46-58)
  • IncompleteStateError (21-21)
  • RetrieveSealedHeight (82-94)
storage/errors.go (1)
  • ErrNotFound (17-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (6)
storage/cluster_blocks.go (1)

20-20: LGTM!

The documentation updates correctly use the Go 1.19+ godoc link syntax [storage.ErrNotFound] to create hyperlinks to the error type in generated documentation. This is a nice improvement for documentation consistency across the codebase.

Also applies to: 29-29

storage/operation/heights_test.go (2)

17-27: LGTM!

The test correctly validates the sentinel error behavior for non-bootstrapped databases. The dual assertion (ErrorIs for IncompleteStateError and NotErrorIs for ErrNotFound) properly verifies that the error wrapping masks the low-level ErrNotFound as intended.


29-39: LGTM!

Symmetric test for sealed height retrieval. Together with TestNonBootstrappedRetrieveFinalizedHeight, this provides complete coverage for the IncompleteStateError sentinel behavior on non-bootstrapped databases.

storage/store/blocks.go (3)

42-42: Documentation correctly reflects new ErrWrongChain behavior for ID-based lookups.

The addition of storage.ErrWrongChain to the documented error returns is appropriate for methods that accept arbitrary block IDs from callers (BatchStore, retrieve, retrieveProposal, ByID, ProposalByID). These methods can legitimately encounter blocks from a different chain than the one the storage instance is bound to.

Also applies to: 60-60, 93-93, 127-129, 138-140


149-157: Verify: ErrWrongChain could propagate from ByID but is not documented.

ByView calls ByID(blockID) on line 156, which documents ErrWrongChain as an expected error. If the view index ever contains a block ID from a different chain (e.g., due to data corruption), this error would propagate upward.

The current documentation assumes the view index is always consistent with the bound chain, which should hold in normal operation. If this is the intended contract, consider adding a brief comment clarifying this assumption, or documenting that ErrWrongChain is a possible (though unexpected) return.


204-225: Inconsistency with AI summary regarding ErrWrongChain documentation.

The AI summary states ErrWrongChain was documented for ByCollectionID and BlockIDByCollectionID, but the actual documentation only lists ErrNotFound.

If this is intentional (collection lookups use chain-specific indices), the current documentation is correct. Please confirm this matches the expected behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
storage/store/headers.go (2)

99-110: Minor: Redundant error return on line 109.

The err variable is guaranteed to be nil after the check on line 102-104. Returning err on line 109 is technically correct but slightly misleading.

✨ Suggested improvement
-		return &header, err
+		return &header, nil

330-335: Deprecation notice added to FindHeaders.

Good addition of the deprecation notice. Consider also adding a // Deprecated: comment that Go tooling recognizes.

✨ Suggested improvement for tooling recognition
-// Deprecated: Undocumented, hence unsafe for public use.
+// Deprecated: FindHeaders is undocumented, hence unsafe for public use.
+// This method does not filter by chainID and may return headers from any chain.
engine/collection/epochmgr/factories/cluster_state.go (1)

74-78: Verify the error return on the success path.

Line 78 returns err which is the error from NewMutableState on line 74. If NewMutableState succeeds, err is nil, so this is correct. However, the pattern is inconsistent with earlier returns that explicitly return nil for the error on success paths (not shown in this diff, but typical Go style).

For consistency, consider returning nil explicitly on the success path:

✨ Suggested improvement for explicit nil return
-	return mutableState, clusterHeaders, clusterPayloads, clusterBlocks, consensusHeaders, err
+	return mutableState, clusterHeaders, clusterPayloads, clusterBlocks, consensusHeaders, nil

This makes it clearer that this is a success path. However, returning err is not incorrect since it will be nil after the error check on line 75-77.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 187dbf7 and 355b43f.

📒 Files selected for processing (3)
  • engine/collection/epochmgr/engine_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
  • storage/store/headers.go
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • engine/collection/epochmgr/factories/cluster_state.go
  • storage/store/headers.go
  • engine/collection/epochmgr/engine_test.go
{module,engine,cmd}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

All major processing components must implement the Component interface from /module/component/component.go to ensure consistent lifecycle management and graceful shutdown patterns

Files:

  • engine/collection/epochmgr/factories/cluster_state.go
  • engine/collection/epochmgr/engine_test.go
{crypto,fvm,ledger,access,engine}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Files:

  • engine/collection/epochmgr/factories/cluster_state.go
  • engine/collection/epochmgr/engine_test.go
{network,engine,consensus}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Network messages must be authenticated and validated

Files:

  • engine/collection/epochmgr/factories/cluster_state.go
  • engine/collection/epochmgr/engine_test.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/store/headers.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/store/headers.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests should be co-located with the code they test
Follow the existing pattern of *_test.go files for test naming
Use fixtures for realistic test data as defined in /utils/unittest/

Files:

  • engine/collection/epochmgr/engine_test.go
🧠 Learnings (5)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • engine/collection/epochmgr/factories/cluster_state.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/headers.go
🧬 Code graph analysis (1)
engine/collection/epochmgr/engine_test.go (2)
model/flow/chain.go (1)
  • ChainID (14-14)
state/protocol/mock/params.go (1)
  • NewParams (171-181)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (9)
engine/collection/epochmgr/engine_test.go (4)

70-73: LGTM! Cluster state params mock properly wired.

The mock correctly sets up ChainID() on cluster params and wires it through components.state.On("Params"). The .Maybe() calls are appropriate since not all tests may exercise these paths.


111-140: LGTM! Factory mock correctly updated for new arity.

The Create mock now accepts mock.Anything for the consensusChainID parameter, and all returned provider functions are updated to take (epoch realprotocol.CommittedEpoch, consensusChainID flow.ChainID) as parameters. The implementation correctly ignores the consensusChainID in tests since the mock components don't need chain-specific behavior.


167-169: LGTM! Protocol state params mock properly configured.

The test setup correctly creates a protocol.Params mock and configures ChainID() to return a test chain ID. This aligns with the engine's new requirement to retrieve the consensus chain ID from protocol state params.


278-282: LGTM! Unauthorized node mock correctly updated.

The unauthorizedMatcher is properly applied with mock.Anything for the consensusChainID parameter, maintaining consistency with the updated Create signature.

storage/store/headers.go (3)

31-59: LGTM! Consensus headers constructor with proper validation.

The constructor correctly:

  1. Validates that the chainID is not a cluster ID using cluster.IsCanonicalClusterID
  2. Returns an irrecoverable exception for misconfiguration
  3. Validates chainID consistency in storeWithLock
  4. Requires proper lock acquisition before insertion

61-89: LGTM! Cluster headers constructor with appropriate limitations.

The constructor correctly:

  1. Validates that the chainID is a canonical cluster ID
  2. Uses cluster-specific operations (InsertClusterHeader, LookupClusterBlockHeight)
  3. Returns storage.ErrNotAvailableForClusterConsensus for view lookups (as cluster consensus doesn't support this)
  4. Requires LockInsertOrFinalizeClusterBlock instead of LockInsertBlock

251-253: Documentation accurately reflects the method's cross-chain behavior.

The Exists method's documentation correctly notes that it does not distinguish between cluster and consensus headers because it checks block existence by ID without loading the header type. Examination of call sites across the codebase confirms proper usage: consensus nodes call this method for consensus headers, collection nodes for cluster headers, and the underlying store architecture prevents cross-chain confusion. All callers properly handle errors.

engine/collection/epochmgr/factories/cluster_state.go (2)

37-55: LGTM! Factory correctly creates chain-specific headers.

The factory now properly separates concerns:

  1. clusterHeaders for cluster block headers (using NewClusterHeaders)
  2. consensusHeaders for reference block lookups (using NewHeaders)

The comment on line 52 clearly explains the purpose of consensusHeaders for reference blocks.


63-66: LGTM! OpenState updated with cluster-specific storage.

The call to clusterkv.OpenState now correctly receives clusterHeaders and clusterPayloads instead of the previous generic headers/payloads, ensuring cluster state uses the appropriate chain-bound storage.

Copy link
Member

@AlexHentschel AlexHentschel left a comment

Choose a reason for hiding this comment

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

👏 Very nice and clear Thanks for grinding through the large change surface.

Prefer constructor if present

Co-authored-by: Alexander Hentschel <alex.hentschel@flowfoundation.org>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go (1)

28-32: Variable shadows imported package name.

The variable metrics on line 28 shadows the imported metrics package. This can cause confusion and potential issues if someone tries to use the package elsewhere in the function.

♻️ Suggested fix
-			metrics := metrics.NewNoopCollector()
-			setups := store.NewEpochSetups(metrics, db)
-			epochCommits := store.NewEpochCommits(metrics, db)
-			epochProtocolStateEntries := store.NewEpochProtocolStateEntries(metrics, setups, epochCommits, db,
+			collector := metrics.NewNoopCollector()
+			setups := store.NewEpochSetups(collector, db)
+			epochCommits := store.NewEpochCommits(collector, db)
+			epochProtocolStateEntries := store.NewEpochProtocolStateEntries(collector, setups, epochCommits, db,
 				store.DefaultEpochProtocolStateCacheSize, store.DefaultProtocolStateIndexCacheSize)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 355b43f and e4d1235.

📒 Files selected for processing (9)
  • admin/commands/storage/read_range_cluster_blocks.go
  • cmd/util/cmd/read-badger/cmd/blocks.go
  • cmd/util/cmd/read-badger/cmd/collections.go
  • cmd/util/cmd/read-badger/cmd/epoch_commit.go
  • cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go
  • cmd/util/cmd/read-badger/cmd/guarantees.go
  • cmd/util/cmd/read-badger/cmd/protocol_kvstore.go
  • cmd/util/cmd/read-badger/cmd/seals.go
  • cmd/util/cmd/read-badger/cmd/transactions.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • cmd/util/cmd/read-badger/cmd/guarantees.go
  • cmd/util/cmd/read-badger/cmd/protocol_kvstore.go
  • cmd/util/cmd/read-badger/cmd/blocks.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go
  • admin/commands/storage/read_range_cluster_blocks.go
  • cmd/util/cmd/read-badger/cmd/transactions.go
  • cmd/util/cmd/read-badger/cmd/collections.go
  • cmd/util/cmd/read-badger/cmd/seals.go
  • cmd/util/cmd/read-badger/cmd/epoch_commit.go
{module,engine,cmd}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

All major processing components must implement the Component interface from /module/component/component.go to ensure consistent lifecycle management and graceful shutdown patterns

Files:

  • cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go
  • cmd/util/cmd/read-badger/cmd/transactions.go
  • cmd/util/cmd/read-badger/cmd/collections.go
  • cmd/util/cmd/read-badger/cmd/seals.go
  • cmd/util/cmd/read-badger/cmd/epoch_commit.go
🧠 Learnings (1)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {module,engine,cmd}/**/*.go : All major processing components must implement the `Component` interface from `/module/component/component.go` to ensure consistent lifecycle management and graceful shutdown patterns

Applied to files:

  • admin/commands/storage/read_range_cluster_blocks.go
🧬 Code graph analysis (3)
cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go (1)
storage/store/epoch_protocol_state.go (2)
  • DefaultEpochProtocolStateCacheSize (19-19)
  • DefaultProtocolStateIndexCacheSize (23-23)
admin/commands/storage/read_range_cluster_blocks.go (3)
storage/store/cluster_payloads.go (1)
  • ClusterPayloads (16-19)
storage/store/headers.go (1)
  • NewClusterHeaders (66-89)
model/flow/chain.go (1)
  • ChainID (14-14)
cmd/util/cmd/read-badger/cmd/transactions.go (1)
storage/inmemory/transactions_reader.go (1)
  • NewTransactions (14-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (10)
admin/commands/storage/read_range_cluster_blocks.go (2)

53-59: LGTM! Runtime creation of cluster headers aligns with the PR objectives.

The approach of creating clusterHeaders at request time is correct since the chainID is only available from the request parameters. The error from NewClusterHeaders is properly handled, and NewClusterHeaders internally validates that the provided chainID is a valid cluster chain ID (returning an error for non-cluster chain IDs).


29-34: Constructor simplification looks good.

Removing the headers parameter from the constructor is appropriate since cluster headers are now created at runtime with the request-specific chainID. This aligns with the PR's goal of binding ChainID to each Headers instance at construction time.

cmd/util/cmd/read-badger/cmd/collections.go (3)

33-34: Clean migration to store-based abstractions.

The refactor from storages-based access to the new store.NewTransactions and store.NewCollections constructors is consistent with the broader PR changes. Error handling for all subsequent collection operations is properly implemented.


47-61: Method calls correctly updated.

The collections.LightByID and collections.ByID calls properly replace the previous storages.Collections.* pattern. Error handling is preserved.


72-78: Transaction-based lookup updated correctly.

The collections.LightByTransactionID call maintains the same error handling pattern as before.

cmd/util/cmd/read-badger/cmd/transactions.go (1)

28-37: Consistent refactor to store-based transactions.

The change from storages-based access to store.NewTransactions follows the same pattern as collections.go. The transactions.ByID call and error handling are correctly implemented.

Note: The storage/inmemory/transactions_reader.go snippet shows a different NewTransactions constructor (for in-memory testing with transaction bodies), which is in a separate package and serves a different purpose—no conflict here.

cmd/util/cmd/read-badger/cmd/seals.go (1)

30-30: LGTM!

The migration to the store-based initialization with store.NewSeals is clean and correct. Using metrics.NewNoopCollector() is appropriate for this read-only CLI utility.

cmd/util/cmd/read-badger/cmd/epoch_protocol_state.go (1)

41-44: LGTM!

The updated access pattern using the newly constructed epochProtocolStateEntries is correct, and error handling is properly preserved.

cmd/util/cmd/read-badger/cmd/epoch_commit.go (2)

30-30: LGTM!

The migration to store.NewEpochCommits follows the established pattern consistently.


39-42: LGTM!

The updated access pattern is correct and error handling is properly maintained.

Co-authored-by: Alexander Hentschel <alex.hentschel@flowfoundation.org>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
storage/store/headers.go (2)

329-334: FindHeaders is unused and should be removed.

FindHeaders is already marked deprecated and unused throughout the codebase. While the concern about missing chain ID validation is technically valid, the deprecation is the appropriate response. Given the PR's focus on chain separation and the fact that other retrieval methods enforce chain ID checks (see retrieveTx lines 105-106), this method should be removed entirely rather than refactored. The deprecation notice makes removal the clear path forward.


340-368: Add chain validation for the input header parameter.

The method should validate that the input header parameter belongs to the expected chain (h.chainID) before using it to update the executed block. Byzantine inputs must be validated; see the defensive programming pattern used in the Store operation (lines 40-41).

Additionally, consider validating the retrieved header against the chain ID as a defense against database corruption, particularly given the security-sensitive nature of execution state.

🤖 Fix all issues with AI agents
In @engine/collection/epochmgr/factories/cluster_state.go:
- Line 76: Fix the typo in the error message returned where the code currently
uses fmt.Errorf("could create mutable cluster state: %w", err); update this to
fmt.Errorf("could not create mutable cluster state: %w", err) (look for this
exact format call in the cluster state factory code, e.g., in cluster_state.go
or the function that returns the six-tuple including the error) so the error
reads correctly.

In @storage/store/init.go:
- Around line 3-7: The file imports currently lack the "fmt" package while code
later calls fmt.Errorf (use of fmt.Errorf in init or wherever error is
constructed), causing a compile error; fix by adding "fmt" to the import block
alongside the existing imports (e.g., add "fmt" to the import list in init.go)
so fmt.Errorf resolves correctly and the package compiles.
🧹 Nitpick comments (2)
state/protocol/badger/state.go (1)

1005-1014: Redundant irrecoverable wrapping and minor typo.

  1. GetLatestFinalizedHeader already returns errors wrapped with irrecoverable.NewExceptionf, so wrapping again at line 1011 with irrecoverable.NewException is redundant. Consider returning the error directly or adding meaningful context if re-wrapping.

  2. Line 1006: typo "present./" should be "present."

Suggested fix
-// benign [storage.ErrNotFound] and failed reads of quantities that the protocol mandates to be present./
+// benign [storage.ErrNotFound] and failed reads of quantities that the protocol mandates to be present.
 //
 // No error returns expected during normal operations.
 func GetChainID(db storage.DB) (flow.ChainID, error) {
 	h, err := GetLatestFinalizedHeader(db)
 	if err != nil {
-		return "", irrecoverable.NewException(err)
+		return "", fmt.Errorf("could not get chain ID from finalized header: %w", err)
 	}
 	return h.ChainID, nil
 }
storage/store/headers.go (1)

98-109: Minor: Return nil explicitly instead of err for clarity.

After the error check at lines 101-103, err is guaranteed to be nil. Returning nil explicitly would be clearer.

♻️ Suggested improvement
 	retrieve := func(r storage.Reader, blockID flow.Identifier) (*flow.Header, error) {
 		var header flow.Header
 		err := operation.RetrieveHeader(r, blockID, &header)
 		if err != nil {
 			return nil, err
 		}
 		// raise an error when the retrieved header is for a different chain than expected
 		if header.ChainID != chainID {
 			return nil, fmt.Errorf("expected chain ID %v, got %v: %w", chainID, header.ChainID, storage.ErrWrongChain)
 		}
-		return &header, err
+		return &header, nil
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4d1235 and 020aaed.

📒 Files selected for processing (6)
  • engine/collection/epochmgr/engine_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
  • engine/verification/verifier/verifiers.go
  • state/protocol/badger/state.go
  • storage/store/headers.go
  • storage/store/init.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • engine/verification/verifier/verifiers.go
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • engine/collection/epochmgr/engine_test.go
  • state/protocol/badger/state.go
  • storage/store/init.go
  • storage/store/headers.go
  • engine/collection/epochmgr/factories/cluster_state.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests should be co-located with the code they test
Follow the existing pattern of *_test.go files for test naming
Use fixtures for realistic test data as defined in /utils/unittest/

Files:

  • engine/collection/epochmgr/engine_test.go
{module,engine,cmd}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

All major processing components must implement the Component interface from /module/component/component.go to ensure consistent lifecycle management and graceful shutdown patterns

Files:

  • engine/collection/epochmgr/engine_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
{crypto,fvm,ledger,access,engine}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Files:

  • engine/collection/epochmgr/engine_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
{network,engine,consensus}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Network messages must be authenticated and validated

Files:

  • engine/collection/epochmgr/engine_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/store/init.go
  • storage/store/headers.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/store/init.go
  • storage/store/headers.go
🧠 Learnings (6)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • state/protocol/badger/state.go
  • storage/store/init.go
  • storage/store/headers.go
  • engine/collection/epochmgr/factories/cluster_state.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • state/protocol/badger/state.go
  • storage/store/init.go
  • storage/store/headers.go
  • engine/collection/epochmgr/factories/cluster_state.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : NEVER log and continue on best effort basis; ALWAYS explicitly handle errors

Applied to files:

  • storage/store/init.go
  • engine/collection/epochmgr/factories/cluster_state.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/headers.go
🧬 Code graph analysis (4)
engine/collection/epochmgr/engine_test.go (2)
model/flow/chain.go (1)
  • ChainID (14-14)
state/protocol/mock/params.go (1)
  • NewParams (171-181)
storage/store/init.go (3)
storage/badger/all.go (1)
  • InitAll (14-56)
model/flow/chain.go (1)
  • ChainID (14-14)
storage/store/headers.go (1)
  • NewHeaders (35-59)
storage/store/headers.go (6)
model/flow/chain.go (1)
  • ChainID (14-14)
storage/headers.go (1)
  • Headers (15-65)
state/cluster/root_block.go (1)
  • IsCanonicalClusterID (26-28)
storage/errors.go (2)
  • ErrWrongChain (37-37)
  • ErrNotAvailableForClusterConsensus (41-41)
storage/locks.go (2)
  • LockInsertBlock (14-14)
  • LockInsertOrFinalizeClusterBlock (22-22)
storage/operation/headers.go (5)
  • InsertHeader (26-42)
  • LookupBlockHeight (142-144)
  • LookupCertifiedBlockByView (149-151)
  • InsertClusterHeader (57-73)
  • RetrieveHeader (79-81)
engine/collection/epochmgr/factories/cluster_state.go (5)
model/flow/chain.go (1)
  • ChainID (14-14)
state/cluster/badger/mutator.go (2)
  • MutableState (23-30)
  • NewMutableState (34-44)
storage/store/headers.go (3)
  • Headers (19-27)
  • NewClusterHeaders (66-88)
  • NewHeaders (35-59)
storage/store/cluster_payloads.go (2)
  • ClusterPayloads (16-19)
  • NewClusterPayloads (23-37)
state/protocol/badger/state.go (3)
  • IsBootstrapped (982-992)
  • State (34-70)
  • Bootstrap (94-203)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (17)
engine/collection/epochmgr/factories/cluster_state.go (1)

46-55: LGTM! Clean separation of cluster and consensus headers.

The factory now properly creates dedicated storage abstractions for cluster headers (via NewClusterHeaders) and consensus headers (via NewHeaders), correctly wiring the chain IDs to each. This aligns with the PR objective of differentiating between cluster and consensus header storage.

engine/collection/epochmgr/engine_test.go (4)

70-73: LGTM! Cluster state params correctly mocked with ChainID.

The mock setup properly wires ChainID() to return a cluster-specific chain ID, and the params are attached to the cluster state mock. This allows tests to simulate the chain ID retrieval that the real implementation uses.


111-140: LGTM! Factory mock correctly updated to accept the new chainID parameter.

The MockFactoryCreate method properly:

  1. Adds mock.Anything matcher for the consensusChainID parameter (line 111)
  2. Updates all return function signatures to include the consensusChainID flow.ChainID parameter (lines 118-139)

This aligns with the signature change in EpochComponentsFactory.Create.


167-169: LGTM! Protocol state params correctly mocked with consensus ChainID.

The setup properly creates a protocol params mock with ChainID() returning a distinct consensus chain identifier, which is then wired to suite.state.On("Params"). This enables the engine to obtain the consensus chain ID when creating epoch components.


279-281: LGTM! Unauthorized node mock updated for new signature.

The matcher correctly adds mock.Anything for the consensusChainID parameter to align with the updated factory interface.

state/protocol/badger/state.go (3)

13-13: LGTM!

Import addition is necessary for the new helper functions and aligns with the coding guidelines requiring use of the irrecoverable package for exceptions.


981-992: LGTM!

The switch from storage.ErrNotFound to operation.IncompleteStateError aligns with the PR's objective to differentiate between benign not-found errors and incomplete/corrupted protocol state. The updated comment accurately reflects the expected behavior.


1016-1047: LGTM!

The implementation correctly reads directly from the database, bypassing high-level storage abstractions. Error handling properly uses irrecoverable.NewExceptionf with %w verb to preserve the error chain, allowing callers to check for operation.IncompleteStateError using errors.Is(). Based on learnings, this aligns with the requirement to use the irrecoverable package for exceptions.

storage/store/init.go (2)

31-39: Good documentation and error handling for the updated signature.

The comment clearly explains the chainID parameter purpose and the "No errors expected during normal operations" contract. The error wrapping provides useful context for debugging.


60-79: LGTM!

The remainder of the function correctly constructs the All struct and returns nil error on success. The structure aligns with the pattern in storage/badger/all.go.

storage/store/headers.go (7)

11-16: LGTM!

The new imports for irrecoverable and cluster packages are appropriate. irrecoverable is used for programmer errors per the coding guidelines, and cluster.IsCanonicalClusterID is used for chain ID validation.


18-27: Clean struct extension with chainID field.

Adding chainID to the Headers struct enables consistent chain validation across all operations.


31-59: Well-designed constructor with proper validation.

The implementation correctly:

  • Uses irrecoverable.NewExceptionf for programmer errors (calling wrong constructor)
  • Validates header.ChainID matches the bound chainID on insert
  • Enforces the appropriate lock (LockInsertBlock)
  • Documents the "no errors expected" contract

This aligns with the PR objectives of binding Headers to a specific chain ID.


61-88: Cluster-specific constructor correctly mirrors the consensus path.

Good implementation:

  • Validates chainID is a canonical cluster ID
  • Uses LockInsertOrFinalizeClusterBlock and operation.InsertClusterHeader
  • Uses cluster-specific height lookup via LookupClusterBlockHeight
  • Returns ErrNotAvailableForClusterConsensus for view-based retrieval

90-131: Good encapsulation of shared logic in newHeaders.

The internal helper cleanly consolidates:

  • Cache initialization with appropriate metrics
  • Chain ID validation on retrieval
  • Binding of chain-specific store/retrieve functions

The function signature with type-parameterized callbacks (storeWithLockFunc, retrieveFunc) provides good flexibility for cluster vs consensus paths.


191-203: Helpful inline documentation explaining chain-specific index behavior.

The comment at lines 195-197 clearly explains why this method only returns IDs for the expected chain—because the height cache uses a chain-specific database index. This aids future maintainability.


250-264: Remove: Exists intentionally does not distinguish chains.

The interface in storage/headers.go (lines 40-42) explicitly documents that this method does not distinguish between cluster and consensus headers. This is intentional by design—Exists is a simple existence check, while other interface methods like ByBlockID and ByHeight enforce chain separation by returning ErrWrongChain when needed. No action required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
storage/store/headers.go (2)

98-109: Return nil explicitly instead of reusing err.

At line 108, err is guaranteed to be nil after the successful check at line 101-103. While this is technically correct, returning err is misleading. Return nil explicitly for clarity.

Proposed fix
 	retrieve := func(r storage.Reader, blockID flow.Identifier) (*flow.Header, error) {
 		var header flow.Header
 		err := operation.RetrieveHeader(r, blockID, &header)
 		if err != nil {
 			return nil, err
 		}
 		// raise an error when the retrieved header is for a different chain than expected
 		if header.ChainID != chainID {
 			return nil, fmt.Errorf("expected chain ID %v, got %v: %w", chainID, header.ChainID, storage.ErrWrongChain)
 		}
-		return &header, err
+		return &header, nil
 	}

342-347: FindHeaders marked as deprecated.

The deprecation notice is appropriate. Consider adding a TODO or issue reference indicating when this method will be removed, or what should be used instead.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1dc2936 and 2523aa3.

📒 Files selected for processing (8)
  • cmd/util/cmd/export-json-transactions/transactions/range_test.go
  • state/cluster/state_test.go
  • storage/blocks.go
  • storage/cluster_blocks.go
  • storage/headers.go
  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • storage/store/headers.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • storage/headers.go
🧰 Additional context used
📓 Path-based instructions (5)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • storage/cluster_blocks.go
  • storage/store/blocks.go
  • state/cluster/state_test.go
  • cmd/util/cmd/export-json-transactions/transactions/range_test.go
  • storage/store/cluster_blocks.go
  • storage/blocks.go
  • storage/store/headers.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/cluster_blocks.go
  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • storage/blocks.go
  • storage/store/headers.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/cluster_blocks.go
  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • storage/blocks.go
  • storage/store/headers.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests should be co-located with the code they test
Follow the existing pattern of *_test.go files for test naming
Use fixtures for realistic test data as defined in /utils/unittest/

Files:

  • state/cluster/state_test.go
  • cmd/util/cmd/export-json-transactions/transactions/range_test.go
{module,engine,cmd}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

All major processing components must implement the Component interface from /module/component/component.go to ensure consistent lifecycle management and graceful shutdown patterns

Files:

  • cmd/util/cmd/export-json-transactions/transactions/range_test.go
🧠 Learnings (5)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/blocks.go
  • storage/blocks.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • storage/store/blocks.go
  • cmd/util/cmd/export-json-transactions/transactions/range_test.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/blocks.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • storage/store/blocks.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented

Applied to files:

  • storage/store/headers.go
🧬 Code graph analysis (5)
storage/store/blocks.go (1)
storage/blocks.go (1)
  • Blocks (17-112)
state/cluster/state_test.go (3)
model/flow/chain.go (1)
  • AllChainIDs (46-58)
state/cluster/root_block.go (3)
  • IsCanonicalClusterID (26-28)
  • CanonicalClusterID (18-20)
  • ClusterChainPrefix (14-14)
utils/unittest/fixtures.go (1)
  • IdentifierListFixture (1144-1150)
cmd/util/cmd/export-json-transactions/transactions/range_test.go (2)
cmd/util/cmd/common/storage.go (1)
  • InitStorages (62-65)
utils/unittest/fixtures/util.go (1)
  • NoError (6-10)
storage/store/cluster_blocks.go (2)
storage/cluster_blocks.go (1)
  • ClusterBlocks (13-34)
storage/operation/cluster.go (1)
  • LookupClusterBlockHeight (57-59)
storage/store/headers.go (6)
model/flow/chain.go (1)
  • ChainID (14-14)
state/cluster/root_block.go (1)
  • IsCanonicalClusterID (26-28)
storage/errors.go (3)
  • ErrWrongChain (37-37)
  • ErrNotAvailableForClusterConsensus (41-41)
  • ErrNotFound (17-17)
storage/locks.go (2)
  • LockInsertBlock (14-14)
  • LockInsertOrFinalizeClusterBlock (22-22)
storage/operation/headers.go (4)
  • InsertHeader (26-42)
  • LookupBlockHeight (142-144)
  • LookupCertifiedBlockByView (149-151)
  • RetrieveHeader (79-81)
storage/operation/cluster.go (1)
  • LookupClusterBlockHeight (57-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Docker Build
  • GitHub Check: analyze-code (go)
🔇 Additional comments (21)
state/cluster/state_test.go (2)

1-13: LGTM!

The external test package (cluster_test) is appropriate for testing the public API. Imports are well-organized and all used.


15-27: LGTM! Good test coverage with varying identifier counts.

The test properly addresses the prior review feedback by varying the identifier list size from 0 to 99, including the empty list edge case. The combination of negative tests (consensus chain IDs rejected) and positive tests (canonical cluster IDs accepted) provides good coverage of the IsCanonicalClusterID function.

cmd/util/cmd/export-json-transactions/transactions/range_test.go (2)

63-64: Appropriate ChainID for consensus block storage.

Using flow.Emulator as the ChainID is correct here since b1 and b2 are consensus chain blocks created via unittest.BlockFixture(), and flow.Emulator is a main consensus chain ID. The error handling addition properly validates storage initialization.

This addresses the concern raised in the past review about ChainID correctness - using the consensus chain ID (rather than a cluster chain ID) is appropriate for storing these consensus blocks.


77-87: Correct fix: avoid shadowing the outer err variable.

The change from err := to err = properly reuses the err variable declared on line 63, ensuring the error from WithLock is checked by the require.NoError on line 87. Using := would have shadowed the outer variable, which could lead to confusion.

storage/blocks.go (3)

26-36: Documentation updates for ID-based methods are correct.

The addition of ErrWrongChain to BatchStore and ByID is appropriate—these ID-based operations can receive identifiers for blocks that exist on a different chain (e.g., cluster vs. consensus). The standardized error documentation format improves consistency.


43-45: Consistent with ByID documentation pattern.


51-98: Correct omission of ErrWrongChain from height/view/collection-based methods.

These methods correctly do not document ErrWrongChain:

  • Height-based lookups (ByHeight, ProposalByHeight) use chain-specific indices, so they can only return blocks from the bound chain.
  • View-based lookups (ByView, ProposalByView) are similarly chain-specific.
  • Collection-based lookups are consensus-chain-specific.

Note: The AI summary claims these methods have ErrWrongChain documentation added, but the actual code does not include it—this is the correct behavior.

storage/cluster_blocks.go (2)

20-21: Documentation correctly reflects new error semantics.

The added storage.ErrWrongChain error documentation for ProposalByID aligns with the implementation in storage/store/cluster_blocks.go where chain ID is checked during header retrieval.


30-30: Correct: ProposalByHeight does not document ErrWrongChain.

This is accurate since ProposalByHeight uses a chain-specific height index (LookupClusterBlockHeight with chainID), so cross-chain lookups cannot occur at the height lookup level.

storage/store/blocks.go (4)

67-70: Correct use of irrecoverable exception for state corruption.

If a header is successfully retrieved but the payload cannot be found, this indicates data inconsistency. Wrapping with irrecoverable.NewExceptionf is appropriate here. Based on learnings, this aligns with the convention to use the irrecoverable package for exceptions indicating state corruption.


100-103: Correct use of irrecoverable exception for missing proposer signature.

After successfully retrieving the block, a missing proposer signature indicates state corruption. The use of irrecoverable.NewExceptionf is appropriate here.


169-175: Correct error handling for indexed proposal retrieval.

When a block is indexed by view but cannot be retrieved, this is a symptom of state corruption. Wrapping the error with irrecoverable.NewExceptionf is the correct approach.


43-43: Documentation aligns with interface.

The ErrWrongChain documentation for BatchStore matches the interface definition in storage/blocks.go.

storage/store/cluster_blocks.go (1)

40-90: Well-structured implementation of ProposalByID.

The implementation correctly:

  1. Retrieves header first (allows ErrNotFound/ErrWrongChain to propagate)
  2. Uses irrecoverable exceptions for subsequent failures (payload, signature) indicating state corruption
  3. Handles both root and non-root block/proposal construction
storage/store/headers.go (7)

31-59: Well-designed constructor with proper validation.

The constructor correctly:

  1. Validates that the chainID is NOT a cluster chain (using cluster.IsCanonicalClusterID)
  2. Returns an irrecoverable.NewExceptionf error instead of panicking (addresses past review feedback)
  3. Validates header.ChainID matches the bound chainID before storage
  4. Checks for the appropriate lock (LockInsertBlock)
  5. Uses chain-specific height and view retrieval functions

61-88: Correct cluster-specific constructor implementation.

The cluster headers constructor:

  1. Validates that the chainID IS a cluster chain
  2. Uses LockInsertOrFinalizeClusterBlock lock
  3. Uses InsertClusterHeader and LookupClusterBlockHeight operations
  4. Correctly returns ErrNotAvailableForClusterConsensus for view lookups

184-188: Correct use of irrecoverable exception for missing proposer signature.

After successfully retrieving the header, a missing proposer signature indicates state corruption. The error wrapping and comment are appropriate.


231-236: Correct error handling for height-indexed header retrieval.

When the height index lookup succeeds but header retrieval fails, this indicates state corruption. The irrecoverable exception wrapping is appropriate, and the comment clearly explains the reasoning.


252-257: Correct error handling for view-indexed header retrieval.

Similar to ByHeight, when the view cache returns a block ID but the header cannot be retrieved, this indicates state corruption. The irrecoverable exception wrapping is appropriate.


260-276: Well-designed Exists method handles chain boundaries correctly.

The implementation correctly:

  1. Short-circuits with cache check (blocks on wrong chain are never cached)
  2. Falls back to retrieveTx which validates ChainID
  3. Treats both ErrNotFound and ErrWrongChain as "does not exist on this chain"

This addresses the past review comment about Exists needing to be chain-aware.


317-324: Error handling for child retrieval is appropriate as-is.

ErrWrongChain is explicitly documented as an expected error return in the ByParentID interface (see documentation comment). When children from the parent-child index are retrieved via ByBlockID, returning ErrWrongChain is expected behavior, not state corruption—this occurs when indexed children exist on a different chain than the current Headers instance's chain.

Unlike ByHeight and ByView, which use indexed lookups that guarantee block existence, the parent-child index can legitimately contain block IDs from multiple chains since the index itself is not chain-specific. Cross-chain child references are a normal scenario, not an indicator of index corruption.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
storage/store/blocks.go (1)

227-233: Wrap ByID error as irrecoverable for state corruption detection.

ByCollectionID follows the same pattern as ByHeight and ByView: an index lookup followed by a block retrieval. However, unlike those methods, it does not wrap the ByID error with irrecoverable.NewExceptionf. Both ByHeight and ByView wrap their second retrieval with the explicit comment "failure to retrieve a block that has been indexed indicates state corruption."

If BlockIDByCollectionID succeeds and returns a finalized block ID, failure to retrieve that block by ID indicates the same state corruption condition and should be handled identically. Apply the same error wrapping pattern:

return nil, irrecoverable.NewExceptionf("could not retrieve indexed block for collection %x: %w", collID[:], err)
♻️ Duplicate comments (2)
module/builder/collection/builder.go (1)

308-310: Use irrecoverable.NewExceptionf for unexpected errors.

Line 309 uses fmt.Errorf for an unexpected error path, but per coding guidelines, unexpected errors (exceptions) should use irrecoverable.NewExceptionf. This is also inconsistent with line 340 which correctly uses irrecoverable.NewExceptionf for a similar unexpected error case.

Proposed fix
 			} else {
-				return nil, fmt.Errorf("unexpected failure to retrieve first height of operating epoch: %w", err)
+				return nil, irrecoverable.NewExceptionf("unexpected failure to retrieve first height of operating epoch: %w", err)
 			}

Based on learnings, use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions.

storage/store/cluster_blocks.go (1)

103-108: Correct irrecoverable wrapping for indexed retrieval.

The implementation correctly treats any error from ProposalByID after a successful height lookup as state corruption. The height index is chain-specific, so ErrWrongChain should never occur here, and ErrNotFound would indicate an inconsistent index.

🧹 Nitpick comments (1)
state/protocol/badger/state.go (1)

1008-1014: Double-wrapping inconsistency with documented error type.

The documentation states this function may return [operation.IncompleteStateError], but GetLatestFinalizedHeader already wraps errors with irrecoverable.NewExceptionf, and then GetChainID wraps again with irrecoverable.NewException. This double-wrapping is redundant.

Since GetLatestFinalizedHeader already returns an irrecoverable exception on any error, you can simplify the error return here:

♻️ Suggested simplification
 func GetChainID(db storage.DB) (flow.ChainID, error) {
 	h, err := GetLatestFinalizedHeader(db)
 	if err != nil {
-		return "", irrecoverable.NewException(err)
+		return "", err // already wrapped as irrecoverable exception by GetLatestFinalizedHeader
 	}
 	return h.ChainID, nil
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2523aa3 and 5aa5c4d.

📒 Files selected for processing (11)
  • engine/collection/epochmgr/factories/cluster_state.go
  • engine/testutil/nodes.go
  • engine/verification/verifier/verifiers.go
  • module/builder/collection/builder.go
  • state/protocol/badger/state.go
  • storage/headers.go
  • storage/store/blocks.go
  • storage/store/blocks_test.go
  • storage/store/cluster_blocks.go
  • storage/store/headers.go
  • storage/store/headers_test.go
✅ Files skipped from review due to trivial changes (1)
  • storage/headers.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • engine/verification/verifier/verifiers.go
🧰 Additional context used
📓 Path-based instructions (7)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • storage/store/blocks_test.go
  • module/builder/collection/builder.go
  • engine/testutil/nodes.go
  • state/protocol/badger/state.go
  • storage/store/headers_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
  • storage/store/headers.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • storage/store/blocks_test.go
  • storage/store/headers_test.go
  • storage/store/headers.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • storage/store/blocks_test.go
  • storage/store/headers_test.go
  • storage/store/headers.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests should be co-located with the code they test
Follow the existing pattern of *_test.go files for test naming
Use fixtures for realistic test data as defined in /utils/unittest/

Files:

  • storage/store/blocks_test.go
  • storage/store/headers_test.go
{module,engine,cmd}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

All major processing components must implement the Component interface from /module/component/component.go to ensure consistent lifecycle management and graceful shutdown patterns

Files:

  • module/builder/collection/builder.go
  • engine/testutil/nodes.go
  • engine/collection/epochmgr/factories/cluster_state.go
{crypto,fvm,ledger,access,engine}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Files:

  • engine/testutil/nodes.go
  • engine/collection/epochmgr/factories/cluster_state.go
{network,engine,consensus}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Network messages must be authenticated and validated

Files:

  • engine/testutil/nodes.go
  • engine/collection/epochmgr/factories/cluster_state.go
🧠 Learnings (6)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • storage/store/blocks.go
  • storage/store/cluster_blocks.go
  • module/builder/collection/builder.go
  • state/protocol/badger/state.go
  • engine/collection/epochmgr/factories/cluster_state.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/blocks.go
  • module/builder/collection/builder.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • storage/store/blocks.go
  • storage/store/blocks_test.go
  • state/protocol/badger/state.go
  • storage/store/headers_test.go
  • engine/collection/epochmgr/factories/cluster_state.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/blocks.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented

Applied to files:

  • storage/store/cluster_blocks.go
  • module/builder/collection/builder.go
  • storage/store/headers.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : NEVER log and continue on best effort basis; ALWAYS explicitly handle errors

Applied to files:

  • module/builder/collection/builder.go
  • engine/collection/epochmgr/factories/cluster_state.go
🧬 Code graph analysis (5)
storage/store/blocks.go (2)
storage/blocks.go (1)
  • Blocks (17-112)
storage/mock/blocks.go (1)
  • Blocks (15-17)
storage/store/cluster_blocks.go (2)
storage/cluster_blocks.go (1)
  • ClusterBlocks (13-34)
storage/operation/cluster.go (1)
  • LookupClusterBlockHeight (57-59)
storage/store/blocks_test.go (5)
storage/store/init.go (1)
  • InitAll (37-82)
storage/badger/all.go (1)
  • InitAll (14-56)
storage/blocks.go (1)
  • Blocks (17-112)
storage/errors.go (2)
  • ErrNotFound (17-17)
  • ErrWrongChain (37-37)
storage/locks.go (1)
  • LockInsertBlock (14-14)
engine/testutil/nodes.go (4)
storage/store/init.go (1)
  • InitAll (37-82)
storage/badger/all.go (1)
  • InitAll (14-56)
model/flow/chain.go (1)
  • ChainID (14-14)
storage/store/headers.go (1)
  • NewHeaders (35-59)
state/protocol/badger/state.go (2)
storage/operation/heights.go (2)
  • RetrieveFinalizedHeight (46-58)
  • IncompleteStateError (21-21)
storage/operation/headers.go (2)
  • LookupBlockHeight (142-144)
  • RetrieveHeader (79-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (37)
  • GitHub Check: Lint (./)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (engine)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: Integration Tests Others (integration)
  • GitHub Check: analyze-code (go)
  • GitHub Check: Docker Build
🔇 Additional comments (33)
engine/collection/epochmgr/factories/cluster_state.go (4)

37-44: LGTM!

The updated function signature correctly introduces consensusChainID parameter and returns separate header instances for cluster and consensus chains. This aligns well with the PR objective to differentiate between cluster and consensus header storage.


46-55: LGTM!

The storage abstraction creation correctly separates cluster-specific storage (clusterHeaders, clusterPayloads, clusterBlocks) from consensus storage (consensusHeaders). The dependency chain is properly established with clusterBlocks receiving its header and payload dependencies. Error messages provide good context for debugging.


57-72: LGTM!

The bootstrap/open state logic correctly passes the new clusterHeaders and clusterPayloads to OpenState, maintaining proper separation of cluster-specific storage. Error handling is consistent with descriptive messages.


74-78: LGTM!

The mutable state creation correctly receives both clusterHeaders and consensusHeaders, enabling proper validation of reference blocks against the main consensus chain. This supports the PR objective to differentiate between cluster and consensus header storage.

module/builder/collection/builder.go (2)

47-56: LGTM! Clear documentation for epoch height caching.

The added documentation thoroughly explains why epochFirstHeight uses a pointer type and the runtime initialization pattern. This addresses the concern that the builder is instantiated before the epoch starts.


639-669: LGTM! Excellent documentation and correct epoch-aware bounds.

The extensive comments clearly explain:

  1. The mathematical derivation of the search range bounds
  2. The transformation from exclusive to inclusive bounds
  3. The epoch boundary consideration
  4. The underflow protection logic

The implementation correctly bounds the lower search range by epochFirstHeight while preventing underflow through the rewritten comparison.

state/protocol/badger/state.go (2)

13-13: LGTM!

The irrecoverable import is correctly added to support the new error handling in GetChainID and GetLatestFinalizedHeader.


980-992: LGTM!

The IsBootstrapped function correctly:

  1. Documents that no errors are expected during normal operation (addressing past review feedback)
  2. Checks for operation.IncompleteStateError which is returned by RetrieveFinalizedHeight when the finalized height is not found

This aligns with the PR objective to avoid confusion between benign storage.ErrNotFound and missing protocol-mandated data.

engine/testutil/nodes.go (2)

239-240: LGTM!

The InitAll call correctly passes the chainID from the root snapshot and properly handles the new error return value. This aligns with the PR's objective of binding Headers instances to a ChainID.


562-563: LGTM!

The NewHeaders call correctly passes the chainID and handles the error return, consistent with the updated API signature.

storage/store/blocks_test.go (3)

23-25: LGTM!

The InitAll call is correctly updated to use the new signature with flow.Emulator and proper error handling. The pattern is consistently applied across all test functions.


29-33: Good addition of pre-insert validation.

Testing that ErrNotFound is returned before the block exists improves test coverage for the expected error semantics.


186-224: Excellent test coverage for ErrWrongChain behavior.

This test properly validates the differentiation between consensus and cluster headers:

  • ByID/ProposalByID correctly return ErrWrongChain when the block exists but belongs to a different chain
  • ByHeight/ProposalByHeight correctly return ErrNotFound since the height index is chain-specific

This addresses the PR objective of returning documented sentinel errors for chain mismatches.

storage/store/blocks.go (4)

67-70: Correct use of irrecoverable for state corruption.

When a header exists but the payload cannot be retrieved, this indicates database inconsistency. Using irrecoverable.NewExceptionf appropriately signals this as a fatal condition rather than an expected error.


100-103: LGTM!

Wrapping signature retrieval errors as irrecoverable is correct since the block was already successfully retrieved, so missing signature data indicates state corruption.


157-162: LGTM!

When the view index lookup succeeds, failure to retrieve the indexed block indicates state corruption. The irrecoverable wrapping correctly captures that the index is inconsistent with actual stored data.


192-197: LGTM!

Height-indexed block retrieval failures are correctly treated as state corruption since the height index is maintained for finalized blocks only.

storage/store/cluster_blocks.go (1)

40-53: LGTM!

The error handling correctly distinguishes between:

  • ErrNotFound/ErrWrongChain from header retrieval (lines 41-44) - propagated as expected errors
  • Payload/signature retrieval failures (lines 46-53) - wrapped as irrecoverable since header existence implies these must exist
storage/store/headers_test.go (5)

53-60: Good addition of retrieval by ID and ProposalByBlockID tests.

This adds coverage for ByBlockID and ProposalByBlockID happy paths, addressing the past review request for testing these methods.


96-104: Excellent test for ErrNotAvailableForClusterConsensus sentinel.

Testing both an existing view and a non-existent view confirms that the error relates to the method being unavailable for cluster consensus, not the specific data.


184-194: Good test case for ErrWrongChain in ByParentID.

This addresses the past review request to verify that ByParentID returns ErrWrongChain when the parent block exists but belongs to a different chain.


263-291: Comprehensive test for ErrWrongChain on store operations.

The test validates that attempting to store blocks with various mismatched chain IDs (cluster chains, other main chains, invalid chains) all correctly return ErrWrongChain. This thoroughly tests the PR's objective of rejecting headers from the wrong chain.


293-367: Excellent comprehensive test for cross-chain retrieval errors.

This test thoroughly validates the PR's sentinel error behavior across multiple scenarios:

  1. Consensus Headers correctly retrieves consensus blocks
  2. Cluster Headers returns ErrNotFound for height (chain-specific index) and ErrWrongChain for ID lookup of consensus blocks
  3. Different cluster chains correctly return ErrWrongChain for each other's blocks
  4. Consensus Headers correctly returns ErrWrongChain for cluster blocks

This provides strong coverage for the differentiated header storage behavior.

storage/store/headers.go (10)

18-27: LGTM - Headers struct updated with chainID field.

The addition of the chainID field enables the chain-specific behavior required by this PR. The struct is properly implementing storage.Headers interface as confirmed by the compile-time check on line 29.


31-59: LGTM - NewHeaders constructor properly validates and configures main consensus headers.

The implementation correctly:

  • Returns an error via irrecoverable.NewExceptionf for invalid chain IDs (addressed from prior review)
  • Validates header.ChainID matches on store operations
  • Uses the correct lock (LockInsertBlock) and height/view lookup operations for main consensus

61-88: LGTM - NewClusterHeaders constructor correctly implements cluster-specific behavior.

The implementation properly:

  • Returns an error via irrecoverable.NewExceptionf for non-cluster chain IDs
  • Uses the correct lock (LockInsertOrFinalizeClusterBlock) and InsertClusterHeader operation
  • Uses the cluster-specific height index with LookupClusterBlockHeight
  • Returns ErrNotAvailableForClusterConsensus for view lookups as documented

90-131: LGTM - Shared initialization logic correctly enforces chainID validation.

The retrieve function (lines 98-109) properly validates that the retrieved header's ChainID matches the expected chain, returning storage.ErrWrongChain on mismatch. This ensures all cache retrievals enforce chain separation.


223-237: LGTM - ByHeight correctly handles state corruption scenarios.

The error handling at lines 233-235 properly uses irrecoverable.NewExceptionf when an indexed block cannot be retrieved, as this indicates state corruption rather than a normal error condition.


260-276: LGTM - Exists method correctly treats ErrWrongChain as non-existence.

Per the interface contract that a Headers instance operates only on its bound chain, a header from a different chain correctly returns false (not found on this chain) rather than propagating ErrWrongChain. This aligns with the prior review discussion on robust API conventions.


297-325: LGTM - ByParentID properly validates parent chain before querying children.

The method correctly:

  1. Validates the parent exists on the correct chain first (lines 298-302)
  2. Returns ErrWrongChain if the parent belongs to a different chain
  3. Treats failure to retrieve indexed children as state corruption (lines 319-320)

327-339: LGTM - BlockIDByView properly documents cluster limitation.

The documentation correctly specifies that ErrNotAvailableForClusterConsensus may be returned when called on a cluster Headers instance, aligning with the interface contract.


341-346: No action needed — method is deprecated and unused.

The FindHeaders method is marked deprecated with "unsafe for public use" warning, and comprehensive codebase search confirms it has no callers. While the technical concern about chainID filtering is valid, it's already addressed through the deprecation notice. Since the method is dead code, the deprecation status is appropriate.


348-380: RollbackExecutedBlock bypasses chainID validation - add explicit safety check or use retrieveTx.

This method retrieves headers directly via operation.RetrieveHeader (line 360), bypassing chainID validation that h.retrieveTx provides through the cache and indexed lookups. While the method is documented as "Intended to be used by Execution Nodes only" and currently only used in an admin recovery utility, there is no code-level enforcement preventing it from being called on a cluster Headers instance created via NewClusterHeaders. A cluster instance could theoretically bypass the intended EN-only constraint.

Recommend either:

  1. Add a check to ensure this method is only callable on main consensus Headers instances (validate that h.chainID is not a cluster chain ID)
  2. Use h.retrieveTx instead of operation.RetrieveHeader for consistency with other retrieval methods, which ensures chainID validation
  3. At minimum, validate that the retrieved header's chainID matches h.chainID before using it

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@state/protocol/badger/state.go`:
- Around line 1036-1044: The current error handling in LookupBlockHeight and
RetrieveHeader discards the original err by returning
operation.IncompleteStateError directly; change the returns in the functions
calling LookupBlockHeight and RetrieveHeader to wrap the original err so the
original error is preserved while still indicating semantic IncompleteStateError
(e.g., use fmt.Errorf to wrap operation.IncompleteStateError with %w or include
the original err as a wrapped cause). Update the two return statements around
operation.LookupBlockHeight(...) and operation.RetrieveHeader(...) to return a
wrapped error that includes operation.IncompleteStateError and the original err
(so tools and callers can inspect both), and keep behavior consistent with
RetrieveFinalizedHeight's mapping of storage.ErrNotFound to
operation.IncompleteStateError.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abd6b9f and 2a14b8d.

📒 Files selected for processing (2)
  • state/protocol/badger/state.go
  • storage/store/blocks.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/coding_conventions.mdc)

Follow Go coding conventions as documented in @docs/agents/CodingConventions.md

Follow Go coding standards and conventions as documented in @docs/agents/GoDocs.md

**/*.go: Follow the existing module structure in /module/, /engine/, /model/ and use dependency injection patterns for component composition
Implement proper interfaces before concrete types
Follow Go naming conventions and the project's coding style defined in /docs/CodingConventions.md
Use mock generators: run make generate-mocks after interface changes
All inputs must be considered potentially byzantine; error classification is context-dependent and no code path is safe unless explicitly proven and documented
Use comprehensive error wrapping for debugging; avoid fmt.Errorf, use irrecoverable package for exceptions
NEVER log and continue on best effort basis; ALWAYS explicitly handle errors
Uses golangci-lint with custom configurations (.golangci.yml) and custom linters for Flow-specific conventions (struct write checking)

Files:

  • storage/store/blocks.go
  • state/protocol/badger/state.go
{crypto,fvm,ledger,storage}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Security checks for cryptographic misuse must be enforced using gosec

Files:

  • storage/store/blocks.go
{storage,ledger,execution,fvm}/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

State consistency is paramount; use proper synchronization primitives

Files:

  • storage/store/blocks.go
🧠 Learnings (4)
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Use comprehensive error wrapping for debugging; avoid `fmt.Errorf`, use `irrecoverable` package for exceptions

Applied to files:

  • storage/store/blocks.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {crypto,fvm,ledger,access,engine}/**/*.go : Cryptographic operations require careful handling; refer to crypto library documentation for proper implementation

Applied to files:

  • storage/store/blocks.go
  • state/protocol/badger/state.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to {storage,ledger,execution,fvm}/**/*.go : State consistency is paramount; use proper synchronization primitives

Applied to files:

  • storage/store/blocks.go
  • state/protocol/badger/state.go
📚 Learning: 2025-12-23T00:28:41.005Z
Learnt from: CR
Repo: onflow/flow-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T00:28:41.005Z
Learning: Applies to **/*.go : Implement proper interfaces before concrete types

Applied to files:

  • storage/store/blocks.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (35)
  • GitHub Check: Lint (./insecure/)
  • GitHub Check: Lint (./integration/)
  • GitHub Check: Lint (./)
  • GitHub Check: Unit Tests (network/test/cohort2)
  • GitHub Check: Unit Tests (engine/execution)
  • GitHub Check: Unit Tests (network/p2p/scoring)
  • GitHub Check: Unit Tests (module/dkg)
  • GitHub Check: Unit Tests (utils)
  • GitHub Check: Unit Tests (storage)
  • GitHub Check: Unit Tests (engine/access)
  • GitHub Check: Unit Tests (state)
  • GitHub Check: Unit Tests (cmd)
  • GitHub Check: Unit Tests (admin)
  • GitHub Check: Unit Tests (fvm)
  • GitHub Check: Unit Tests (engine/common)
  • GitHub Check: Unit Tests (network/test/cohort1)
  • GitHub Check: Unit Tests (network/alsp)
  • GitHub Check: Unit Tests (network)
  • GitHub Check: Unit Tests (network/p2p/node)
  • GitHub Check: Unit Tests (others)
  • GitHub Check: Unit Tests (consensus)
  • GitHub Check: Unit Tests (network/p2p)
  • GitHub Check: Unit Tests (engine/consensus)
  • GitHub Check: Unit Tests (ledger)
  • GitHub Check: Unit Tests (network/p2p/connection)
  • GitHub Check: Unit Tests (engine/verification)
  • GitHub Check: Unit Tests (engine/execution/computation)
  • GitHub Check: Unit Tests (engine/collection)
  • GitHub Check: Unit Tests (module)
  • GitHub Check: Unit Tests (engine/execution/ingestion)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/rpc_inspector)
  • GitHub Check: Unit Tests Insecure (insecure)
  • GitHub Check: Unit Tests Insecure (insecure/integration/functional/test/gossipsub/scoring)
  • GitHub Check: analyze-code (go)
  • GitHub Check: Docker Build
🔇 Additional comments (8)
state/protocol/badger/state.go (2)

979-991: LGTM!

The updated error handling correctly uses operation.IncompleteStateError instead of storage.ErrNotFound, which aligns with the PR's goal of distinguishing protocol-mandated data absence from benign not-found scenarios. The documentation also properly states that errors indicate bugs or state corruption.


993-1013: LGTM!

The function is well-documented, correctly delegates to GetLatestFinalizedHeader, and properly wraps errors with context. The documentation clearly explains the error semantics and when this function should be called.

storage/store/blocks.go (6)

67-70: Correct use of irrecoverable for state corruption.

The reasoning is sound: if the header was successfully retrieved, the payload must exist since they are stored atomically via BatchStore. A missing payload indicates state corruption.


100-103: Appropriate irrecoverable wrapping for signature retrieval.

If the block body was successfully retrieved, the proposer signature must exist since they are stored together. This correctly signals state corruption.


152-162: State corruption handling in ByView is well-implemented.

The logic correctly wraps retrieval errors as irrecoverable: if BlockIDByView succeeds (block is indexed), but ByID fails, this indicates inconsistent state. Both ErrNotFound and ErrWrongChain from ByID would represent corruption here, so wrapping all errors is correct. The docstring appropriately only lists ErrNotFound (from the index lookup) as the expected error.


185-197: Consistent pattern applied to ByHeight.

Same reasoning applies: successful height-index lookup followed by failed block retrieval indicates state corruption. Documentation correctly omits ErrWrongChain since the height index is chain-specific.


225-237: ByCollectionID follows the established pattern correctly.

The docstring lists only ErrNotFound as expected, which is appropriate. If the collection-to-block index exists but ByID fails (including ErrWrongChain), this indicates corruption in the index rather than a user-recoverable error.


1-283: Interface documentation is already up-to-date. The storage.Blocks interface in storage/blocks.go already documents the ErrWrongChain error semantics for the appropriate methods (BatchStore, ByID, ProposalByID), and it matches the implementation documentation. No updates are needed.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +1036 to +1044
err = operation.LookupBlockHeight(r, finalized, &id)
if err != nil {
return nil, fmt.Errorf("could not retrieve blockID of finalized block at height %d: %w", finalized, operation.IncompleteStateError)
}
var header flow.Header
err = operation.RetrieveHeader(r, id, &header)
if err != nil {
return nil, fmt.Errorf("could not retrieve latest finalized block %x: %w", id, operation.IncompleteStateError)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Original error is discarded, losing debugging information.

Lines 1038 and 1043 replace the actual error err with operation.IncompleteStateError, discarding the original error details. This makes debugging harder when failures occur. The error wrapping should preserve the original error while also indicating the semantic meaning (incomplete state).

Additionally, there's inconsistency with how RetrieveFinalizedHeight handles errors (it internally converts storage.ErrNotFound to IncompleteStateError), while here the original error from LookupBlockHeight and RetrieveHeader is simply discarded.

Proposed fix to preserve original error
 	var id flow.Identifier
 	err = operation.LookupBlockHeight(r, finalized, &id)
 	if err != nil {
-		return nil, fmt.Errorf("could not retrieve blockID of finalized block at height %d: %w", finalized, operation.IncompleteStateError)
+		if errors.Is(err, storage.ErrNotFound) {
+			return nil, fmt.Errorf("could not retrieve blockID of finalized block at height %d: %w", finalized, operation.IncompleteStateError)
+		}
+		return nil, fmt.Errorf("could not retrieve blockID of finalized block at height %d: %w", finalized, err)
 	}
 	var header flow.Header
 	err = operation.RetrieveHeader(r, id, &header)
 	if err != nil {
-		return nil, fmt.Errorf("could not retrieve latest finalized block %x: %w", id, operation.IncompleteStateError)
+		if errors.Is(err, storage.ErrNotFound) {
+			return nil, fmt.Errorf("could not retrieve latest finalized block %x: %w", id, operation.IncompleteStateError)
+		}
+		return nil, fmt.Errorf("could not retrieve latest finalized block %x: %w", id, err)
 	}

Alternatively, if the intent is to always return IncompleteStateError for any failure in this function (since all data is protocol-mandated), consider wrapping the original error within the IncompleteStateError to preserve debugging context:

return nil, fmt.Errorf("could not retrieve blockID of finalized block at height %d: %w (original: %v)", finalized, operation.IncompleteStateError, err)
🤖 Prompt for AI Agents
In `@state/protocol/badger/state.go` around lines 1036 - 1044, The current error
handling in LookupBlockHeight and RetrieveHeader discards the original err by
returning operation.IncompleteStateError directly; change the returns in the
functions calling LookupBlockHeight and RetrieveHeader to wrap the original err
so the original error is preserved while still indicating semantic
IncompleteStateError (e.g., use fmt.Errorf to wrap
operation.IncompleteStateError with %w or include the original err as a wrapped
cause). Update the two return statements around operation.LookupBlockHeight(...)
and operation.RetrieveHeader(...) to return a wrapped error that includes
operation.IncompleteStateError and the original err (so tools and callers can
inspect both), and keep behavior consistent with RetrieveFinalizedHeight's
mapping of storage.ErrNotFound to operation.IncompleteStateError.

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.

[LN Decentralization E] Differentiate between consensus Headers and cluster Headers

5 participants