diff --git a/CHANGELOG.md b/CHANGELOG.md index 17126d5bc28a..761f7e20159d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,8 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i ### Bug Fixes +* (x/auth) [#23741](https://github.com/cosmos/cosmos-sdk/pull/23741) Support legacy global AccountNumber. + ### Removed * (tools/hub) [#23562](https://github.com/cosmos/cosmos-sdk/pull/23562) Remove `tools/hubl`. A similar tool will be maintained in [ignite](https://www.github.com/ignite/cli). diff --git a/x/accounts/keeper.go b/x/accounts/keeper.go index 6c31d4d7ab9f..1ace9718f6a6 100644 --- a/x/accounts/keeper.go +++ b/x/accounts/keeper.go @@ -9,6 +9,7 @@ import ( "fmt" gogoproto "github.com/cosmos/gogoproto/proto" + gogotypes "github.com/cosmos/gogoproto/types" _ "cosmossdk.io/api/cosmos/accounts/defaults/base/v1" // import for side-effects "cosmossdk.io/collections" @@ -22,6 +23,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) var ( @@ -122,14 +124,39 @@ func (k Keeper) IsAccountsModuleAccount( return hasAcc } +func (k Keeper) GetAccountNumberLegacy(ctx context.Context) (uint64, error) { + store := k.KVStoreService.OpenKVStore(ctx) + b, err := store.Get(authtypes.LegacyGlobalAccountNumberKey) + if err != nil { + return 0, fmt.Errorf("failed to get legacy account number: %w", err) + } + v := new(gogotypes.UInt64Value) + if err := v.Unmarshal(b); err != nil { + return 0, fmt.Errorf("failed to unmarshal legacy account number: %w", err) + } + return v.Value, nil +} + func (k Keeper) NextAccountNumber( ctx context.Context, ) (accNum uint64, err error) { - accNum, err = k.AccountNumber.Next(ctx) + accNum, err = collections.Item[uint64](k.AccountNumber).Get(ctx) + if err != nil && errors.Is(err, collections.ErrNotFound) { + // This change makes the method works in historical states. + // Although the behavior is not identical, but semantically compatible. + // + // For the state machine, it also does the migration lazily. + accNum, err = k.GetAccountNumberLegacy(ctx) + } + if err != nil { return 0, err } + if err := k.AccountNumber.Set(ctx, accNum+1); err != nil { + return 0, err + } + return accNum, nil } diff --git a/x/accounts/keeper_test.go b/x/accounts/keeper_test.go index ba75abb37966..2c7649109376 100644 --- a/x/accounts/keeper_test.go +++ b/x/accounts/keeper_test.go @@ -9,6 +9,8 @@ import ( "cosmossdk.io/collections" "cosmossdk.io/x/accounts/accountstd" "cosmossdk.io/x/accounts/internal/implementation" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestKeeper_Init(t *testing.T) { @@ -90,3 +92,28 @@ func TestKeeper_Query(t *testing.T) { require.True(t, implementation.Equal(&types.Int64Value{Value: 1000}, resp)) }) } + +func TestKeeper_NextAccountNumber(t *testing.T) { + m, ctx := newKeeper(t, accountstd.AddAccount("test", NewTestAccount)) + store := m.KVStoreService.OpenKVStore(ctx) + num := uint64(10) + val := &types.UInt64Value{ + Value: num, + } + data, err := val.Marshal() + require.NoError(t, err) + err = store.Set(authtypes.LegacyGlobalAccountNumberKey, data) + require.NoError(t, err) + + n, err := m.NextAccountNumber(ctx) + require.NoError(t, err) + require.Equal(t, num, n) + + num = uint64(0) + err = m.AccountNumber.Set(ctx, num) + require.NoError(t, err) + + n, err = m.NextAccountNumber(ctx) + require.NoError(t, err) + require.Equal(t, num, n) +} diff --git a/x/auth/migrations/v5/migrate.go b/x/auth/migrations/v5/migrate.go index 44fcf970c359..8d8da10de361 100644 --- a/x/auth/migrations/v5/migrate.go +++ b/x/auth/migrations/v5/migrate.go @@ -7,13 +7,13 @@ import ( "cosmossdk.io/collections" storetypes "cosmossdk.io/core/store" -) -var LegacyGlobalAccountNumberKey = []byte("globalAccountNumber") + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) func Migrate(ctx context.Context, storeService storetypes.KVStoreService, sequence collections.Sequence) error { store := storeService.OpenKVStore(ctx) - b, err := store.Get(LegacyGlobalAccountNumberKey) + b, err := store.Get(authtypes.LegacyGlobalAccountNumberKey) if err != nil { return err } @@ -37,7 +37,7 @@ func Migrate(ctx context.Context, storeService storetypes.KVStoreService, sequen } // remove the value from the old prefix. - err = store.Delete(LegacyGlobalAccountNumberKey) + err = store.Delete(authtypes.LegacyGlobalAccountNumberKey) if err != nil { return err } diff --git a/x/auth/migrations/v5/migrate_test.go b/x/auth/migrations/v5/migrate_test.go index 2ed3d69c78b5..07a1af6dec1b 100644 --- a/x/auth/migrations/v5/migrate_test.go +++ b/x/auth/migrations/v5/migrate_test.go @@ -7,7 +7,9 @@ import ( "github.com/stretchr/testify/require" "cosmossdk.io/collections" - "cosmossdk.io/core/testing" + coretesting "cosmossdk.io/core/testing" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestMigrate(t *testing.T) { @@ -22,7 +24,7 @@ func TestMigrate(t *testing.T) { legacySeqBytes, err := (&types.UInt64Value{Value: wantValue}).Marshal() require.NoError(t, err) - err = kv.OpenKVStore(ctx).Set(LegacyGlobalAccountNumberKey, legacySeqBytes) + err = kv.OpenKVStore(ctx).Set(authtypes.LegacyGlobalAccountNumberKey, legacySeqBytes) require.NoError(t, err) err = Migrate(ctx, kv, seq) diff --git a/x/auth/types/keys.go b/x/auth/types/keys.go index fb3295511f23..cd3af0ad0b3e 100644 --- a/x/auth/types/keys.go +++ b/x/auth/types/keys.go @@ -28,4 +28,7 @@ var ( // AccountNumberStoreKeyPrefix prefix for account-by-id store AccountNumberStoreKeyPrefix = collections.NewPrefix("accountNumber") + + // legacy param key for global account number + LegacyGlobalAccountNumberKey = []byte("globalAccountNumber") )