Skip to content

Conversation

@tac0turtle
Copy link
Contributor

@tac0turtle tac0turtle commented Jun 16, 2025

Overview

fixes #2250

this pr adds apphash verification based on the execution environment.

Summary by CodeRabbit

  • New Features
    • Added support for immediate and delayed execution modes, allowing more flexible block processing and validation.
    • Exposed execution mode information in executors and related interfaces.
  • Bug Fixes
    • Re-enabled strict block time validation to ensure block times are strictly increasing.
    • Improved AppHash validation logic based on execution mode.
  • Tests
    • Reactivated and updated tests for block time and AppHash validation.
    • Extended mocks to support execution mode queries.
  • Chores
    • Updated dependency management and Go toolchain versions.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 16, 2025

Walkthrough

The changes introduce explicit support for "execution modes" (immediate vs. delayed) throughout the block manager, executor interfaces, and related implementations. The Manager now caches the execution mode, and block validation, creation, and application logic are updated to handle these modes correctly. Timestamp and AppHash validation are reinstated in execValidate, with their logic conditional on execution mode. Executor implementations, mocks, and tests are updated accordingly.

Changes

File(s) Change Summary
block/manager.go Added execution mode awareness to Manager; reinstated timestamp and AppHash validation in execValidate; updated block creation/application logic for execution modes.
core/execution/execution.go Introduced ExecutionMode type/constants; added GetExecutionMode() to Executor interface.
apps/testapp/kv/kvexecutor.go
core/execution/dummy.go
execution/evm/execution.go
Added GetExecutionMode() method to executor implementations, returning appropriate execution mode.
block/manager_test.go Reactivated tests for timestamp and AppHash validation in execValidate.
test/mocks/execution.go Added mock support for GetExecutionMode() in MockExecutor.
block/publish_block_p2p_test.go Added GetExecutionMode() to test mockExecutor.
apps/testapp/go.mod
execution/evm/go.mod
Updated module files: dependency adjustments, Go/toolchain version bump, and local replace directive.

Sequence Diagram(s)

sequenceDiagram
    participant Manager
    participant Executor
    participant State

    Note over Manager: Block Validation (execValidate)
    Manager->>Executor: GetExecutionMode()
    alt Delayed Execution
        Manager->>State: Compare AppHash with header (previous block)
    else Immediate Execution
        Manager->>State: Skip AppHash check (AppHash reflects post-execution)
    end
    Manager->>State: Validate timestamp is increasing
Loading
sequenceDiagram
    participant Manager
    participant Executor
    participant State

    Note over Manager: Block Creation (execCreateBlock)
    Manager->>Executor: GetExecutionMode()
    alt Immediate Execution and non-empty block
        Manager->>Executor: ExecuteTxs()
        Executor-->>Manager: New state root (AppHash)
    else
        Manager->>State: Use last state's AppHash
    end
    Manager->>Manager: Set header fields (version, chain ID, etc.)
Loading

Assessment against linked issues

Objective Addressed Explanation
Reinstate timestamp validation in execValidate (#2250)
Reinstate AppHash validation in execValidate, conditional on execution mode (#2250)
Make execValidate aware of immediate vs. deferred execution modes (#2250)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation

Suggested reviewers

  • tzdybal
  • julienrbrt
  • randygrok
  • alpe

Poem

In fields of code where blocks align,
The rabbit hops through space and time.
Immediate or delayed, the mode’s now clear,
AppHash and timestamps reappear!
With every test and interface tuned,
The blockchain garden’s finely pruned.
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6322e1e and 1e6f42f.

⛔ Files ignored due to path filters (1)
  • execution/evm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • block/manager.go (7 hunks)
  • block/manager_test.go (1 hunks)
  • execution/evm/go.mod (1 hunks)
  • test/mocks/execution.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • block/manager_test.go
  • execution/evm/go.mod
  • test/mocks/execution.go
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.go`: Follow standard Go formatting (enforced by golangci-lint). Use meanin...

**/*.go: Follow standard Go formatting (enforced by golangci-lint).
Use meaningful variable names in Go code.
Keep functions small and focused in Go code.
Document exported types and functions in Go code.
Use context.Context for cancellation in Go code.
Wrap errors with context using fmt.Errorf in Go code.
Return errors early in Go code.
Use custom error types for domain-specific errors in Go code.
Use structured logging and include relevant context in log messages in Go code.
Use appropriate log levels in Go code.
Never expose private keys in logs or errors.
Validate all inputs from external sources.
Use secure random number generation in Go code.
Be careful with concurrent access to shared state in Go code.
Be mindful of goroutine leaks in Go code.
Use buffered channels appropriately in Go code.
Profile before optimizing Go code.
Consider memory allocation in hot paths in Go code.

📄 Source: CodeRabbit Inference Engine (CLAUDE.md)

List of files the instruction was applied to:

  • block/manager.go
🧠 Learnings (2)
📓 Common learnings
Learnt from: tzdybal
PR: rollkit/rollkit#1641
File: node/full_node_test.go:266-321
Timestamp: 2024-10-08T18:35:32.960Z
Learning: Issue #1663 was created to address adding comprehensive tests for error scenarios in `TestVoteExtension` in the rollkit repository.
Learnt from: tzdybal
PR: rollkit/rollkit#1641
File: node/full_node_test.go:266-321
Timestamp: 2024-06-10T19:23:16.839Z
Learning: Issue #1663 was created to address adding comprehensive tests for error scenarios in `TestVoteExtension` in the rollkit repository.
Learnt from: S1nus
PR: rollkit/rollkit#1374
File: block/manager.go:0-0
Timestamp: 2024-10-08T18:35:32.960Z
Learning: The overriding of `DataHash` and `Commit` in `block/manager.go` is necessary for the block to pass basic validation when `applyBlock` is called. The user has added a comment to document this requirement.
Learnt from: S1nus
PR: rollkit/rollkit#1374
File: block/manager.go:0-0
Timestamp: 2024-06-10T19:23:16.839Z
Learning: The overriding of `DataHash` and `Commit` in `block/manager.go` is necessary for the block to pass basic validation when `applyBlock` is called. The user has added a comment to document this requirement.
block/manager.go (4)
Learnt from: S1nus
PR: rollkit/rollkit#1374
File: block/manager.go:0-0
Timestamp: 2024-10-08T18:35:32.960Z
Learning: The overriding of `DataHash` and `Commit` in `block/manager.go` is necessary for the block to pass basic validation when `applyBlock` is called. The user has added a comment to document this requirement.
Learnt from: S1nus
PR: rollkit/rollkit#1374
File: block/manager.go:0-0
Timestamp: 2024-06-10T19:23:16.839Z
Learning: The overriding of `DataHash` and `Commit` in `block/manager.go` is necessary for the block to pass basic validation when `applyBlock` is called. The user has added a comment to document this requirement.
Learnt from: S1nus
PR: rollkit/rollkit#1368
File: types/utils.go:128-130
Timestamp: 2024-06-10T19:23:16.839Z
Learning: The `ProposerPubkey` field in the `SignedHeader` struct is of type `[]byte` and not `ed25519.PubKey`, which requires conversion before using `Address().Bytes()` method.
Learnt from: S1nus
PR: rollkit/rollkit#1368
File: types/utils.go:128-130
Timestamp: 2024-10-08T18:35:32.960Z
Learning: The `ProposerPubkey` field in the `SignedHeader` struct is of type `[]byte` and not `ed25519.PubKey`, which requires conversion before using `Address().Bytes()` method.
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test / Run Integration Tests
  • GitHub Check: test / Run Unit Tests
  • GitHub Check: test / Build All Rollkit Binaries
  • GitHub Check: test / Build Rollkit Docker Image
  • GitHub Check: test / Build Rollkit EVM Single Docker Image
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (5)
block/manager.go (5)

150-151: Good performance optimization by caching execution mode.

Caching the execution mode from the executor avoids repeated method calls and improves performance. The initialization is properly handled during manager construction.

Also applies to: 356-357, 387-387


808-823: Excellent restoration of critical validations with execution mode awareness.

The timestamp validation ensures strictly increasing block times, which is essential for blockchain consistency. The conditional AppHash validation correctly handles the semantic differences between execution modes:

  • Delayed mode: Validates against previous state since AppHash reflects pre-execution state
  • Immediate mode: Skips validation since AppHash reflects post-execution state

This implementation properly addresses the execution verification requirements mentioned in the PR objectives.


827-827: Well-implemented execution mode-aware block creation logic.

The changes correctly implement the different semantics for immediate vs delayed execution modes:

  1. Immediate mode: Executes transactions during block creation to compute the correct AppHash for the new state
  2. Delayed mode: Uses the previous state's AppHash since execution happens later
  3. Empty blocks: Properly handled with empty transaction lists and appropriate DataHash

The use of lastState for version and ChainID ensures consistency with the current blockchain state.

Also applies to: 853-884, 888-889, 892-893, 899-899, 910-910


920-962: Excellent execution mode-aware block application logic.

The rewritten execApplyBlock method correctly implements the execution semantics for both modes:

  1. Immediate mode optimization: Block creators can trust their computed AppHash, while syncing nodes verify by re-executing transactions
  2. Delayed mode consistency: Always executes transactions to maintain deterministic state progression
  3. Proper context enrichment: Adding the signed header to context for executor access
  4. Robust error handling: Clear error messages for AppHash mismatches and execution failures

This implementation aligns well with the PR's execution verification objectives and maintains blockchain integrity across different execution environments.


945-961: Robust transaction execution and verification implementation.

The transaction execution logic properly handles both execution modes:

  1. Consistent transaction preparation: Proper conversion from types.Tx to raw byte slices
  2. Context enrichment: Adding signed header to context enables executors to access block metadata
  3. AppHash verification: Critical security check in immediate mode prevents state divergence
  4. Clear error reporting: Informative error messages aid in debugging and monitoring

The implementation ensures execution integrity while providing the flexibility needed for different execution environments.

Also applies to: 964-974

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 16, 2025

The latest Buf updates on your PR. Results from workflow CI and Release / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedJun 30, 2025, 1:42 PM

@codecov
Copy link

codecov bot commented Jun 16, 2025

Codecov Report

Attention: Patch coverage is 53.12500% with 45 lines in your changes missing coverage. Please review.

Project coverage is 72.48%. Comparing base (70fa52e) to head (1e6f42f).
Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
block/manager.go 54.25% 41 Missing and 2 partials ⚠️
core/execution/dummy.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2377      +/-   ##
==========================================
- Coverage   73.05%   72.48%   -0.58%     
==========================================
  Files          67       67              
  Lines        6377     6510     +133     
==========================================
+ Hits         4659     4719      +60     
- Misses       1320     1391      +71     
- Partials      398      400       +2     
Flag Coverage Δ
combined 72.48% <53.12%> (-0.58%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tac0turtle tac0turtle marked this pull request as ready for review June 19, 2025 09:50
Copy link
Member

@Manav-Aggarwal Manav-Aggarwal left a comment

Choose a reason for hiding this comment

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

Overall, looks good to me, would be good to have an E2E test with the full node verifying both cases - immediate and delayed though. Also, need to fix E2E tests and merge conflicts

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)
block/manager.go (1)

819-850: Review execution mode handling in block creation.

The execution mode logic appears correct:

  • For immediate mode with transactions: executes transactions during creation to compute AppHash
  • For delayed mode or empty blocks: uses AppHash from last state

However, there's a potential issue with the empty block check condition on line 834.

Consider clarifying the condition logic:

-	if m.executionMode == coreexecutor.ExecutionModeImmediate && !isEmpty {
+	if m.executionMode == coreexecutor.ExecutionModeImmediate && !isEmpty {

While the current logic is correct, you might want to add a comment explaining why empty blocks always use the last state's AppHash regardless of execution mode:

	// Determine AppHash based on execution mode
	var appHash []byte
+	// Note: Empty blocks always use last state AppHash regardless of execution mode
+	// since there are no transactions to execute
	if m.executionMode == coreexecutor.ExecutionModeImmediate && !isEmpty {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6153b00 and 0ea1823.

📒 Files selected for processing (5)
  • block/manager.go (7 hunks)
  • block/manager_test.go (1 hunks)
  • block/publish_block_p2p_test.go (2 hunks)
  • da/internal/mocks/da.go (1 hunks)
  • test/mocks/da.go (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • da/internal/mocks/da.go
  • test/mocks/da.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • block/manager_test.go
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: test / Build Rollkit EVM Single Docker Image
  • GitHub Check: test / Build Rollkit Docker Image
  • GitHub Check: test / Run Unit Tests
  • GitHub Check: test / Build All Rollkit Binaries
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (8)
block/publish_block_p2p_test.go (2)

23-23: LGTM: Appropriate import addition.

The import of the core execution package is necessary to support the new execution mode functionality.


240-242: LGTM: Correct interface implementation.

The mock executor correctly implements the new GetExecutionMode() method, returning ExecutionModeDelayed which is consistent with other test executors and supports the execution mode feature.

block/manager.go (6)

156-157: LGTM: Proper execution mode caching.

The execution mode is correctly cached from the executor during manager initialization and stored in the struct for efficient access during block operations.

Also applies to: 362-362, 393-393


775-789: LGTM: Execution mode-aware validation logic.

The validation correctly:

  • Re-enables timestamp validation for strictly increasing block times
  • Implements execution mode-specific AppHash validation
  • In delayed mode, validates AppHash matches last state (since transactions haven't been executed yet)
  • Appropriately skips AppHash validation in immediate mode during this phase

The logic aligns with the execution mode semantics.


794-794: LGTM: Added lastState parameter for block creation.

The addition of the lastState parameter enables the block creation logic to access version and chain ID information, which is necessary for proper block header construction.


854-866: LGTM: Proper usage of lastState for header fields.

The header construction correctly uses lastState for version, chainID, and computed AppHash, ensuring consistency with the current blockchain state.


917-917: LGTM: Proper context passing for signed header.

The addition of the signed header to the context enables executors to access header information during transaction execution, which may be necessary for certain validation or state computations.

Also applies to: 936-936


886-942: Verify the block creator detection logic.

The execution logic for immediate mode is sophisticated but potentially fragile. The block creator detection relies on:

  1. Address matching between signer and proposer
  2. Height comparison with store height + 1

This could have edge cases during concurrent operations or rapid block production.

The logic assumes that if we're the proposer and the height matches currentHeight + 1, then we created this block. However, this could be problematic in scenarios where:

  1. Multiple blocks are being processed concurrently
  2. There's a race condition between block creation and application
  3. Store height updates happen asynchronously

Consider adding additional verification or making the block creator detection more robust.

#!/bin/bash
# Search for concurrent access patterns and potential race conditions
rg -A 5 -B 5 "store\.Height|SetHeight" --type go
ast-grep --pattern 'go func() {
  $$$
  SetHeight($$$)
  $$$
}'

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ea1823 and 6322e1e.

⛔ Files ignored due to path filters (1)
  • execution/evm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • block/manager.go (7 hunks)
  • block/manager_test.go (1 hunks)
  • execution/evm/execution.go (1 hunks)
  • execution/evm/go.mod (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • execution/evm/go.mod
  • execution/evm/execution.go
  • block/manager_test.go
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: test / Go Mod Tidy Check
  • GitHub Check: test / Run Unit Tests
  • GitHub Check: test / Build Rollkit EVM Single Docker Image
  • GitHub Check: test / Build All Rollkit Binaries
  • GitHub Check: test / Build Rollkit Docker Image
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (10)
block/manager.go (10)

159-160: LGTM! Clean addition of execution mode caching.

The addition of the executionMode field to cache the executor's execution mode is well-documented and follows good practice by avoiding repeated calls to GetExecutionMode().


365-365: LGTM! Proper execution mode retrieval.

Getting the execution mode from the executor during manager initialization is the correct approach.


396-396: LGTM! Execution mode properly stored in manager.

The execution mode is correctly stored in the manager struct for later use.


817-832: LGTM! Improved validation logic with execution mode awareness.

The timestamp validation re-enablement and conditional AppHash validation based on execution mode is well-implemented:

  • Timestamp monotonicity validation is correctly restored
  • AppHash validation is appropriately skipped for immediate mode since the AppHash represents the state after executing the current block's transactions
  • The error messages are clear and informative

836-836: LGTM! Updated method signature with lastState parameter.

The addition of the lastState parameter to execCreateBlock is necessary for the execution mode logic that follows.


861-893: LGTM! Well-structured execution mode handling in block creation.

The logic correctly handles both execution modes:

  • For immediate mode with non-empty blocks: executes transactions to compute the new state root
  • For delayed mode or empty blocks: uses the last state's AppHash
  • Transaction conversion is properly handled
  • Error handling is appropriate

The implementation follows the execution semantics correctly.


897-908: LGTM! Proper header construction with state-derived values.

The header construction correctly uses values from lastState:

  • Version information is properly copied
  • ChainID is set from the last state
  • AppHash is set based on the execution mode logic above

918-924: LGTM! Correct DataHash handling for empty vs non-empty blocks.

The DataHash is properly set:

  • For non-empty blocks: uses the block data's DA commitment
  • For empty blocks: uses the predefined hash for empty transactions

948-984: LGTM! Sound execution logic for immediate mode.

The execution logic properly handles both cases:

  • Block creator: trusts the header's AppHash (since transactions were already executed during creation)
  • Syncing node: executes transactions to verify the AppHash matches

The AppHash verification and error handling are correct.


971-984: LGTM! Consistent delayed mode execution.

The delayed mode logic correctly always executes transactions regardless of whether the node is the block creator, which aligns with the delayed execution semantics.

Comment on lines +929 to 984
var newStateRoot []byte

// Check if we need to execute transactions
if m.executionMode == coreexecutor.ExecutionModeImmediate {
// For immediate mode, the aggregator already executed during block creation
// But syncing nodes need to execute to verify the AppHash
// Check if we're the block creator by verifying we signed this block
isBlockCreator := false
if m.signer != nil {
myAddress, err := m.signer.GetAddress()
if err == nil && bytes.Equal(header.ProposerAddress, myAddress) {
// Also verify this is the current height we're producing
currentHeight, err := m.store.Height(ctx)
if err == nil && header.Height() == currentHeight+1 {
isBlockCreator = true
}
}
}

ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
newStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
if isBlockCreator {
// We created this block, so we already executed transactions in execCreateBlock
// Just use the AppHash from the header
newStateRoot = header.AppHash
} else {
// We're syncing this block, need to execute to verify AppHash
rawTxs := make([][]byte, len(data.Txs))
for i := range data.Txs {
rawTxs[i] = data.Txs[i]
}

ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
computedStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
}

// Verify the AppHash matches
if !bytes.Equal(header.AppHash, computedStateRoot) {
return types.State{}, fmt.Errorf("AppHash mismatch in immediate mode: expected %x, got %x", computedStateRoot, header.AppHash)
}
newStateRoot = computedStateRoot
}
} else {
// For delayed mode, always execute transactions
rawTxs := make([][]byte, len(data.Txs))
for i := range data.Txs {
rawTxs[i] = data.Txs[i]
}

ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
var err error
newStateRoot, _, err = m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential logic issue in block creator detection.

The block creator detection logic has a potential race condition or timing issue. The check header.Height() == currentHeight+1 might not be reliable in all scenarios, especially during concurrent operations or if there are delays in processing.

Consider simplifying the block creator detection or adding additional safeguards.

-		// Check if we're the block creator by verifying we signed this block
-		isBlockCreator := false
-		if m.signer != nil {
-			myAddress, err := m.signer.GetAddress()
-			if err == nil && bytes.Equal(header.ProposerAddress, myAddress) {
-				// Also verify this is the current height we're producing
-				currentHeight, err := m.store.Height(ctx)
-				if err == nil && header.Height() == currentHeight+1 {
-					isBlockCreator = true
-				}
-			}
-		}
+		// Check if we're the block creator by verifying we signed this block
+		isBlockCreator := false
+		if m.signer != nil {
+			myAddress, err := m.signer.GetAddress()
+			if err == nil && bytes.Equal(header.ProposerAddress, myAddress) {
+				isBlockCreator = true
+			}
+		}

The height check might be unnecessary since the proposer address check should be sufficient to determine if we created the block.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var newStateRoot []byte
// Check if we need to execute transactions
if m.executionMode == coreexecutor.ExecutionModeImmediate {
// For immediate mode, the aggregator already executed during block creation
// But syncing nodes need to execute to verify the AppHash
// Check if we're the block creator by verifying we signed this block
isBlockCreator := false
if m.signer != nil {
myAddress, err := m.signer.GetAddress()
if err == nil && bytes.Equal(header.ProposerAddress, myAddress) {
// Also verify this is the current height we're producing
currentHeight, err := m.store.Height(ctx)
if err == nil && header.Height() == currentHeight+1 {
isBlockCreator = true
}
}
}
ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
newStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
if isBlockCreator {
// We created this block, so we already executed transactions in execCreateBlock
// Just use the AppHash from the header
newStateRoot = header.AppHash
} else {
// We're syncing this block, need to execute to verify AppHash
rawTxs := make([][]byte, len(data.Txs))
for i := range data.Txs {
rawTxs[i] = data.Txs[i]
}
ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
computedStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
}
// Verify the AppHash matches
if !bytes.Equal(header.AppHash, computedStateRoot) {
return types.State{}, fmt.Errorf("AppHash mismatch in immediate mode: expected %x, got %x", computedStateRoot, header.AppHash)
}
newStateRoot = computedStateRoot
}
} else {
// For delayed mode, always execute transactions
rawTxs := make([][]byte, len(data.Txs))
for i := range data.Txs {
rawTxs[i] = data.Txs[i]
}
ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
var err error
newStateRoot, _, err = m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
}
}
var newStateRoot []byte
// Check if we need to execute transactions
if m.executionMode == coreexecutor.ExecutionModeImmediate {
// For immediate mode, the aggregator already executed during block creation
// But syncing nodes need to execute to verify the AppHash
// Check if we're the block creator by verifying we signed this block
isBlockCreator := false
if m.signer != nil {
myAddress, err := m.signer.GetAddress()
if err == nil && bytes.Equal(header.ProposerAddress, myAddress) {
isBlockCreator = true
}
}
if isBlockCreator {
// We created this block, so we already executed transactions in execCreateBlock
// Just use the AppHash from the header
newStateRoot = header.AppHash
} else {
// We're syncing this block, need to execute to verify AppHash
rawTxs := make([][]byte, len(data.Txs))
for i := range data.Txs {
rawTxs[i] = data.Txs[i]
}
ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
computedStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
}
// Verify the AppHash matches
if !bytes.Equal(header.AppHash, computedStateRoot) {
return types.State{}, fmt.Errorf("AppHash mismatch in immediate mode: expected %x, got %x", computedStateRoot, header.AppHash)
}
newStateRoot = computedStateRoot
}
} else {
// For delayed mode, always execute transactions
rawTxs := make([][]byte, len(data.Txs))
for i := range data.Txs {
rawTxs[i] = data.Txs[i]
}
ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header)
var err error
newStateRoot, _, err = m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash)
if err != nil {
return types.State{}, fmt.Errorf("failed to execute transactions: %w", err)
}
}
🤖 Prompt for AI Agents
In block/manager.go around lines 929 to 984, the block creator detection logic
uses a height check that may cause race conditions or timing issues. To fix
this, remove the height comparison and rely solely on verifying if the signer’s
address matches the header’s proposer address to determine block creator status.
This simplifies the logic and avoids potential concurrency problems.

@tac0turtle
Copy link
Contributor Author

tac0turtle commented Jun 30, 2025

merging this as the follow up evm tests will test the desired behaviour

@tac0turtle tac0turtle added this pull request to the merge queue Jun 30, 2025
Merged via the queue into main with commit 1ca8457 Jun 30, 2025
28 checks passed
@tac0turtle tac0turtle deleted the marko/2250 branch June 30, 2025 14:14
@github-project-automation github-project-automation bot moved this to Done in Evolve Jun 30, 2025
Manav-Aggarwal added a commit that referenced this pull request Jul 1, 2025
@Manav-Aggarwal Manav-Aggarwal restored the marko/2250 branch July 1, 2025 03:17
@mergify
Copy link

mergify bot commented Jul 1, 2025

⚠️ The sha of the head commit of this PR conflicts with #2415. Mergify cannot evaluate rules on this PR. ⚠️

github-merge-queue bot pushed a commit that referenced this pull request Jul 3, 2025
<!--
Please read and fill out this form before submitting your PR.

Please make sure you have reviewed our contributors guide before
submitting your
first PR.

NOTE: PR titles should follow semantic commits:
https://www.conventionalcommits.org/en/v1.0.0/
-->

## Overview

Simpler version of #2377, was
reverted in #2394

<!-- 
Please provide an explanation of the PR, including the appropriate
context,
background, goal, and rationale. If there is an issue with this
information,
please provide a tl;dr and link the issue. 

Ex: Closes #<issue number>
-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved block validation to enforce stricter checks on block
timestamp and application hash consistency.
* Enhanced error messages for validation failures to provide clearer
feedback.

* **Tests**
* Re-enabled tests to verify block timestamp and application hash
validation during block processing.

* **Refactor**
* Simplified transaction execution logic for improved performance by
removing unnecessary locking during forkchoice state creation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
alpe added a commit that referenced this pull request Jul 3, 2025
* main:
  feat: fix execution verification (#2433)
  build(deps): Bump github.com/spf13/viper from 1.19.0 to 1.20.1 (#2431)
  build(deps): Bump github.com/libp2p/go-libp2p-kad-dht from 0.29.1 to 0.33.1 (#2432)
  build(deps): Bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0 (#2430)
  build(deps): Bump alpine from 3.18.3 to 3.22.0 (#2429)
  ci: fix dependabot patterns (#2427)
  feat: migrate logging to ipfs/go-log/v2 (#2416)
  fix: Consolidate some EVM E2E tests into one (#2423)
  feat: Support GetHeader in store (#2422)
  fix: Optimize E2E EVM tests (#2421)
  fix: getPeers size check (#2417)
  test: Add comprehensive EVM E2E tests (#2394)
  feat: migrate logging to ipfs/go-log/v2 (#2411)
  feat: execution verification  (#2377)
@tac0turtle tac0turtle removed this from Evolve Aug 25, 2025
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.

Add back timestamp and app hash validation in execValidate

3 participants