Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use memstore instead of block wise transient store #117

Merged
merged 7 commits into from
Nov 19, 2024

Conversation

beer-1
Copy link
Collaborator

@beer-1 beer-1 commented Nov 19, 2024

Description

I'm not sure why we're continuing to encounter app hash crashes on the query node. To identify the root cause, this PR removes the use of block-wise transient storage.


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title, you can find examples of the prefixes below:
  • confirmed ! in the type prefix if API or client breaking change
  • targeted the correct branch
  • provided a link to the relevant issue or specification
  • reviewed "Files changed" and left comments if necessary
  • included the necessary unit and integration tests
  • updated the relevant documentation or specification, including comments for documenting Go code
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new memory-based storage implementation for improved state management.
    • Added a structured way to manage context with both SDK and memory store capabilities.
  • Improvements

    • Simplified test setups by removing transient store service initializations.
    • Enhanced verification of state integrity during snapshot and revert operations.
    • Updated contract binary representation for better deployment and interaction.
    • Modified various functions and methods to utilize a new context type.
  • Bug Fixes

    • Improved logging assertions in fuzz tests for better clarity and validation.
    • Adjusted the handling of ERC20 contract address resolution to ensure accurate functionality.
    • Refined auction-related rewards management within the application.

@beer-1 beer-1 self-assigned this Nov 19, 2024
@beer-1 beer-1 requested a review from a team as a code owner November 19, 2024 08:41
Copy link

coderabbitai bot commented Nov 19, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes in this pull request primarily focus on modifications to the handling of transient data within various components, particularly the evmKeeper. Key alterations include the removal of transient store service instantiations in test input functions and the NewAppKeeper method, alongside updates to context management in the StateDB and related structures. New methods and structures have been introduced to streamline context handling, while several existing fields related to transient storage have been eliminated. Overall, these changes refine the initialization and testing processes without altering core functionality.

Changes

File Path Change Summary
app/ibc-hooks/common_test.go Removed transient store service instantiation for evmtypes.TStoreKey in createTestInput function.
app/ibc-hooks/receive_test.go Updated import statement and function call to DenomToContractAddr using evmtypes.
app/keepers/keepers.go Removed transient store service instantiation for evmtypes.TStoreKey in NewAppKeeper. Introduced NewKeeperWithRewardsAddressProvider for AuctionKeeper.
app/keepers/keys.go Modified GenerateKeys to only include forwardingtypes.TransientStoreKey.
x/bank/keeper/common_test.go Removed transient store service initialization for evmKeeper in createTestInput.
x/evm/keeper/common_test.go Removed transient store service initialization for evmtypes.TStoreKey in _createTestInput.
x/evm/keeper/context.go Updated methods to use new context type; removed transient fields from NewStateDB.
x/evm/keeper/keeper.go Removed several transient storage fields from Keeper struct and updated NewKeeper constructor.
x/evm/precompiles/cosmos/common_test.go Updated MockStateDB to use state.Context instead of sdk.Context.
x/evm/precompiles/erc20_registry/common_test.go Updated MockStateDB to use state.Context instead of sdk.Context.
x/evm/state/common_test.go Removed transient store service initialization for evmtypes.TStoreKey in _createTestInput.
x/evm/state/context.go Introduced new Context struct with memory store capabilities.
x/evm/state/keys.go Added new variables for managing keys related to a transient store.
x/evm/state/snapshot.go Updated Snapshot struct to use new Context type.
x/evm/state/statedb.go Transitioned StateDB to use new Context type and replaced transient storage structures with memory store equivalents.
x/evm/types/keys.go Removed several transient store prefix constants.
x/evm/contracts/counter/Counter.go Updated binary representation of the contract and its metadata.
x/evm/contracts/counter/Counter.sol Changed visibility of increase_for_fuzz function from external to public and added base case for recursion.
x/evm/keeper/fuzz_test.go Removed unused imports and replaced bloom filter logic with a new helper function for log validation.
x/evm/state/memstore.go Introduced memory-based storage implementation with standard KVStore operations.
x/evm/state/statedb_test.go Enhanced Test_SnapshotRevert function with new assertions for state integrity during snapshot operations.

Possibly related PRs

Suggested reviewers

  • djm07073

🐰 In the land of code so bright,
Changes hop with pure delight.
Transient stores now take a rest,
Simplified tests, we do our best!
With contexts new and keys refined,
A smoother path for all aligned! 🌟

Warning

Rate limit exceeded

@beer-1 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 23 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between baabf3d and c72ab65.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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.

Copy link

@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: 6

🧹 Outside diff range and nitpick comments (11)
x/evm/state/context.go (2)

15-22: Consider adding error handling for memory store initialization

The newMemStore() call might fail (e.g., due to resource constraints). Consider propagating any potential errors to the caller for proper error handling.

-func NewContext(ctx sdk.Context) Context {
+func NewContext(ctx sdk.Context) (Context, error) {
-	memStore, memStoreKey := newMemStore()
+	memStore, memStoreKey, err := newMemStore()
+	if err != nil {
+		return Context{}, fmt.Errorf("failed to create memory store: %w", err)
+	}
	return Context{
		Context:     ctx,
		memStore:    memStore,
		memStoreKey: memStoreKey,
-	}
+	}, nil
}

34-45: Add documentation for CacheContext method

The implementation looks good, but please add documentation explaining:

  • The purpose of caching both SDK context and memory store
  • The behavior of the writeCache function
  • When changes are actually committed

Add documentation like:

// CacheContext creates a cached copy of the context and returns it along with a
// write function. The write function atomically commits all cached changes to both
// the SDK context and memory store when called. If the write function is not called,
// all cached changes are discarded.
func (c Context) CacheContext() (cc Context, writeCache func()) {
x/evm/state/keys.go (1)

41-42: Consider adding documentation about key space separation

The file manages two distinct key spaces (permanent and transient storage) with different prefix schemes. Consider adding a package-level comment to document this separation and the rationale.

Add this documentation at the package level:

package state

import (
	"encoding/binary"

	"github.com/ethereum/go-ethereum/common"
)

+// This package manages two distinct key spaces:
+// 1. Permanent storage: Uses string-based prefixes ("code", "codesize", "state")
+// 2. Memory/transient storage: Uses single-byte prefixes (0x01-0x07)
+// This separation ensures no key collisions between permanent and transient data.
+
var (
x/evm/types/keys.go (1)

Based on the search results, I can now provide a final response:

The TStoreKey constant can be safely removed

The search results confirm that:

  1. TStoreKey is only referenced in test files (common_test.go files)
  2. The actual transient store functionality has been replaced with memstore collections as seen in x/evm/state/statedb.go
  3. No production code is using the transient store key
🔗 Analysis chain

Line range hint 8-11: Consider removing unused TStoreKey constant

Since we're moving away from transient storage to memstore, the TStoreKey constant might no longer be needed.

Let's verify if this constant is still in use:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if TStoreKey is still being used

# Search for TStoreKey usage
echo "Checking TStoreKey usage..."
rg "TStoreKey" 

# Search for transient store initialization patterns
echo "Checking store initialization patterns..."
ast-grep --pattern 'NewTransient$_($_, $TStoreKey)'

Length of output: 3046


Script:

#!/bin/bash
# Let's search specifically for TStoreKey usage
echo "Checking TStoreKey usage..."
rg "TStoreKey"

# Also check for any transient store usage patterns
echo "Checking transient store usage..."
rg "TransientStore|GetTransient"

# Check if there are any other files importing the types package and using transient storage
echo "Checking imports of evm types..."
rg -p "\".*x/evm/types\"" -A 5

Length of output: 21032

x/evm/state/memdb.go (3)

14-16: Consider using a more specific constant name

The constant storeKey is quite generic. Consider using a more specific name like memStoreKey or evmStateKey to better indicate its purpose and avoid potential naming conflicts.

-	storeKey = "stateDB"
+	memStoreKey = "evmStateDB"

18-27: Consider enhancing error handling and configuration

While using panic for initialization errors is acceptable, there are several potential improvements:

  1. The error message could be more descriptive about what caused the failure
  2. Consider accepting configuration parameters (e.g., logger, metrics) instead of hardcoding them
-func newMemStore() (storetypes.MultiStore, storetypes.StoreKey) {
+func newMemStore(logger log.Logger, metrics storemetrics.Metrics) (storetypes.MultiStore, storetypes.StoreKey) {
 	memStoreKey := storetypes.NewMemoryStoreKey(storeKey)
-	memStore := store.NewCommitMultiStore(dbm.NewMemDB(), log.NewNopLogger(), storemetrics.NewNoOpMetrics())
+	memStore := store.NewCommitMultiStore(dbm.NewMemDB(), logger, metrics)
 	memStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
 	if err := memStore.LoadLatestVersion(); err != nil {
-		panic(fmt.Sprintf("failed to initialize memory store: %v", err))
+		panic(fmt.Sprintf("failed to initialize memory store: failed to load latest version: %v", err))
 	}

1-80: Consider performance and persistence implications

While using a memory store instead of a transient store might help diagnose app hash crashes, consider:

  1. Memory usage implications for large datasets
  2. Data persistence requirements across node restarts
  3. Impact on state sync and snapshots

Would you like assistance in implementing monitoring or metrics to track memory usage?

x/evm/precompiles/erc20_registry/common_test.go (1)

Line range hint 28-33: Add documentation for the context conversion.

The constructor correctly handles the conversion from sdk.Context to state.Context. Consider adding a comment explaining the conversion for better maintainability.

 func NewMockStateDB(sdkCtx sdk.Context) *MockStateDB {
+	// Convert SDK context to state.Context for EVM state management
 	ctx := state.NewContext(sdkCtx)
 	return &MockStateDB{
 		ctx:        ctx,
 		initialCtx: ctx,
 	}
 }
x/evm/state/statedb.go (3)

Line range hint 643-656: Error handling in Commit function may lead to data inconsistency

In the Commit function, errors returned from s.vmStore.Clear and s.accountKeeper.RemoveAccount are not handled explicitly within the Walk closure. Since the Walk function continues even if an error occurs, this may lead to incomplete cleanup of destructed accounts.

Modify the closure to handle errors properly and stop walking when an error occurs:

 err := s.memStoreSelfDestruct.Walk(s.ctx, nil, func(key []byte) (stop bool, err error) {
     addr := common.BytesToAddress(key)

     // If ether was sent to account post-selfdestruct it is burnt.
     if bal := s.GetBalance(addr); bal.Sign() != 0 {
         s.SubBalance(addr, bal, tracing.BalanceDecreaseSelfdestructBurn)
     }

-    err = s.vmStore.Clear(s.ctx, new(collections.Range[[]byte]).Prefix(addr.Bytes()))
+    if err := s.vmStore.Clear(s.ctx, new(collections.Range[[]byte]).Prefix(addr.Bytes())); err != nil {
+        return true, err
+    }

     // remove cosmos account
-    s.accountKeeper.RemoveAccount(s.ctx, s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(addr.Bytes())))
+    acc := s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(addr.Bytes()))
+    if acc != nil {
+        s.accountKeeper.RemoveAccount(s.ctx, acc)
+    }
+    return false, nil
 })
 if err != nil {
     return err
 }

This adjustment ensures that errors are not ignored and that the function exits gracefully if an error occurs during account cleanup.


Line range hint 217-223: Handling errors by panicking in AddAddressToAccessList

The function AddAddressToAccessList uses panic to handle errors from memStoreAccessList.Set. Reliance on panicking for error handling can lead to abrupt termination of the application, which is undesirable in production environments.

Refactor the function to return an error instead of panicking:

-func (s *StateDB) AddAddressToAccessList(addr common.Address) {
-    err := s.memStoreAccessList.Set(s.ctx, addr.Bytes())
-    if err != nil {
-        panic(err)
-    }
+func (s *StateDB) AddAddressToAccessList(addr common.Address) error {
+    return s.memStoreAccessList.Set(s.ctx, addr.Bytes())
 }

Update the function's callers to handle the error appropriately. This approach enhances the robustness of the application by allowing controlled error propagation and handling.


Based on the code analysis and EIP-6780 requirements, I can now provide the final response:

Incomplete implementation of EIP-6780: Missing balance transfer to beneficiary

The current implementation of Selfdestruct6780 only checks if the contract was created in the same transaction and calls SelfDestruct accordingly. However, it's missing critical requirements from EIP-6780:

  • When called in the same transaction as creation (current implementation):

    • Correctly deletes the account via SelfDestruct
    • But missing: Transfer of balance to beneficiary before deletion
  • When called in different transactions:

    • Correctly skips deletion
    • But missing: Required transfer of entire balance to beneficiary

The implementation needs to be updated to handle balance transfers in both cases, as per EIP-6780 specification.

🔗 Analysis chain

Line range hint 519-523: Incomplete implementation of EIP-6780 in Selfdestruct6780

The Selfdestruct6780 function only calls SelfDestruct if the account was created in the same transaction. However, according to EIP-6780, additional considerations might be required for self-destruction behavior.

Please verify that the implementation fully complies with EIP-6780 specifications.

Run the following script to check for compliance details:


Let me check the implementation details of Selfdestruct6780 and SelfDestruct to verify compliance with EIP-6780.


Let me check if there's any balance transfer to beneficiary implementation, as that's a key requirement of EIP-6780.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that `Selfdestruct6780` aligns with EIP-6780.

# Fetch EIP-6780 details
curl -s https://eips.ethereum.org/EIPS/eip-6780 | grep -i "selfdestruct"

# Analyze the implementation for completeness
# [Manual analysis required]

Length of output: 8155


Script:

#!/bin/bash
# Get implementation of Selfdestruct6780 and SelfDestruct
ast-grep --pattern 'func (s $_) Selfdestruct6780($_) {
  $$$
}'

echo "---"

ast-grep --pattern 'func (s $_) SelfDestruct($_) {
  $$$
}'

# Check for balance transfer implementation
rg -A 10 "func.*SelfDestruct"

Length of output: 6517


Script:

#!/bin/bash
# Search for balance transfer related functions and their usage
rg -A 5 "func.*(AddBalance|SubBalance|TransferBalance)" x/evm/state/

# Check if there's any beneficiary handling
rg -B 3 -A 5 "beneficiary" x/evm/

Length of output: 795

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d4298cf and 5154fe7.

📒 Files selected for processing (17)
  • app/ibc-hooks/common_test.go (0 hunks)
  • app/ibc-hooks/receive_test.go (1 hunks)
  • app/keepers/keepers.go (0 hunks)
  • app/keepers/keys.go (1 hunks)
  • x/bank/keeper/common_test.go (0 hunks)
  • x/evm/keeper/common_test.go (0 hunks)
  • x/evm/keeper/context.go (2 hunks)
  • x/evm/keeper/keeper.go (0 hunks)
  • x/evm/precompiles/cosmos/common_test.go (2 hunks)
  • x/evm/precompiles/erc20_registry/common_test.go (2 hunks)
  • x/evm/state/common_test.go (0 hunks)
  • x/evm/state/context.go (1 hunks)
  • x/evm/state/keys.go (1 hunks)
  • x/evm/state/memdb.go (1 hunks)
  • x/evm/state/snapshot.go (2 hunks)
  • x/evm/state/statedb.go (17 hunks)
  • x/evm/types/keys.go (1 hunks)
💤 Files with no reviewable changes (6)
  • app/ibc-hooks/common_test.go
  • app/keepers/keepers.go
  • x/bank/keeper/common_test.go
  • x/evm/keeper/common_test.go
  • x/evm/keeper/keeper.go
  • x/evm/state/common_test.go
🔇 Additional comments (24)
x/evm/state/snapshot.go (3)

Line range hint 8-13: Verify caching behavior and state isolation

The constructor now uses the custom Context's CacheContext() method. While the commit mechanism appears unchanged, we should ensure that:

  1. State changes remain isolated until explicitly committed
  2. The cache context provides the same atomicity guarantees as before
✅ Verification successful

Let's search for test cases and implementation patterns to verify the caching behavior and state isolation mechanisms.


Based on the search results, I can now generate the final response as we have sufficient evidence about the caching behavior:

Cache context implementation maintains proper state isolation

The implementation correctly preserves state isolation through the following mechanisms:

  • The CacheContext() method in x/evm/state/context.go properly wraps both the SDK context and memstore with cache layers
  • The commit function is correctly captured and only executed when explicitly called
  • Usage patterns across the codebase (in keepers, hooks, and tests) demonstrate consistent cache-then-commit patterns with proper error handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for tests verifying cache isolation
rg -A 5 'TestSnapshot.*Cache' --type go
rg -A 5 'TestContext.*Isolation' --type go

# Check for any existing issues with cache contexts
rg -l 'CacheContext.*[Bb]ug|[Ii]ssue|[Ff]ix' --type go

Length of output: 213


Script:

#!/bin/bash
# Search for snapshot tests and their implementation
rg -A 10 'func TestSnapshot' --type go

# Look for cache context usage patterns
rg -A 5 'CacheContext\(\)' --type go

# Check snapshot commit patterns
rg -A 5 'snapshot.*commit' --type go

# Look for any cache-related tests
rg -A 5 'func Test.*Cache' --type go

Length of output: 4629


21-21: Verify test coverage and mock usage

The Context() method's return type change affects mock testing scenarios. Ensure all test files have been updated to handle the new Context type correctly.

✅ Verification successful

The previous script had syntax errors. Let's try with a more comprehensive search to find any relevant usage patterns of the Context() method and related test files.


Based on the search results, I can now provide a final response:

Test coverage is properly maintained for Context() method

The codebase shows consistent handling of the Context() method change:

  • Mock implementations in test files (e.g., x/evm/precompiles/erc20_registry/common_test.go, x/evm/precompiles/cosmos/common_test.go) have been updated to use the new Context type
  • The change is properly reflected in the interface definition (x/evm/types/expected_keeper.go)
  • No stray references to the old context type usage were found in test files
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for test files using this method
rg -l 'Context\(\).*mock' --type go
rg -l 'snapshot\.Context\(\)' --type go

# Check for any remaining references to sdk.Context in tests
rg 'sdk\.Context.*snapshot' --type go

Length of output: 213


Script:

#!/bin/bash
# Look for test files using Context() method
rg "Context\(\)" --type go -l

# Look for test files specifically
fd ".*_test\.go$" -x rg "Context\(\)" {}

# Check for any references to sdk.Context
rg "sdk\.Context" --type go

# Look for mock usage
rg "mock.*Context" --type go

Length of output: 17736


4-4: Verify thread-safety and isolation guarantees of the new Context type

The change from sdk.Context to a custom Context type is significant. While this aligns with the goal of eliminating block-wise transient storage, we should ensure the new type maintains proper state isolation.

✅ Verification successful

Based on the search results, I can now provide a conclusive response about the Context type change:

Custom Context type maintains proper state isolation and thread safety

The custom Context type in x/evm/state/context.go is a wrapper around sdk.Context that adds EVM-specific functionality:

  • It embeds sdk.Context to inherit all its thread-safety properties
  • Adds memory store capabilities for EVM state management
  • Provides clean separation between SDK and EVM-specific context needs

The change is safe and appropriate for the following reasons:

  • Maintains compatibility with SDK context functionality
  • Adds necessary EVM-specific storage without compromising thread safety
  • Follows the pattern of extending rather than replacing core functionality
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Context implementation to verify its capabilities
ast-grep --pattern 'type Context struct {
  $$$
}'

# Look for any concurrent access patterns or locks
rg -l 'sync\.' --type go
rg -l 'mutex' --type go -i

Length of output: 213


Script:

#!/bin/bash
# Let's try a different approach to find the Context definition and usage

# Find the Context type definition
rg -A 5 "type Context" --type go

# Find files importing sdk.Context and our custom Context
rg "sdk\.Context|\"Context\"" --type go

# Look for any methods or functions using Context
ast-grep --pattern 'func $_($_Context$_) $_'

Length of output: 14675

x/evm/state/context.go (4)

8-13: LGTM! Well-structured Context definition

The Context struct is well-designed:

  • Properly embeds sdk.Context for standard context functionality
  • Adds memory store capabilities aligned with the PR objective of moving away from transient storage
  • Clear and consistent field naming

24-32: LGTM! Clean and idiomatic method implementations

The WithSDKContext and WithMemStore methods follow the builder pattern correctly and maintain immutability.


1-45: Verify integration with existing state management

The move from transient store to memory store is a significant architectural change. Please ensure:

  1. Memory consumption is monitored and managed appropriately
  2. State consistency is maintained across all EVM operations
  3. Performance impact is measured, especially for large state changes
#!/bin/bash
# Search for transient store usage that might need migration
rg -l "TransientStore|TStore" --type go

# Look for existing state management patterns
ast-grep --pattern 'type $_ struct {
  $$$
  store $_ $$$
  $$$
}'

15-17: Verify the implementation of newMemStore()

The code calls newMemStore() but this function is not visible in the current file or imports. Please ensure this function is properly implemented and accessible.

✅ Verification successful

Let me try a different search pattern to locate the newMemStore function.


newMemStore() is properly implemented in x/evm/state/memdb.go

The function is correctly implemented in a separate file within the same package. It creates and initializes a memory store with the necessary configuration, returning both the store and its key as expected by the NewContext constructor.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for newMemStore implementation
ast-grep --pattern 'func newMemStore() ('

Length of output: 297


Script:

#!/bin/bash
# Search for newMemStore implementation with rg
rg -A 5 'func newMemStore\(\)' --type go

# Also search in the same file for any memory store related code
rg -A 5 'memStore|MemStore' x/evm/state/context.go

Length of output: 1393

x/evm/state/keys.go (1)

43-49: LGTM! Space-efficient prefix design

The new memstore prefixes are well-designed using single bytes (0x01-0x07), which is space-efficient and ensures no collisions with existing string-based prefixes. Each prefix has a clear purpose documented in the comments.

x/evm/types/keys.go (2)

23-23: Verify the migration from transient store to memstore

The removal of transient store prefixes aligns with the PR objective to use memstore instead of block-wise transient store. However, we should verify that all transient store operations have been properly migrated.

Let's verify the complete removal of transient store operations:


23-23: Verify VM state persistence strategy

With the removal of transient prefixes and the retention of only VMStorePrefix, we need to ensure that the VM state management strategy is properly implemented.

Let's verify the VM state handling:

#!/bin/bash
# Description: Check VM state handling implementation

# Check for VMStorePrefix usage
echo "Checking VMStorePrefix usage..."
rg -A 5 "VMStorePrefix"

# Check for state store operations in keeper
echo "Checking state store operations..."
ast-grep --pattern 'func ($k Keeper) $method($ctx $_) {
  $store := $ctx.KVStore($k.storeKey)
  $$$
}'
x/evm/state/memdb.go (1)

63-71: Document iterator behavior and add validation

The iterator methods have important contracts about concurrent writes, but:

  1. The behavior when start > end is not handled
  2. The comment about cachekv.Store seems out of place in this context

Let's verify if this is the standard behavior in other stores:

Also applies to: 73-80

app/keepers/keys.go (2)

Line range hint 1-100: Consider cleaning up unused imports.

The evmtypes package is still imported but its TStoreKey is no longer used in this file. However, since this is part of a larger refactoring effort, let's verify if this import is still needed elsewhere in the file before removing it.

#!/bin/bash
# Check if evmtypes is used for anything else in this file
rg "evmtypes\." app/keepers/keys.go

60-60: LGTM! Verify transient store removal impact.

The removal of evmtypes.TStoreKey from transient store keys aligns with the PR's objective to move away from block-wise transient storage. This is a significant architectural change.

Let's verify that no code still expects EVM data in the transient store:

✅ Verification successful

The previous search might have been too specific. Let's broaden our search to:

  1. Find all transient store usage
  2. Look for store access patterns that might indicate EVM transient store dependencies

The search results show EVM transient store keys are still present in test files and type definitions. Let's verify if there are any actual usages in the keeper implementation and check recent changes to understand the new storage mechanism.


Transient store removal is properly implemented with memory store replacement

The verification confirms that the EVM transient store removal is safe and properly implemented:

  1. The transient store keys are completely removed from the EVM module
  2. A new memory store implementation replaces the transient store functionality:
    • All transient state is now managed via memStore in state/memdb.go
    • The memory store provides the same capabilities but without persistence
  3. The changes are comprehensive and consistent across the codebase:
    • Keeper initialization updated to remove transient store dependency
    • StateDB implementation modified to use memory store
    • Test files updated accordingly
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to EVM transient store usage
rg "TStoreKey|TransientStoreKey.*evm" -A 3

Length of output: 197


Script:

#!/bin/bash
# Let's search more broadly for any references to EVM transient store
rg "TransientStoreKey|TStoreKey" -A 3

# Also search for any EVM-related store access patterns
ast-grep --pattern 'store.Get($$$)'

Length of output: 3345


Script:

#!/bin/bash
# Let's check if there are any remaining EVM transient store usages in keeper implementations
rg "ctx.*Store.*TStore|TransientStore.*evm" -A 3

# Also check for any recent changes to EVM-related files that might indicate the new storage mechanism
git diff origin/main -- "x/evm/**"

Length of output: 31107

x/evm/precompiles/erc20_registry/common_test.go (2)

21-22: LGTM! Verify state.Context usage across the codebase.

The change from sdk.Context to state.Context is consistent with the PR's objective. Both context fields are properly updated.

#!/bin/bash
# Verify consistent usage of state.Context across the codebase
ast-grep --pattern 'type $_ struct {
  $$$
  ctx $ctx_type
  $$$
}'

71-74: LGTM! Verify StateDB interface compliance.

The context accessor methods correctly return the underlying sdk.Context from the state.Context wrapper, maintaining interface compatibility.

Also applies to: 79-79

x/evm/precompiles/cosmos/common_test.go (3)

Line range hint 34-39: LGTM! Clear and correct constructor implementation

The changes improve clarity by:

  1. Renaming the parameter to clearly distinguish between SDK and state contexts
  2. Properly initializing both context fields with the same state context

77-85: Consider using Context getter method if available

The implementation correctly returns the underlying SDK context, but directly accessing the Context field might bypass any necessary state management in the state.Context type. Consider using a getter method if one is available in the state.Context type.

#!/bin/bash
# Description: Check if state.Context has a getter method for the underlying SDK context

# Search for Context getter method in state.Context
ast-grep --pattern 'func ($receiver state.Context) $name() sdk.Context {
  $$$
}'

27-28: Verify context type consistency with production code

The change from sdk.Context to state.Context for both context fields aligns with the PR's objective. However, we should verify that this matches the production implementation.

✅ Verification successful

Based on the search results, I can now provide a definitive response:

Context type change is consistent with production code

The change from sdk.Context to state.Context in the test file aligns with the production implementation. The search results show that:

  1. The production StateDB struct in x/evm/state/statedb.go uses state.Context for both ctx and initialCtx fields
  2. The mock implementation in the test file correctly mirrors this structure
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the context type change is consistent across the codebase
# Look for any remaining sdk.Context usage in StateDB implementations

# Search for StateDB implementations
ast-grep --pattern 'type $name struct {
  $$$
  ctx $type
  $$$
}'

# Search for any remaining sdk.Context usage in state-related files
rg "sdk\.Context" "x/evm/state"

Length of output: 508


Script:

#!/bin/bash
# Let's try a different approach to verify the context type changes

# Search for StateDB struct definitions
ast-grep --pattern 'type StateDB struct {
  $$$
}'

# Search for context-related fields in state-related files
rg "ctx\s+\w+\.Context" "x/evm"

# Search for any remaining sdk.Context usage in EVM-related files
rg "sdk\.Context" "x/evm"

Length of output: 43162

app/ibc-hooks/receive_test.go (2)

Line range hint 1-324: Test coverage looks comprehensive

The test suite thoroughly covers:

  • ICS20 and ICS721 packet handling
  • Success and failure scenarios
  • ACL verification
  • Contract state validation
  • Token allowance checks

113-113: Verify the behavior of DenomToContractAddr remains consistent

The change to use evmtypes.DenomToContractAddr aligns with the PR's objective of moving away from block-wise transient store. Let's verify the implementation to ensure consistent behavior.

✅ Verification successful

Function behavior is consistent and properly implemented

The change to use evmtypes.DenomToContractAddr is correct and safe. The implementation in x/evm/types/denom.go shows that the function:

  • Handles EVM denom prefix correctly
  • Validates hex addresses
  • Falls back to keeper for non-EVM denoms
  • Maintains consistent behavior with proper error handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of DenomToContractAddr in evmtypes

# Check the implementation and its dependencies
ast-grep --pattern 'func DenomToContractAddr($ctx, $keeper, $denom) ($result, $err) {
  $$$
}'

# Look for any related tests
rg -l "DenomToContractAddr.*Test"

Length of output: 132


Script:

#!/bin/bash
# Let's try a broader search to locate the DenomToContractAddr function and its usage

# Search for DenomToContractAddr function definition
rg -A 5 "func.*DenomToContractAddr"

# Search for any imports of evmtypes package
rg "\".*evmtypes\""

# Search for any usage of DenomToContractAddr
rg "DenomToContractAddr"

Length of output: 2769


Script:

#!/bin/bash
# Let's examine the implementation details and any recent changes

# Check the full implementation of DenomToContractAddr
cat x/evm/types/denom.go

# Look for any recent changes to this file
git log -p x/evm/types/denom.go

Length of output: 3013

x/evm/keeper/context.go (4)

30-31: Confirm the use of an infinite gas meter in NewStateDB

Using storetypes.NewInfiniteGasMeter() in NewStateDB may lead to uncontrolled gas consumption. Ensure that this is intentional and that gas is properly accounted for elsewhere to prevent potential Denial-of-Service attacks.


159-159: Preparation of SDK context appears appropriate

The call to prepareSDKContext(sdk.UnwrapSDKContext(ctx)) correctly unwraps the context for SDK usage. This ensures that the EVM operates with the proper context settings.


178-184: Verify initialization of EVM with nil StateDB

Initializing the EVM with nil as the StateDB parameter may lead to nil pointer dereferences if accessed before proper assignment. Confirm that the EVM instance does not interact with the StateDB until it is set later in the code.


195-199: Setting precompiles aligns with updated context and StateDB

Assigning precompiles after initializing the EVM's context and StateDB ensures compatibility with the current configuration. This approach appears correct and consistent with the overall code structure.

x/evm/state/memdb.go Outdated Show resolved Hide resolved
x/evm/keeper/context.go Outdated Show resolved Hide resolved
x/evm/state/statedb.go Outdated Show resolved Hide resolved
x/evm/state/statedb.go Outdated Show resolved Hide resolved
x/evm/state/statedb.go Show resolved Hide resolved
x/evm/state/statedb.go Show resolved Hide resolved
Copy link

codecov bot commented Nov 19, 2024

Codecov Report

Attention: Patch coverage is 81.25000% with 24 lines in your changes missing coverage. Please review.

Project coverage is 40.70%. Comparing base (d4298cf) to head (baabf3d).

Files with missing lines Patch % Lines
x/evm/state/statedb.go 76.36% 7 Missing and 6 partials ⚠️
x/evm/state/memstore.go 69.56% 6 Missing and 1 partial ⚠️
x/evm/keeper/context.go 86.95% 2 Missing and 1 partial ⚠️
x/evm/state/snapshot.go 66.66% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #117      +/-   ##
==========================================
+ Coverage   40.56%   40.70%   +0.13%     
==========================================
  Files         106      108       +2     
  Lines        9923     9963      +40     
==========================================
+ Hits         4025     4055      +30     
- Misses       5322     5330       +8     
- Partials      576      578       +2     
Files with missing lines Coverage Δ
app/keepers/keepers.go 98.05% <ø> (-0.01%) ⬇️
app/keepers/keys.go 93.54% <100.00%> (ø)
x/evm/keeper/keeper.go 69.74% <ø> (-1.90%) ⬇️
x/evm/state/context.go 100.00% <100.00%> (ø)
x/evm/state/keys.go 100.00% <ø> (ø)
x/evm/state/snapshot.go 80.00% <66.66%> (-1.82%) ⬇️
x/evm/keeper/context.go 76.16% <86.95%> (-0.57%) ⬇️
x/evm/state/memstore.go 69.56% <69.56%> (ø)
x/evm/state/statedb.go 64.53% <76.36%> (+0.20%) ⬆️
---- 🚨 Try these New Features:

Copy link

@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: 3

🧹 Outside diff range and nitpick comments (4)
x/evm/keeper/fuzz_test.go (2)

83-83: Remove redundant error check

There's an unnecessary error check here with no preceding error-generating operation.

-		require.NoError(t, err)

74-87: Document assumptions about log ordering and data structure

The function makes several implicit assumptions:

  1. Logs are ordered by iteration index (used in line 84-85)
  2. Log data is exactly 64 bytes (checked in line 79)

Consider adding documentation to explain these requirements and their relationship to the contract's behavior.

+// assertLogs validates the event logs emitted by the counter contract.
+// It expects:
+// - Logs to be ordered by iteration index
+// - Each log to contain exactly 64 bytes of data:
+//   - First 32 bytes: counter value before increment
+//   - Last 32 bytes: counter value after increment
 func assertLogs(t *testing.T, contractAddr common.Address, logs []types.Log) {
x/evm/state/statedb_test.go (2)

91-98: LGTM! Consider adding intermediate snapshot assertions.

The additional assertions effectively verify state consistency across multiple snapshots. This is particularly valuable for ensuring the reliability of the memstore implementation.

Consider adding assertions immediately after the first Snapshot() call to verify the intermediate state, making the test progression more explicit:

 	stateDB.Snapshot()
 	stateDB.SubRefund(10)
+	// Verify intermediate snapshot state
+	require.Equal(t, uint64(90), stateDB.GetBalance(common.BytesToAddress(addr1)).Uint64())
+	require.Equal(t, uint64(80), stateDB.GetRefund())
 
 	require.Equal(t, uint64(90), stateDB.GetBalance(common.BytesToAddress(addr1)).Uint64())
 	require.Equal(t, uint64(80), stateDB.GetRefund())

Line range hint 1-489: Consider adding app hash consistency tests.

Given that this PR aims to address app hash crashes on the query node, it would be valuable to add specific test cases that verify app hash consistency across state operations, particularly during snapshot and revert operations.

Consider adding a new test function that:

  1. Performs a series of state modifications
  2. Takes snapshots at various points
  3. Verifies that the app hash remains consistent after reverting to different snapshots
  4. Ensures the app hash is correctly maintained when switching between transient and persistent storage

Example structure:

func Test_AppHashConsistency(t *testing.T) {
    sdkCtx, input := createDefaultTestInput(t)
    
    // Get initial app hash
    initialHash := input.EVMKeeper.GetAppHash(sdkCtx)
    
    // Perform state modifications and verify app hash
    _, evm, err := input.EVMKeeper.CreateEVM(sdkCtx, evmtypes.StdAddress, nil)
    require.NoError(t, err)
    stateDB := evm.StateDB.(*state.StateDB)
    
    // Take snapshot
    sid := stateDB.Snapshot()
    
    // Modify state
    // ...
    
    // Verify app hash after modifications
    modifiedHash := input.EVMKeeper.GetAppHash(sdkCtx)
    require.NotEqual(t, initialHash, modifiedHash)
    
    // Revert to snapshot
    stateDB.RevertToSnapshot(sid)
    
    // Verify app hash is restored
    finalHash := input.EVMKeeper.GetAppHash(sdkCtx)
    require.Equal(t, initialHash, finalHash)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5154fe7 and d17c737.

📒 Files selected for processing (5)
  • x/evm/contracts/counter/Counter.go (1 hunks)
  • x/evm/contracts/counter/Counter.sol (1 hunks)
  • x/evm/keeper/fuzz_test.go (1 hunks)
  • x/evm/state/memstore.go (1 hunks)
  • x/evm/state/statedb_test.go (1 hunks)
🔇 Additional comments (6)
x/evm/contracts/counter/Counter.sol (1)

17-23: Verify IBC testing coverage with the modified function

Since increase_for_fuzz is likely used in testing scenarios, ensure that the IBC callback functionality is still adequately tested with the new implementation, particularly for:

  • Gas consumption patterns in cross-chain operations
  • Interaction with IBC acknowledgments and timeouts
  • Edge cases in recursive scenarios
x/evm/keeper/fuzz_test.go (1)

62-62: Verify concurrent behavior without transient store

The replacement of bloom filter with assertLogs aligns with the PR's objective. However, we should verify that removing the transient store doesn't impact concurrent operations.

✅ Verification successful

Let me gather more information about the concurrent test implementation to verify the impact.


Let me check the concurrent test implementation more thoroughly using ast-grep.


Based on the complete test implementation, I can now generate the final response:

Concurrent test implementation is correct and safe

The concurrent test implementation is properly designed and the removal of transient store/bloom filter doesn't impact its correctness because:

  • Each goroutine uses its own cached context via CacheContext(), ensuring isolation
  • The test properly validates logs through assertLogs which checks contract events
  • The final state verification is done separately for each cached context
  • The synchronization using WaitGroup ensures all operations complete before verification
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other concurrent tests that might be affected by transient store removal

# Search for concurrent test patterns
echo "Searching for other concurrent tests that might use transient store..."
rg -l "sync\.(WaitGroup|Mutex)" --type go

# Search for transient store usage in test files
echo "Checking for remaining transient store usage in tests..."
rg "TStoreKey|TransientStore" --type go

Length of output: 1830


Script:

#!/bin/bash
# Check the implementation of concurrent test and assertLogs
echo "Checking concurrent test implementation..."
rg -A 10 "func TestConcurrentCounter" x/evm/keeper/fuzz_test.go

echo "Checking assertLogs implementation..."
rg -A 5 "func assertLogs" x/evm/keeper/fuzz_test.go

echo "Checking if there are any remaining bloom filter references..."
rg "bloom|Bloom" x/evm/keeper/

Length of output: 726


Script:

#!/bin/bash
# Get the full implementation of TestConcurrentCounter using ast-grep
ast-grep --pattern 'func TestConcurrentCounter($$$) {
  $$$
}'

# Get the full context of the test file
echo "Getting full test file content..."
cat x/evm/keeper/fuzz_test.go

Length of output: 3101

x/evm/contracts/counter/Counter.go (3)

Line range hint 1019-1067: LGTM: Event handling implementation is robust

The implementation of event handlers (CallbackReceived, Increased, RecursiveCalled) demonstrates good practices:

  • Proper error handling
  • Resource cleanup through subscription unsubscription
  • Thread-safe event forwarding

Also applies to: 1068-1116, 1117-1165


35-35: Verify the updated contract binary

The contract binary has been updated while maintaining the same ABI. This suggests internal implementation changes, which aligns with the PR's objective of switching from block-wise transient store to memstore.


Line range hint 519-519: Review recursive implementation for potential issues

The recursive method implementation could lead to stack overflow or excessive gas consumption if not properly bounded. Consider adding appropriate guards and documentation about the maximum allowed recursion depth.

x/evm/state/memstore.go (1)

69-71: Verify: Ensure iterator methods enforce start < end as per contract

The comments for Iterator and ReverseIterator specify that "Start must be less than end, or the Iterator is invalid." The current implementations delegate directly to store.kvStore.Iterator without explicitly checking this condition.

Run the following script to verify if the underlying kvStore.Iterator methods enforce the start < end constraint:

If the underlying kvStore does not enforce this constraint, consider adding checks in your Iterator and ReverseIterator methods:

 func (store coreKVStore) Iterator(start, end []byte) (store.Iterator, error) {
+    if bytes.Compare(start, end) >= 0 {
+        return nil, fmt.Errorf("invalid iterator range: start must be less than end")
+    }
     return store.kvStore.Iterator(start, end), nil
 }

Also applies to: 78-80

x/evm/contracts/counter/Counter.sol Show resolved Hide resolved
x/evm/state/memstore.go Show resolved Hide resolved
x/evm/state/memstore.go Show resolved Hide resolved
Copy link

@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

📥 Commits

Reviewing files that changed from the base of the PR and between d17c737 and 040a4f1.

📒 Files selected for processing (1)
  • x/evm/keeper/context.go (2 hunks)
🔇 Additional comments (4)
x/evm/keeper/context.go (4)

190-193: ⚠️ Potential issue

Handle potential nil StateDB

As noted in a previous review, proper error handling after NewStateDB is crucial. The current implementation should be enhanced to handle nil StateDB cases.

Apply this diff as suggested in the previous review:

 evm.StateDB, err = k.NewStateDB(ctx, evm, fee)
 if err != nil {
     return ctx, nil, err
 }
+if evm.StateDB == nil {
+    return ctx, nil, fmt.Errorf("StateDB is nil after initialization")
+}

167-168: ⚠️ Potential issue

Reconsider the dummy block context pattern

Using a dummy block context for chain rules initialization is a risky pattern. This could lead to inconsistencies if the chain rules depend on block context state that's not properly initialized.

Consider refactoring to avoid the dummy context:

-// use dummy block context for chain rules in EVM creation
-dummyBlockContext, err := k.buildBlockContext(ctx, nil, fee)
+// build actual block context with proper initialization
+blockContext, err := k.buildBlockContext(ctx, nil, fee)

Likely invalid or redundant comment.


30-31: Verify gas delegation implementation

The use of InfiniteGasMeter is acceptable since gas tracking is delegated to the EVM, preventing double gas charging. However, let's verify the gas consumption is properly tracked in the EVM calls.

✅ Verification successful

Gas consumption is properly tracked and delegated

The use of InfiniteGasMeter in NewStateDB is safe as the code shows proper gas tracking:

  • EVM gas consumption is correctly tracked and charged in the SDK context after EVM execution (3 instances in keeper/context.go)
  • Precompiles properly track and charge gas for their operations
  • No double charging occurs since the infinite gas meter is only used during EVM execution, while actual consumption is tracked and charged afterward
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify gas consumption tracking in EVM calls
# Look for gas consumption patterns in the codebase

# Check for gas meter usage in EVM calls
rg -A 5 "GasMeter\(\).ConsumeGas" 

# Check for potential gas-related issues
ast-grep --pattern 'sdkCtx.GasMeter().ConsumeGas($_, $_)'

Length of output: 8676


177-185: ⚠️ Potential issue

Address the uncertainty in EVM initialization

The comment indicates uncertainty about correct EVM initialization with empty context and stateDB. This is a critical concern that needs to be addressed before merging.

x/evm/keeper/context.go Show resolved Hide resolved
Copy link

@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

🧹 Outside diff range and nitpick comments (5)
x/evm/state/statedb.go (5)

Line range hint 187-209: Improve error handling consistency in refund operations.

The AddRefund and SubRefund methods have inconsistent error handling - some errors trigger panic while others are properly handled.

Consider standardizing error handling across these methods:

 func (s *StateDB) AddRefund(gas uint64) {
     refund, err := s.memStoreRefund.Get(s.ctx)
-    if err != nil {
-        panic(err)
-    }
+    if err != nil {
+        return fmt.Errorf("failed to get refund: %w", err)
+    }
     err = s.memStoreRefund.Set(s.ctx, refund+gas)
-    if err != nil {
-        panic(err)
-    }
+    if err != nil {
+        return fmt.Errorf("failed to set refund: %w", err)
+    }
+    return nil
 }

Line range hint 534-554: Optimize transient state operations.

The current implementation of SetTransientState and GetTransientState creates a new byte slice on every operation by concatenating address and key.

Consider using a more efficient key construction:

+func transientKey(addr common.Address, key common.Hash) []byte {
+    b := make([]byte, common.AddressLength+common.HashLength)
+    copy(b[:common.AddressLength], addr.Bytes())
+    copy(b[common.AddressLength:], key.Bytes())
+    return b
+}

 func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) {
     prev := s.GetTransientState(addr, key)
     if prev == value {
         return
     }
-    if err := s.memStoreVMStore.Set(s.ctx, append(addr.Bytes(), key.Bytes()...), value[:]); err != nil {
+    if err := s.memStoreVMStore.Set(s.ctx, transientKey(addr, key), value[:]); err != nil {
         panic(err)
     }
 }

Line range hint 690-704: Fix potential index out of bounds in Logs method.

The current implementation assumes that log indices are sequential and within bounds, which might not be true if logs were added out of order or if some indices are missing.

Consider using a more robust approach:

 func (s *StateDB) Logs() evmtypes.Logs {
     logSize, err := s.memStoreLogSize.Get(s.ctx)
     if err != nil {
         panic(err)
     } else if logSize == 0 {
         return []evmtypes.Log{}
     }
 
-    logs := make([]evmtypes.Log, logSize)
+    logs := make([]evmtypes.Log, 0, logSize)
     err = s.memStoreLogs.Walk(s.ctx, nil, func(key uint64, log evmtypes.Log) (stop bool, err error) {
-        logs[key] = log
+        logs = append(logs, log)
         return false, nil
     })
     if err != nil {
         panic(err)
     }
     return logs
 }

Line range hint 642-662: Improve error handling in Commit method.

The Commit method could potentially leave the state in an inconsistent state if an error occurs mid-operation.

Consider implementing atomic commits:

 func (s *StateDB) Commit() error {
+    // Collect all operations first
+    var ops []func() error
     err := s.memStoreSelfDestruct.Walk(s.ctx, nil, func(key []byte) (stop bool, err error) {
         addr := common.BytesToAddress(key)
+        ops = append(ops, func() error {
             if bal := s.GetBalance(addr); bal.Sign() != 0 {
                 s.SubBalance(addr, bal, tracing.BalanceDecreaseSelfdestructBurn)
             }
-            err = s.vmStore.Clear(s.ctx, new(collections.Range[[]byte]).Prefix(addr.Bytes()))
+            if err := s.vmStore.Clear(s.ctx, new(collections.Range[[]byte]).Prefix(addr.Bytes())); err != nil {
+                return fmt.Errorf("failed to clear storage: %w", err)
+            }
             s.accountKeeper.RemoveAccount(s.ctx, s.accountKeeper.GetAccount(s.ctx, sdk.AccAddress(addr.Bytes())))
-            return false, err
+            return nil
+        })
+        return false, nil
     })
     if err != nil {
         return err
     }
 
+    // Execute all operations
+    for _, op := range ops {
+        if err := op(); err != nil {
+            return err
+        }
+    }
+
     // commit all changes
     for i := range s.snaps {
         s.snaps[len(s.snaps)-i-1].Commit()
     }
     
     s.ctx = s.initialCtx
     return nil
 }

Line range hint 271-302: Consider adding event attributes for contract creation failure.

The CreateContract method emits an event on success but doesn't emit any event on failure.

Consider adding failure events for better observability:

 func (s *StateDB) CreateContract(contractAddr common.Address) {
+    defer func() {
+        if r := recover(); r != nil {
+            s.ctx.EventManager().EmitEvent(sdk.NewEvent(
+                evmtypes.EventTypeContractCreationFailed,
+                sdk.NewAttribute(evmtypes.AttributeKeyContract, contractAddr.Hex()),
+                sdk.NewAttribute(evmtypes.AttributeKeyError, fmt.Sprint(r)),
+            ))
+            panic(r)
+        }
+    }()
     // ... rest of the function
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 040a4f1 and baabf3d.

📒 Files selected for processing (1)
  • x/evm/state/statedb.go (16 hunks)
🔇 Additional comments (1)
x/evm/state/statedb.go (1)

592-595: ⚠️ Potential issue

Add bounds check in ContextOfSnapshot.

The method doesn't validate the snapshot index, which could lead to a panic if an invalid index is provided.

Add boundary validation:

 func (s *StateDB) ContextOfSnapshot(i int) sdk.Context {
     if i == -1 {
         return s.initialCtx.Context
     }
+    if i < 0 || i >= len(s.snaps) {
+        panic(fmt.Sprintf("snapshot index out of range: %d", i))
+    }
     return s.snaps[i].ctx.Context
 }

Likely invalid or redundant comment.

x/evm/state/statedb.go Outdated Show resolved Hide resolved
Copy link
Contributor

@djm07073 djm07073 left a comment

Choose a reason for hiding this comment

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

LGTM

@beer-1 beer-1 merged commit 101f470 into main Nov 19, 2024
1 of 2 checks passed
@beer-1 beer-1 deleted the feat/use-memstore branch November 19, 2024 10:44
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