Skip to content

feat: add block rollback support#2499

Merged
tac0turtle merged 24 commits intomainfrom
julien/rollback
Aug 21, 2025
Merged

feat: add block rollback support#2499
tac0turtle merged 24 commits intomainfrom
julien/rollback

Conversation

@julienrbrt
Copy link
Copy Markdown
Member

@julienrbrt julienrbrt commented Jul 28, 2025

Closes: #2407
Superseded #2446

  • Create an API for rollback to use in execution environments
  • Adds an example in testapp for kv app
  • Helps Rollback ev-abci#204

NOTE: this doesn't implement Rollback for any prod execution environment (evm).

- Add `rollback` command to testapp for rolling back the last block -
Implement `RollbackOneBlock` in block manager to revert one block - Add
`Rollback` method to store interface and implementation - Add mock for
store rollback in tests
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jul 28, 2025

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedAug 21, 2025, 8:55 AM

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @julienrbrt, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a fundamental block rollback capability to the ev-node system. It provides the necessary API within the block management layer and the underlying data store to revert the blockchain state by one block. Additionally, a command-line utility is provided to facilitate testing and usage of this new feature.

Highlights

  • Block Rollback API: Introduced a new RollbackOneBlock method within the block.Manager to enable reverting the node to the state of the previous block. This involves fetching previous block data, deleting current block data from the store, and reapplying the state of the previous block.
  • Data Store Rollback: Added a Rollback method to the pkg/store.Store interface and its DefaultStore implementation. This method handles the deletion of block headers, data, signatures, and index keys for blocks beyond a specified height, effectively truncating the block history in the data store.
  • Command-Line Utility: A new rollback command has been added to the testapp CLI, allowing users to easily trigger a single block rollback operation for testing and debugging purposes.
  • Mocking Updates: The test/mocks/store.go file was updated to include a mock implementation for the new Rollback method, ensuring testability of components interacting with the Store interface.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces block rollback support, a valuable feature for chain stability. The implementation adds a RollbackOneBlock method to the block manager, a corresponding Rollback method to the store, and a new rollback command to the testapp.

My review has identified a few critical issues that should be addressed. The core rollback logic in block.Manager appears to be flawed, as it re-applies a block instead of restoring a previous state. The store's Rollback operation is not atomic, which could lead to data inconsistency. Furthermore, the new functionality is missing unit tests, which are essential for ensuring its correctness.

I've provided specific suggestions to fix these issues. Once these points are addressed, this will be a solid contribution to the project.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Jul 29, 2025

Claude finished @julienrbrt's task —— View job


Pull Request Code Review

I've completed a thorough review of this PR that adds block rollback support to ev-node. Here's my comprehensive analysis:

Review Progress:

  • Analyze core rollback implementation
  • Review store rollback functionality
  • Examine CLI command implementation
  • Assess test coverage
  • Check for security and performance issues
  • Provide comprehensive feedback

Overall Assessment

This PR introduces a solid foundation for block rollback functionality with good test coverage and security considerations. However, there are several areas that need attention before merge.

Major Issues

1. Store Rollback Logic - Critical Bug in State Update 🚨

Location: pkg/store/store.go:324-342

The current implementation has a critical flaw in how it handles state restoration:

targetState, err := s.GetStateAtHeight(ctx, height)
// ...
if err := batch.Put(ctx, ds.NewKey(getStateAtHeightKey(height)), data); err != nil {
    return fmt.Errorf("failed to set state at height %d: %w", height, err)
}

Problem: The code saves the target state at the same height key, but it should be updating the "current" state to point to the target state. This creates inconsistency between GetState() (which uses current height) and the actual state.

Fix: Should update both the height and the current state key to maintain consistency.

2. Incomplete Rollback of State History ⚠️

Location: pkg/store/store.go:291-315

The rollback only deletes block data but doesn't clean up state entries stored at heights above the rollback point. This leaves orphaned state data that could cause issues.

Recommendation: Add cleanup of state keys for heights > rollback target.

Security Issues

3. DA Finalized Height ProtectionGood

Location: pkg/store/store.go:281-289

The implementation correctly prevents rollbacks below the DA included height, which is crucial for chain integrity. Well implemented!

4. Log Injection Vulnerability ⚠️

Location: apps/testapp/kv/kvexecutor.go:59, 71

User-controlled input is logged without sanitization:

fmt.Printf("Error querying DB for key '%s': %v\n", key, err)
fmt.Printf("Error iterating query results for key '%s': %v\n", key, result.Error)

Risk: Log injection if keys contain special characters or ANSI escape sequences.
Fix: Sanitize or escape user input before logging.

Code Quality Issues

5. CLI Command Code Duplication

Location: apps/testapp/cmd/rollback.go

As noted by previous reviewers, there's significant code duplication between the rollback and start commands. Consider extracting common setup logic into a shared helper function.

6. Interface Documentation

Location: pkg/store/types.go:49

The Rollback method documentation is vague: "deletes x height from the ev-node store"

Suggestion:

// Rollback deletes block data for all heights above the given height,
// effectively reverting the chain state to the specified height.
Rollback(ctx context.Context, height uint64) error

Performance Considerations

7. Batch Operation EfficiencyGood

The use of batching for atomic operations is well implemented and ensures consistency.

8. Height Iteration Efficiency

Location: pkg/store/store.go:291-315

The rollback iterates backwards from current height, which is efficient for small rollbacks but could be optimized for large ones.

Test Coverage Assessment

9. Excellent Test CoverageExcellent

The test coverage is comprehensive with 8 different rollback test scenarios covering:

  • Basic rollback functionality
  • Edge cases (same height, higher height)
  • Error conditions (batch errors, height errors)
  • DA finalized height validation
  • Various error paths

This addresses the previous Gemini Code Assist feedback about missing tests.

10. KV Executor Rollback Tests ⚠️

While the store has excellent test coverage, I didn't find comprehensive tests for the KVExecutor.Rollback method. Consider adding tests for the executor-level rollback logic.

Minor Issues

11. Error Handling Consistency

Some error messages could be more descriptive, particularly in the KV executor rollback logic.

12. Magic Numbers

Location: apps/testapp/kv/kvexecutor.go:161, 189, 277

The gas value 1024 appears as a magic number. Consider making it a constant.

Positive Aspects ✅

  1. Atomic Operations: Excellent use of batching for consistency
  2. Security: Proper validation against DA finalized height
  3. Test Coverage: Comprehensive test suite with multiple scenarios
  4. Error Handling: Generally good error propagation and context
  5. Interface Design: Clean separation between store and executor rollback

Recommendations

Priority 1 (Must Fix):

  • Fix state update logic in store rollback
  • Address log injection vulnerabilities

Priority 2 (Should Fix):

  • Add state cleanup for orphaned state entries
  • Improve interface documentation
  • Add KV executor tests

Priority 3 (Nice to Have):

  • Refactor CLI command duplication
  • Extract magic numbers to constants

@codecov
Copy link
Copy Markdown

codecov bot commented Jul 29, 2025

Codecov Report

❌ Patch coverage is 47.77778% with 47 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.35%. Comparing base (0e82805) to head (ede5086).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/store/store.go 46.59% 32 Missing and 15 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2499      +/-   ##
==========================================
- Coverage   72.77%   72.35%   -0.43%     
==========================================
  Files          72       72              
  Lines        7310     7394      +84     
==========================================
+ Hits         5320     5350      +30     
- Misses       1566     1604      +38     
- Partials      424      440      +16     
Flag Coverage Δ
combined 72.35% <47.77%> (-0.43%) ⬇️

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.

@julienrbrt julienrbrt marked this pull request as ready for review July 29, 2025 15:54
}

// Rollback rolls back the state to the specified height.
func (c *EngineClient) Rollback(ctx context.Context, height uint64) error {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should be done in a follow-up by someone with more context with the EVM execution environment. This is enough to unblock me for ev-abci rollback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

with evm, the system would rollback on replay of the block or a different block.

return fmt.Errorf("failed to marshal state to protobuf: %w", err)
}
return s.db.Put(ctx, ds.NewKey(getStateKey()), data)
return s.db.Put(ctx, ds.NewKey(getStateAtHeightKey(currentHeight)), data)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This change increase the need of coming up with a pruning solution: #2093

q := query.Query{}
results, err := k.db.Query(ctx, q)
if err != nil {
fmt.Printf("Error querying DB for key '%s': %v\n", key, err)

Check failure

Code scanning / CodeQL

Log entries created from user input High test

This log entry depends on a
user-provided value
.

Copilot Autofix

AI 8 months ago

To fix the problem, we need to sanitize the user-provided key before logging it. Since the logs are plain text, the recommended approach is to remove any newline (\n) and carriage return (\r) characters from the key before including it in the log message. This can be done using strings.ReplaceAll. The fix should be applied directly at the point where the log message is constructed, i.e., in apps/testapp/kv/kvexecutor.go at line 59. We need to ensure that the strings package is imported (it already is), so no new imports are needed.


Suggested changeset 1
apps/testapp/kv/kvexecutor.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/testapp/kv/kvexecutor.go b/apps/testapp/kv/kvexecutor.go
--- a/apps/testapp/kv/kvexecutor.go
+++ b/apps/testapp/kv/kvexecutor.go
@@ -56,7 +56,8 @@
 	q := query.Query{}
 	results, err := k.db.Query(ctx, q)
 	if err != nil {
-		fmt.Printf("Error querying DB for key '%s': %v\n", key, err)
+		safeKey := strings.ReplaceAll(strings.ReplaceAll(key, "\n", ""), "\r", "")
+		fmt.Printf("Error querying DB for key '%s': %v\n", safeKey, err)
 		return "", false
 	}
 	defer results.Close()
EOF
@@ -56,7 +56,8 @@
q := query.Query{}
results, err := k.db.Query(ctx, q)
if err != nil {
fmt.Printf("Error querying DB for key '%s': %v\n", key, err)
safeKey := strings.ReplaceAll(strings.ReplaceAll(key, "\n", ""), "\r", "")
fmt.Printf("Error querying DB for key '%s': %v\n", safeKey, err)
return "", false
}
defer results.Close()
Copilot is powered by AI and may make mistakes. Always verify output.

for result := range results.Next() {
if result.Error != nil {
fmt.Printf("Error iterating query results for key '%s': %v\n", key, result.Error)

Check failure

Code scanning / CodeQL

Log entries created from user input High test

This log entry depends on a
user-provided value
.

Copilot Autofix

AI 8 months ago

To fix the problem, we need to sanitize the user input before logging it. For plain text logs, the recommended approach is to remove any newline (\n) and carriage return (\r) characters from the user input before including it in the log entry. This can be done using strings.ReplaceAll. The fix should be applied directly in the logging statement on line 71 of apps/testapp/kv/kvexecutor.go, ensuring that the sanitized version of key is used in the log message. No changes to existing functionality are required, and no new methods or definitions are needed. The only required import (strings) is already present.

Suggested changeset 1
apps/testapp/kv/kvexecutor.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/testapp/kv/kvexecutor.go b/apps/testapp/kv/kvexecutor.go
--- a/apps/testapp/kv/kvexecutor.go
+++ b/apps/testapp/kv/kvexecutor.go
@@ -68,7 +68,8 @@
 
 	for result := range results.Next() {
 		if result.Error != nil {
-			fmt.Printf("Error iterating query results for key '%s': %v\n", key, result.Error)
+			safeKey := strings.ReplaceAll(strings.ReplaceAll(key, "\n", ""), "\r", "")
+			fmt.Printf("Error iterating query results for key '%s': %v\n", safeKey, result.Error)
 			return "", false
 		}
 
EOF
@@ -68,7 +68,8 @@

for result := range results.Next() {
if result.Error != nil {
fmt.Printf("Error iterating query results for key '%s': %v\n", key, result.Error)
safeKey := strings.ReplaceAll(strings.ReplaceAll(key, "\n", ""), "\r", "")
fmt.Printf("Error iterating query results for key '%s': %v\n", safeKey, result.Error)
return "", false
}

Copilot is powered by AI and may make mistakes. Always verify output.
//
// Returns:
// - error: Any errors during rollback
Rollback(ctx context.Context, height uint64) error
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

since the execution environment needs to implement rollback already for their system, why not wrap our rollback call in their command?

Copy link
Copy Markdown
Contributor

@tac0turtle tac0turtle left a comment

Choose a reason for hiding this comment

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

i dont think we should extend the execution interface here. the app dev needs to implement rollback anyways then wrap this call. This is equivalent to the execution environment implementing the command

@julienrbrt
Copy link
Copy Markdown
Member Author

i dont think we should extend the execution interface here. the app dev needs to implement rollback anyways then wrap this call. This is equivalent to the execution environment implementing the command

I was thinking of that too. Do you think having a method on the rollkit store and remove the helper from the manager is sufficient?

Because when I added it in the manager it felt like adding it to the exécution interface made sense.

Then the command will be pure store access.

@julienrbrt julienrbrt requested a review from tac0turtle July 30, 2025 14:34
tac0turtle
tac0turtle previously approved these changes Aug 18, 2025
@julienrbrt julienrbrt enabled auto-merge August 20, 2025 10:33
@julienrbrt
Copy link
Copy Markdown
Member Author

Note, the rollback method (or its caller -- tbd) must make sure to not revert an height that has already been finalized (ie posted on da).

Add validation to disallow rollback to a height lower than the DA
included height, ensuring finalized heights cannot be rolled back. Add
tests for various DA included height scenarios.
@julienrbrt
Copy link
Copy Markdown
Member Author

Note, the rollback method (or its caller -- tbd) must make sure to not revert an height that has already been finalized (ie posted on da).

implemented in ede5086

@julienrbrt julienrbrt requested a review from tac0turtle August 20, 2025 22:13
tac0turtle
tac0turtle previously approved these changes Aug 21, 2025
tac0turtle
tac0turtle previously approved these changes Aug 21, 2025
@tac0turtle tac0turtle disabled auto-merge August 21, 2025 08:55
@tac0turtle tac0turtle merged commit f483445 into main Aug 21, 2025
18 of 22 checks passed
@tac0turtle tac0turtle deleted the julien/rollback branch August 21, 2025 08:55
@github-project-automation github-project-automation bot moved this to Done in Evolve Aug 21, 2025
@julienrbrt julienrbrt mentioned this pull request Aug 21, 2025
@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 Rollback support for Rollkit and EVM/ABCI

3 participants