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

[OTE-882] Add prefix to accountplus keeper #2526

Merged
merged 5 commits into from
Oct 28, 2024

Conversation

jerryfan01234
Copy link
Contributor

@jerryfan01234 jerryfan01234 commented Oct 21, 2024

Changelist

[Describe or list the changes made in this PR]

Test Plan

[Describe how this PR was tested (if applicable)]

Author/Reviewer Checklist

  • If this PR has changes that result in a different app state given the same prior state and transaction list, manually add the state-breaking label.
  • If the PR has breaking postgres changes to the indexer add the indexer-postgres-breaking label.
  • If this PR isn't state-breaking but has changes that modify behavior in PrepareProposal or ProcessProposal, manually add the label proposal-breaking.
  • If this PR is one of many that implement a specific feature, manually label them all feature:[feature-name].
  • If you wish to for mergify-bot to automatically create a PR to backport your change to a release branch, manually add the label backport/[branch-name].
  • Manually add any of the following labels: refactor, chore, bug.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a migration process for account states to transition to a prefixed key structure.
    • Added a new constant for account state key prefixes to enhance data organization.
  • Bug Fixes

    • Improved error handling and data retrieval methods for account states, ensuring consistency and reliability.
  • Tests

    • Added comprehensive tests for the migration process and account state management, ensuring functionality and correctness.

@jerryfan01234 jerryfan01234 requested a review from a team as a code owner October 21, 2024 21:54
Copy link

linear bot commented Oct 21, 2024

Copy link
Contributor

coderabbitai bot commented Oct 21, 2024

Walkthrough

The changes introduce a migration function for the accountplus module's AccountState, transitioning from non-prefixed to prefixed keys in a key-value store. The updates include new methods in the keeper to facilitate access to store keys and enhance data retrieval. A new test suite for verifying the migration process is also added, alongside modifications to existing functions and tests to ensure consistency and correctness in account state management.

Changes

File Path Change Summary
protocol/app/upgrades/v8.0/upgrade.go Added migration function migrateAccountplusAccountState for transitioning AccountState to prefixed keys.
protocol/x/accountplus/keeper/keeper.go Added methods GetStoreKey() and GetCdc(), refactored GetAllAccountStates, updated GetAccountState and SetAccountState to use prefix store, renamed GetAccountPlusStateWithTimestampNonceDetails to AccountStateFromTimestampNonceDetails.
protocol/x/accountplus/keeper/keeper_test.go Introduced TestKeeper_Set_Get_GetAllAccountState to test setting, getting, and retrieving account states.
protocol/x/accountplus/keeper/timestampnonce.go Updated ProcessTimestampNonce to use renamed function, refactored EjectStaleTimestampNonces for clarity, simplified isLargerThanSmallestValue.
protocol/x/accountplus/types/keys.go Added constant AccountStateKeyPrefix for account state keys.
protocol/app/upgrades/v8.0/migrate_accountplus_test.go Introduced test suite UpgradeTestSuite to test migration of account states, including setup and validation methods.

Possibly related PRs

Suggested labels

proto, feature:timestamp_nonce

Suggested reviewers

  • teddyding

🐰 In the meadow, we hop and play,
With account states, we pave the way.
From old to new, we make the shift,
With prefixes now, our data's a gift!
Migration's done, let’s cheer and sing,
For every change, a joyful spring! 🌼


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8d8231a and 3d9b80a.

📒 Files selected for processing (2)
  • protocol/app/upgrades/v8.0/migrate_accountplus_test.go (1 hunks)
  • protocol/app/upgrades/v8.0/upgrade.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • protocol/app/upgrades/v8.0/migrate_accountplus_test.go
  • protocol/app/upgrades/v8.0/upgrade.go

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
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: 5

🧹 Outside diff range and nitpick comments (6)
protocol/x/accountplus/types/keys.go (1)

20-23: LGTM! Consider enhancing the comment for clarity.

The addition of AccountStateKeyPrefix aligns well with the PR objective and follows the existing naming conventions. Good job on organizing it in a separate const block for clarity.

Consider expanding the comment slightly for better context:

-// Prefix for account state.
+// Prefix for account state keys in the KV store.
protocol/app/upgrades/v8.0/upgrade_test.go (2)

13-27: LGTM: Test suite structure is well-organized.

The UpgradeTestSuite and its setup follow best practices for Go test suites. The SetupTest method properly initializes the test environment.

Consider adding a TearDownTest method to clean up resources after each test, even if it's not strictly necessary in this case. This promotes good testing practices:

func (s *UpgradeTestSuite) TearDownTest() {
    // Clean up any resources if necessary
}

29-71: LGTM: The test method comprehensively covers the migration process.

The TestUpgrade_MigrateAccountplusAccountState method effectively tests the migration of account states from unprefixed to prefixed storage. It creates test data, performs the migration, and verifies the results using appropriate assertions.

Consider the following improvements to enhance the test's robustness:

  1. Use a helper function to generate test data with random addresses and nonces:
func generateTestAccountStates(count int) []accountplustypes.AccountState {
    states := make([]accountplustypes.AccountState, count)
    for i := 0; i < count; i++ {
        states[i] = accountplustypes.AccountState{
            Address: sdk.AccAddress(testutil.RandomAddress()).String(),
            TimestampNonceDetails: accountplustypes.TimestampNonceDetails{
                TimestampNonces: []uint64{uint64(rand.Intn(1000)), uint64(rand.Intn(1000)), uint64(rand.Intn(1000))},
                MaxEjectedNonce: uint64(rand.Intn(1000)),
            },
        }
    }
    return states
}
  1. Add error checking when marshaling account states:
-		bz := s.tApp.App.AccountPlusKeeper.GetCdc().MustMarshal(&accountState)
+		bz, err := s.tApp.App.AccountPlusKeeper.GetCdc().Marshal(&accountState)
+		s.Require().NoError(err, "Failed to marshal account state")
  1. Test with a larger number of account states to ensure scalability:
testAccountStates := generateTestAccountStates(100)

These changes will make the test more robust and closer to real-world scenarios.

protocol/x/accountplus/keeper/timestampnonce.go (1)

119-119: LGTM: Simplified return statement in isLargerThanSmallestValue

The simplification of the return statement in isLargerThanSmallestValue is a good improvement. It reduces code complexity while maintaining the same logic.

For even better readability, consider using a named return value:

func isLargerThanSmallestValue(value uint64, values []uint64) (isLarger bool, minIndex int) {
    // ... existing code ...
    return value > values[minIndex], minIndex
}

This makes the purpose of the return values clearer at both the function signature and the return statement.

protocol/app/upgrades/v8.0/upgrade.go (1)

47-47: Resolve TODO comments before merging

The file contains a TODO comment referencing upgrade scaffolding. Please address this task or create a tracking issue before merging to maintain code cleanliness.

protocol/x/accountplus/keeper/keeper.go (1)

68-69: Include the key in the error message for better debugging

Adding the key that caused the unmarshalling error can aid in diagnosing issues more effectively.

Apply this diff to include the key in the error message:

if err != nil {
-    return nil, fmt.Errorf("failed to unmarshal account state: %w", err)
+    return nil, fmt.Errorf("failed to unmarshal account state for key %s: %w", string(iterator.Key()), err)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0eff57c and 39f921e.

📒 Files selected for processing (6)
  • protocol/app/upgrades/v8.0/upgrade.go (1 hunks)
  • protocol/app/upgrades/v8.0/upgrade_test.go (1 hunks)
  • protocol/x/accountplus/keeper/keeper.go (6 hunks)
  • protocol/x/accountplus/keeper/keeper_test.go (1 hunks)
  • protocol/x/accountplus/keeper/timestampnonce.go (3 hunks)
  • protocol/x/accountplus/types/keys.go (1 hunks)
🧰 Additional context used
🔇 Additional comments (12)
protocol/x/accountplus/types/keys.go (1)

22-22: Verify usage of the new constant across the module.

The AccountStateKeyPrefix constant has been added correctly, but it's not used within this file. To ensure it's properly integrated:

Run the following script to check its usage across the module:

✅ Verification successful

Usage of AccountStateKeyPrefix has been verified across the module and is properly integrated.

  • Found usage in keeper/keeper.go files ensuring effective integration with state management.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of AccountStateKeyPrefix across the module

# Test: Search for AccountStateKeyPrefix usage
rg -A 5 'AccountStateKeyPrefix' protocol/x/accountplus

Length of output: 1788

protocol/app/upgrades/v8.0/upgrade_test.go (1)

1-11: LGTM: Package declaration and imports are appropriate.

The package name v_8_0 follows the version naming convention, and all imports are relevant to the test suite's functionality.

protocol/x/accountplus/keeper/timestampnonce.go (3)

44-44: LGTM: Function name update improves clarity

The change from GetAccountPlusStateWithTimestampNonceDetails to AccountStateFromTimestampNonceDetails is a good improvement. The new function name more accurately describes its purpose, which is to create an account state from timestamp nonce details rather than retrieving it.


59-62: LGTM: Improved efficiency in EjectStaleTimestampNonces

The refactoring in the EjectStaleTimestampNonces function is a good improvement:

  1. Calculating oldestAllowedTs once at the beginning of the function avoids repeated calculations in the loop.
  2. The simplified condition in the for loop using oldestAllowedTs improves readability.

These changes maintain the same logic while potentially improving performance and making the code easier to understand.


Line range hint 1-121: Overall assessment: Positive improvements to account state management

The changes in this file are part of a broader restructuring of account state management in the accountplus module. The modifications enhance code clarity, efficiency, and maintainability while maintaining the core functionality of timestamp nonce processing.

Key improvements:

  1. Better function naming in ProcessTimestampNonce.
  2. Improved efficiency in EjectStaleTimestampNonces.
  3. Simplified logic in isLargerThanSmallestValue.

These changes align well with the related modifications in keeper.go and keys.go, contributing to a more robust and efficient account state management system.

protocol/app/upgrades/v8.0/upgrade.go (2)

41-41: Ensure correct deletion of old unprefixed keys

When deleting the old keys, confirm that you are deleting the correct unprefixed keys. Since the migration iterates over all keys, it's important to ensure that only the intended AccountState keys are deleted to prevent data loss.


38-38: Confirm that SetAccountState correctly handles key prefixing

The function k.SetAccountState(ctx, key, accountState) is called with the original unprefixed key. Verify that SetAccountState correctly adds the new prefix to the key internally. If it does not, you may need to modify the key before passing it to ensure the AccountState is stored under the new prefixed key.

Run the following script to verify how SetAccountState handles key prefixing:

This script searches for the SetAccountState method to confirm whether it prefixes the key with AccountStateKeyPrefix.

protocol/x/accountplus/keeper/keeper.go (5)

8-8: Import statement is appropriate

The addition of cosmossdk.io/store/prefix is necessary for utilizing prefix stores in the subsequent code changes.


56-69: Refactoring to use prefix store enhances efficiency

The changes in GetAllAccountStates to use prefix.NewStore with types.AccountStateKeyPrefix improve the specificity and efficiency of data retrieval from the store. The implementation correctly iterates over the prefixed keys and unmarshals the AccountState objects.


168-169: Consistent use of prefix store in data access functions

Updating GetAccountState and SetAccountState to use prefix.NewStore with types.AccountStateKeyPrefix ensures consistency in data access patterns across the Keeper. This enhances maintainability and aligns with the refactoring done in GetAllAccountStates.

Also applies to: 191-193


Line range hint 150-155: Ensure all references to the renamed function are updated

The function GetAccountPlusStateWithTimestampNonceDetails has been renamed to AccountStateFromTimestampNonceDetails. Please verify that all occurrences of the old function name have been updated throughout the codebase to prevent any potential issues.

Run the following script to find any usages of the old function name:

#!/bin/bash
# Description: Search for any remaining usages of the old function name.

rg 'GetAccountPlusStateWithTimestampNonceDetails'

40-47: Verify the necessity and security of exposing internal fields

The methods GetStoreKey() and GetCdc() expose the internal fields storeKey and cdc of the Keeper struct. Ensure that exposing these fields is required and does not introduce any security concerns.

Run the following script to verify the usage of these methods:

✅ Verification successful

Verified: The methods GetStoreKey() and GetCdc() are used internally within the codebase as required and do not introduce any security concerns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of 'GetStoreKey' and 'GetCdc' methods to ensure they are necessary.

rg 'GetStoreKey\('
rg 'GetCdc\('

Length of output: 143


Script:

#!/bin/bash
# Description: Find all usages of 'GetStoreKey' and 'GetCdc' methods to ensure they are necessary.

rg 'GetStoreKey\('
rg 'GetCdc\('

Length of output: 622

protocol/app/upgrades/v8.0/upgrade_test.go Outdated Show resolved Hide resolved
protocol/x/accountplus/keeper/keeper_test.go Outdated Show resolved Hide resolved
protocol/app/upgrades/v8.0/upgrade.go Show resolved Hide resolved
protocol/app/upgrades/v8.0/upgrade.go Show resolved Hide resolved
protocol/app/upgrades/v8.0/upgrade.go Show resolved Hide resolved
@@ -144,7 +147,7 @@ func (k Keeper) GetAllAuthenticatorData(ctx sdk.Context) ([]types.AuthenticatorD
return accountAuthenticators, nil
}

func GetAccountPlusStateWithTimestampNonceDetails(
func AccountStateFromTimestampNonceDetails(
Copy link
Contributor

@teddyding teddyding Oct 23, 2024

Choose a reason for hiding this comment

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

Nit: this is only used for newly initialized account state

Suggested change
func AccountStateFromTimestampNonceDetails(
func NewAccountStateFromTimestampNonceDetails(

@@ -46,26 +53,22 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
func (k Keeper) InitializeForGenesis(ctx sdk.Context) {
}

// Get all account details pairs in store
// Get all AccountStates from kvstore
func (k Keeper) GetAllAccountStates(ctx sdk.Context) ([]types.AccountState, error) {
store := ctx.KVStore(k.storeKey)
Copy link
Contributor

Choose a reason for hiding this comment

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

Simplify next 3 lines by:

iterator := storetypes.KVStorePrefixIterator(ctx.KVStore(k.storeKey), []byte(types.AccountStateKeyPrefix))

// GetAllAccountStates
accountStates, err := s.tApp.App.AccountPlusKeeper.GetAllAccountStates(ctx)
s.Require().NoError(err, "Should not error when getting all account states")
s.Require().Equal(len(accountStates), 1, "Incorrect number of AccountStates retrieved")
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and below let's also compare returned object(s)

Ctx sdk.Context
}

func TestUpgradeTestSuite(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For my understanding did you write these tests based on some existing template? Have we considered using the conventional upgrade containter test (example), which is more end-to-end?

The current test more like a unit test on migrateAccountplusAccountState - no upgrade is actually being run and we are just calling migrateAccountplusAccountState with whitebox set-up and postcheck. Probably more accurate to rename this test to TestMigrateAccountplusAccountState.

Copy link
Contributor Author

@jerryfan01234 jerryfan01234 Oct 23, 2024

Choose a reason for hiding this comment

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

I spoke with Jay on this and we thought unit test should be sufficient for this. I looked at the container e2e tests which is the convention, but I don't think accountplus can be tested in that manner right now because query has not been implemented.

s.Require().Nil(bzOld, "Unprefixed AccountState should be deleted for %s", addr)

// Check that the new prefixed key exists
bzNew := prefixStore.Get(accAddress)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be relatively easy to not only check the node keys but also the content

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

🧹 Outside diff range and nitpick comments (4)
protocol/app/upgrades/v8.0/migrate_accountplus_test.go (3)

9-10: Consolidate duplicate imports for accountplus types

There are two imports for the same package with different aliases. Consider using a single import:

-	"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
-	accountplustypes "github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
+	"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"

And update all references to use the same package alias throughout the code.


25-28: Consider adding TearDown method

While the setup is correct, consider adding a TearDown method to clean up resources after tests:

func (s *UpgradeTestSuite) TearDownTest() {
    // Clean up any resources if necessary
    s.tApp = nil
}

35-48: Consider using more realistic test data

The test uses simple address strings. Consider using actual bech32 addresses to better reflect production scenarios:

-	addresses := []string{"address1", "address2", "address3"}
+	addresses := []string{
+		"dydx1qqgzqvzq2ps8pqys5zcvp58q7rqrz9c6h2v",
+		"dydx1qqqfzqvzq2ps8pqys5zcvp58q7rqrz9c6h3",
+		"dydx1qqpfzqvzq2ps8pqys5zcvp58q7rqrz9c6h4",
+	}
protocol/x/accountplus/keeper/keeper.go (1)

56-74: LGTM: Improved efficiency with prefix iterator.

The refactor to use KVStorePrefixIterator is a good improvement. The error handling is also more robust with specific error messages.

Consider wrapping the iterator error in the returned error for better debugging:

 if err != nil {
-    return nil, fmt.Errorf("failed to unmarshal account state: %w", err)
+    return nil, fmt.Errorf("failed to unmarshal account state at key %X: %w", iterator.Key(), err)
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 39f921e and b2370d2.

📒 Files selected for processing (3)
  • protocol/app/upgrades/v8.0/migrate_accountplus_test.go (1 hunks)
  • protocol/x/accountplus/keeper/keeper.go (6 hunks)
  • protocol/x/accountplus/keeper/keeper_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • protocol/x/accountplus/keeper/keeper_test.go
🧰 Additional context used
🔇 Additional comments (4)
protocol/app/upgrades/v8.0/migrate_accountplus_test.go (1)

14-19: LGTM! Well-structured test suite

The test suite structure follows testing best practices with proper embedding of suite.Suite and necessary test dependencies.

protocol/x/accountplus/keeper/keeper.go (3)

8-8: LGTM: Clean implementation of prefix store support and getter methods.

The addition of the prefix store import and the implementation of getter methods align well with the PR objective of adding prefix support to the accountplus keeper. The methods follow Go conventions and provide controlled access to private fields.

Also applies to: 40-46


Line range hint 150-161: Skip comment: Function rename was already reviewed.


168-169: LGTM: Consistent implementation of prefix store.

The update to use prefix store in both methods is well-implemented and consistent with the module's new design.

Let's verify that all account state accesses use the new prefix:

Also applies to: 191-193

✅ Verification successful

Let me search for any other potential access patterns to ensure completeness of the verification.

This will help us verify that:

  1. There are no other store operations involving AccountState that might bypass the prefix store
  2. The AccountStateKeyPrefix is consistently used across the codebase

Based on the search results, I can now generate the final response as we have comprehensive evidence about the AccountState storage access patterns.

Verified: Account state prefix store implementation is correct and consistent

The verification confirms that:

  • All account state access in keeper.go properly uses the prefix store with AccountStateKeyPrefix
  • The only other usages of AccountStateKeyPrefix are in:
    • Key definition (types/keys.go)
    • Migration tests and upgrade handlers that explicitly check for proper prefix usage
  • No direct KVStore access bypassing the prefix store was found for account states
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any direct store access to account states that might bypass the prefix
rg -g '*.go' -B 2 -A 2 'KVStore.*AccountState' --no-heading

Length of output: 880


Script:

#!/bin/bash
# Search for any store operations related to AccountState
rg -g '*.go' -B 2 -A 2 'AccountState.*(?:Get|Set|Delete)' --no-heading

# Also check for any other usage of AccountStateKeyPrefix
rg -g '*.go' -B 2 -A 2 'AccountStateKeyPrefix' --no-heading

Length of output: 4323

Comment on lines 30 to 82
func (s *UpgradeTestSuite) TestUpgrade_MigrateAccountplusAccountState() {
ctx := s.Ctx
store := ctx.KVStore(s.tApp.App.AccountPlusKeeper.GetStoreKey())
prefixStore := prefix.NewStore(store, []byte(accountplustypes.AccountStateKeyPrefix))

// Create some AccountState with no prefixes
addresses := []string{"address1", "address2", "address3"}
for _, addr := range addresses {
accAddress := sdk.AccAddress([]byte(addr))
accountState := accountplustypes.AccountState{
Address: addr,
TimestampNonceDetails: accountplustypes.TimestampNonceDetails{
TimestampNonces: []uint64{1, 2, 3},
MaxEjectedNonce: 0,
},
}
bz := s.tApp.App.AccountPlusKeeper.GetCdc().MustMarshal(&accountState)
store.Set(accAddress, bz)
}

// Verify unprefixed keys were successfully created
for _, addr := range addresses {
accAddress := sdk.AccAddress([]byte(addr))
bz := store.Get(accAddress)
s.Require().NotNil(bz, "Unprefixed key not created for %s", addr)
}

// Migrate
migrateAccountplusAccountState(ctx, s.tApp.App.AccountPlusKeeper)

// Verify that unprefixed keys are deleted and prefixed keys exist
for _, addr := range addresses {
accAddress := sdk.AccAddress([]byte(addr))

// Check that the old key no longer exists
bzOld := store.Get(accAddress)
s.Require().Nil(bzOld, "Unprefixed AccountState should be deleted for %s", addr)

// Check that the new prefixed key exists
bzNew := prefixStore.Get(accAddress)
var actualAccountState types.AccountState
s.tApp.App.AccountPlusKeeper.GetCdc().MustUnmarshal(bzNew, &actualAccountState)
expectedAccountState := accountplustypes.AccountState{
Address: addr,
TimestampNonceDetails: accountplustypes.TimestampNonceDetails{
TimestampNonces: []uint64{1, 2, 3},
MaxEjectedNonce: 0,
},
}
s.Require().NotNil(bzNew, "Prefixed AccountState should exist for %s", addr)
s.Require().Equal(expectedAccountState, actualAccountState, "Incorrect AccountState after migration for %s", addr)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Based on the implementation found, I can now provide a final response.

Additional test cases needed with specific focus on error handling

The migration function contains critical error paths that aren't currently tested:

  • It panics when encountering keys with unexpected prefixes
  • It panics on unmarshaling errors for invalid account states

The review suggestion for additional test cases is valid, but should specifically include:

  1. Test for panic on prefixed keys:
func (s *UpgradeTestSuite) TestUpgrade_MigrateAccountplusAccountState_PrefixedKeyPanic() {
    // Create an account state with the prefix that should trigger panic
}
  1. Test for panic on invalid state:
func (s *UpgradeTestSuite) TestUpgrade_MigrateAccountplusAccountState_InvalidStatePanic() {
    // Store corrupted/invalid bytes that will fail unmarshaling
}
  1. Empty state and idempotency tests as originally suggested

The implementation shows these are critical scenarios that should be verified through tests.

🔗 Analysis chain

Enhance test coverage with edge cases

The test covers the happy path but should also verify:

  1. Empty account states
  2. Invalid account states
  3. Migration idempotency (running migration multiple times)
  4. Large number of accounts to verify performance

Consider adding these test cases:

func (s *UpgradeTestSuite) TestUpgrade_MigrateAccountplusAccountState_Empty() {
    // Test migration with no existing account states
    migrateAccountplusAccountState(s.Ctx, s.tApp.App.AccountPlusKeeper)
    // Verify no errors occur
}

func (s *UpgradeTestSuite) TestUpgrade_MigrateAccountplusAccountState_Idempotent() {
    // Run original test setup
    s.TestUpgrade_MigrateAccountplusAccountState()
    // Run migration again
    migrateAccountplusAccountState(s.Ctx, s.tApp.App.AccountPlusKeeper)
    // Verify state remains correct
}

Let me gather more information about the test coverage and error handling in the codebase.


Let me check the implementation of the migration function to understand the error handling better.


Let me try one final check to locate the migration function implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any existing error handling tests
rg -l "TestUpgrade.*Error" --type go

Length of output: 38


Script:

#!/bin/bash
# Check the implementation of migrateAccountplusAccountState to understand error handling
ast-grep --pattern 'func migrateAccountplusAccountState'

# Look for any existing test files related to accountplus migration
fd "migrate.*test.go"

# Check for any error handling patterns in the test file
rg "s\.Require\(\)" protocol/app/upgrades/v8.0/migrate_accountplus_test.go

Length of output: 561


Script:

#!/bin/bash
# Get the implementation of migrateAccountplusAccountState
rg -A 20 "func migrateAccountplusAccountState" protocol/app/upgrades/v8.0/migrate_accountplus.go

# Check if there are any other test files in v8.0 that might have error handling patterns
fd ".*test.go" protocol/app/upgrades/v8.0/

# Look for any existing test setup patterns
rg "func.*TestUpgrade" protocol/app/upgrades/v8.0/migrate_accountplus_test.go -A 5

Length of output: 661


Script:

#!/bin/bash
# Search for the migration function in the entire codebase
rg -A 20 "func migrateAccountplusAccountState" 

# Check if there are any similar migration functions in v8.0
rg "func migrate" protocol/app/upgrades/v8.0/

# Look for the AccountState type definition to understand the data structure
ast-grep --pattern 'type AccountState struct'

Length of output: 1960

}

// SetAccountState stores with prefix
k.SetAccountState(ctx, key, accountState)
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this break the contract of iterators?

	// Iterator over a domain of keys in ascending order. End is exclusive.
	// Start must be less than end, or the Iterator is invalid.
	// Iterator must be closed by caller.
	// To iterate over entire domain, use store.Iterator(nil, nil)
	// CONTRACT: No writes may happen within a domain while an iterator exists over it.
	// Exceptionally allowed for cachekv.Store, safe to write in the modules.
	Iterator(start, end []byte) Iterator

e.g. is it safe to modify state while having an open iterator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be safe, I will collect the items and set and remove outside of iteration

@jerryfan01234 jerryfan01234 force-pushed the add-prefix-to-accountplus-keeper branch from 8d8231a to 3d9b80a Compare October 23, 2024 19:19
@jerryfan01234 jerryfan01234 merged commit 2198a3b into main Oct 28, 2024
22 checks passed
@jerryfan01234 jerryfan01234 deleted the add-prefix-to-accountplus-keeper branch October 28, 2024 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

3 participants