Skip to content

fix(evm:PLA-716): validate block bloom emptiness by default#48

Merged
0x666c6f merged 5 commits intomorpho-mainfrom
feature/pla-716-investigate-eth_getlogs-inconsistency-causing-failed-block
Mar 6, 2026
Merged

fix(evm:PLA-716): validate block bloom emptiness by default#48
0x666c6f merged 5 commits intomorpho-mainfrom
feature/pla-716-investigate-eth_getlogs-inconsistency-causing-failed-block

Conversation

@0x666c6f
Copy link
Copy Markdown
Collaborator

@0x666c6f 0x666c6f commented Mar 6, 2026

Summary

  • fix block-level logsBloom emptiness validation for eth_getBlockByNumber when ground-truth logs are available
  • make validateLogsBloomEmptiness default-on so inconsistent block/log emptiness is rejected automatically
  • add static fixtures, regression coverage, and a benchmark for bloom recomputation cost

Changes

  • validate block logsBloom against GroundTruthLogs for emptiness and optional full bloom matching
  • add block validation fixtures and tests covering empty, matching, and mismatching log sets
  • benchmark 1-log, 100-log, and synthetic 80k-log bloom recomputation paths
  • document the new default for validateLogsBloomEmptiness

Linear

@0x666c6f
Copy link
Copy Markdown
Collaborator Author

0x666c6f commented Mar 6, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dab939ea4b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if !dirs.ValidateLogsBloomEmptiness && !dirs.ValidateLogsBloomMatch {
return nil
}
if dirs.GroundTruthLogs == nil || block == nil || block.LogsBloom == "" {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate missing logsBloom instead of skipping checks

validateBlockLogsBloom returns early when block.LogsBloom == "", which bypasses both emptiness and full-match validation even when GroundTruthLogs is provided. In the scenario this change targets (using ground-truth logs to detect inconsistent block data), an upstream that omits logsBloom will now be accepted instead of rejected, so malformed/incomplete block payloads can slip through integrity checks.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR strengthens EVM block integrity validation by defaulting validateLogsBloomEmptiness to on and adding block-level bloom validation against caller-provided GroundTruthLogs, with accompanying fixtures/tests and a recomputation benchmark.

Changes:

  • Enable validateLogsBloomEmptiness by default via directive defaults and document the new default.
  • Add block-level logsBloom validation against GroundTruthLogs for emptiness consistency and optional full bloom matching.
  • Add static fixtures + regression tests + benchmarks for bloom recomputation cost at different log volumes.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
docs/pages/config/failsafe/integrity.mdx Documents validateLogsBloomEmptiness as default-on and updates the sample config commentary accordingly.
common/defaults.go Sets DirectiveDefaultsConfig.ValidateLogsBloomEmptiness to default true.
common/defaults_test.go Adds regression test asserting directive defaults include core integrity defaults (incl. bloom emptiness).
architecture/evm/eth_getBlockByNumber.go Adds ground-truth-based block logsBloom validation (emptiness + optional full match).
architecture/evm/eth_getBlockByNumber_validation_test.go Adds fixtures-driven tests for emptiness/match behavior plus a benchmark for bloom recomputation.
architecture/evm/testdata/block_validation_block.json Adds a minimal block fixture containing logsBloom used for validation tests.
architecture/evm/testdata/block_validation_logs.json Adds a minimal ground-truth logs fixture used for bloom validation tests.

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

Comment on lines +786 to +791
gtLogCount := 0
for _, log := range dirs.GroundTruthLogs {
if log != nil {
gtLogCount++
}
}
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

validateBlockLogsBloom iterates over dirs.GroundTruthLogs twice when ValidateLogsBloomMatch is enabled (first to count non-nil logs, then again to recompute the bloom). For large ground-truth log sets this adds measurable overhead; consider combining the count with the bloom recomputation loop (or only doing the pre-count when ValidateLogsBloomEmptiness is enabled and match is disabled).

Copilot uses AI. Check for mistakes.
}

if !bytes.Equal(expected, providedBloom) {
return common.NewErrEndpointContentValidation(fmt.Errorf("block logsBloom does not match ground-truth logs"), u)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The error returned on bloom mismatch ("block logsBloom does not match ground-truth logs") lacks identifying context (e.g., block number/hash and how many ground-truth logs were used), which makes upstream debugging harder. Consider including block.Number / block.Hash (when present) and gtLogCount in the validation error message.

Suggested change
return common.NewErrEndpointContentValidation(fmt.Errorf("block logsBloom does not match ground-truth logs"), u)
return common.NewErrEndpointContentValidation(
fmt.Errorf("block logsBloom does not match ground-truth logs (non-nil ground-truth log records: %d)", gtLogCount),
u,
)

Copilot uses AI. Check for mistakes.
@0x666c6f
Copy link
Copy Markdown
Collaborator Author

0x666c6f commented Mar 6, 2026

@claude review

@claude
Copy link
Copy Markdown

claude Bot commented Mar 6, 2026

Claude finished @0x666c6f's task in 2m 5s —— View job


Claude's Review

  • Gathering context
  • Reading changed files
  • Analyzing code
  • Posting review feedback

Overall this is clean, well-structured work. The logic is clear, the fixtures are minimal and useful, and the benchmark gives real data on recomputation cost. A few issues are worth addressing before merging.


architecture/evm/eth_getBlockByNumber.go

🔴 Logic gap: empty logsBloom bypasses all validation (line 782)

if dirs.GroundTruthLogs == nil || block == nil || block.LogsBloom == "" {
    return nil
}

When a node omits the logsBloom field (empty string), this early-return silently skips all checks — including ValidateLogsBloomEmptiness — even when GroundTruthLogs has entries. This means a malformed/partial block response where the bloom field is stripped passes without error. Notably, isZeroBloom("") returns true (it trims "0x" and leading zeros, leaving an empty string which equals ""), so removing the block.LogsBloom == "" guard and letting the emptiness check run would correctly flag "bloom is zero (absent) but got N ground-truth log records."

Suggestion: remove the block.LogsBloom == "" short-circuit, or treat it as equivalent to a zero bloom and let the downstream checks handle it. This was also noted by the Codex review.

Fix this →


🟡 Double iteration when both flags are enabled (lines 787–791 and 823+)

When both ValidateLogsBloomEmptiness and ValidateLogsBloomMatch are true, the logs slice is iterated twice: first to count non-nil entries, then again to build the expected bloom. The gtLogCount variable is only needed for the emptiness check and the error message — and a recount during bloom construction is straightforward. Consider folding the count into the bloom-building loop, at a minimum making the first pass conditional on ValidateLogsBloomEmptiness && !ValidateLogsBloomMatch (as the Copilot review also noted).

Fix this →


🟡 Error message lacks identifying context (line 856)

fmt.Errorf("block logsBloom does not match ground-truth logs")

This error has no block identity (number/hash) and no gtLogCount, which makes it difficult to correlate with a specific block in logs. Including block.Number and gtLogCount (and optionally block.Hash) would make the error actionable. The Copilot review already included a concrete suggestion for this.


architecture/evm/eth_getBlockByNumber_validation_test.go

🟡 Missing test cases

The current tests cover:

  • Non-zero bloom + empty ground-truth logs → reject ✓
  • Non-zero bloom + matching logs → accept ✓
  • Non-matching bloom → reject ✓
  • No ground-truth logs → skip ✓

Missing cases worth adding:

  • Zero bloom + empty ground-truth logs → accept: The most basic valid "no-log" block. This ensures the emptiness check doesn't false-positive on empty blocks.
  • Zero bloom + non-empty ground-truth logs → reject: The symmetric case of the first test. Currently no test exercises this path.
  • Empty string logsBloom + non-empty ground-truth logs: Covers the guard-bypass gap described above once fixed.

Fix this →


common/defaults.go

🟢 Default-on is correct but is a breaking change

Setting ValidateLogsBloomEmptiness to true by default is intentional and well-motivated. The only concern is existing deployments where upstreams don't populate logsBloom at all — those requests will now silently pass (due to the block.LogsBloom == "" guard above), but once that guard is fixed, such blocks would start being rejected. Worth a callout in the changelog/migration notes.


docs/pages/config/failsafe/integrity.mdx

🟢 Documentation update is accurate

The updated sample config and table entry correctly reflect the new default. Minor nit: the table description "when logs are available to validate" could be more precise — the check fires when GroundTruthLogs is explicitly provided to the request directives, not based on response content alone.


Summary

Severity Issue
🔴 Empty logsBloom guard bypasses emptiness validation
🟡 Double-iteration of ground-truth logs when both flags are on
🟡 Bloom mismatch error lacks block number/hash and log count
🟡 Missing test cases for zero-bloom and empty-string logsBloom paths
🟢 Default-on change needs migration note consideration

|

@0x666c6f
Copy link
Copy Markdown
Collaborator Author

0x666c6f commented Mar 6, 2026

@CodeW review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


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

Comment on lines +779 to +784
if !dirs.ValidateLogsBloomEmptiness && !dirs.ValidateLogsBloomMatch {
return nil
}
if dirs.GroundTruthLogs == nil || block == nil {
return nil
}
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

validateBlockLogsBloom relies on the distinction between GroundTruthLogs == nil (no ground truth provided → skip) and len(GroundTruthLogs) == 0 (explicitly provided empty logs → should validate emptiness). However, common.RequestDirectives.Clone() currently only copies GroundTruthLogs when len(d.GroundTruthLogs) > 0, so an explicit empty slice will be dropped during directive cloning (e.g., code paths that do nq.Directives().Clone()), causing this validation to be skipped unexpectedly. Preserve the “explicit empty” semantics by updating RequestDirectives.Clone() to copy GroundTruthLogs when the slice is non-nil (even if empty).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 1d67aa3. Block-level bloom validation now requires GroundTruthLogsComplete, and RequestDirectives.Clone() preserves explicit empty GroundTruthLogs slices plus the completeness flag. See common/request.go and architecture/evm/eth_getBlockByNumber.go.

Comment on lines +845 to +851
fmt.Errorf("block logsBloom is non-zero but got 0 ground-truth log records"),
u,
)
}
if bloomIsZero && hasLogs {
return common.NewErrEndpointContentValidation(
fmt.Errorf("block logsBloom is zero but got %d ground-truth log records", gtLogCount),
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The new bloom/log emptiness validation errors don’t include block identifiers (number/hash), unlike the missing/mismatch errors below. Including block.Number/block.Hash (and maybe gtLogCount) in these two messages would make upstream debugging much easier, especially now that this check is default-on.

Suggested change
fmt.Errorf("block logsBloom is non-zero but got 0 ground-truth log records"),
u,
)
}
if bloomIsZero && hasLogs {
return common.NewErrEndpointContentValidation(
fmt.Errorf("block logsBloom is zero but got %d ground-truth log records", gtLogCount),
fmt.Errorf(
"block logsBloom is non-zero but got %d ground-truth log records (block number=%s hash=%s)",
gtLogCount,
block.Number,
block.Hash,
),
u,
)
}
if bloomIsZero && hasLogs {
return common.NewErrEndpointContentValidation(
fmt.Errorf(
"block logsBloom is zero but got %d ground-truth log records (block number=%s hash=%s)",
gtLogCount,
block.Number,
block.Hash,
),

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 1d67aa3. The emptiness validation errors now include block number/hash context; see architecture/evm/eth_getBlockByNumber.go.

@0x666c6f
Copy link
Copy Markdown
Collaborator Author

0x666c6f commented Mar 6, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2678ff3e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +843 to +845
if !bloomIsZero && !hasLogs {
return common.NewErrEndpointContentValidation(
fmt.Errorf("block logsBloom is non-zero but got 0 ground-truth log records"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Skip non-zero bloom rejection for partial ground-truth logs

logsBloom represents all logs in the block, but this branch assumes GroundTruthLogs is a complete block log set and rejects any non-zero bloom when that slice is empty. If a library caller populates GroundTruthLogs from a filtered eth_getLogs query (address/topic filters), empty results are valid even when the block has unrelated logs, so this check produces false content-validation failures and unnecessary retries; with the new default-on emptiness directive this becomes a production-facing regression.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 1d67aa3 and reinforced with test 3084568. Block-level bloom validation now skips unless GroundTruthLogsComplete is true, and there is a regression test covering incomplete/partial ground-truth logs in architecture/evm/eth_getBlockByNumber_validation_test.go.

@0x666c6f 0x666c6f merged commit 20366f1 into morpho-main Mar 6, 2026
3 of 4 checks passed
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.

2 participants