fix(auth): support legacy global AccountNumber when query historical state (port #23743) (#24533)

Co-authored-by: HuangYi <huang@crypto.com>
Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
This commit is contained in:
mmsqe 2025-04-23 22:53:36 +08:00 committed by GitHub
parent 39bdcc7fc2
commit 3b90ba4ef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 158 additions and 13 deletions

View File

@ -105,6 +105,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (baseapp) [#24074](https://github.com/cosmos/cosmos-sdk/pull/24074) Use CometBFT's ComputeProtoSizeForTxs in defaultTxSelector.SelectTxForProposal for consistency.
* (cli) [#24090](https://github.com/cosmos/cosmos-sdk/pull/24090) Prune cmd should disable async pruning.
* (x/auth) [#19239](https://github.com/cosmos/cosmos-sdk/pull/19239) Sets from flag in multi-sign command to avoid no key name provided error.
* (x/auth) [#23741](https://github.com/cosmos/cosmos-sdk/pull/23741) Support legacy global AccountNumber for legacy compatibility.
* (baseapp) [#24526](https://github.com/cosmos/cosmos-sdk/pull/24526) Fix incorrect retention height when `commitHeight` equals `minRetainBlocks`.
## [v0.50.12](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.12) - 2025-02-20

View File

@ -6,6 +6,8 @@ import (
"fmt"
"time"
gogotypes "github.com/cosmos/gogoproto/types"
"cosmossdk.io/collections"
"cosmossdk.io/collections/indexes"
"cosmossdk.io/core/address"
@ -181,13 +183,39 @@ func (ak AccountKeeper) GetSequence(ctx context.Context, addr sdk.AccAddress) (u
return acc.GetSequence(), nil
}
func (ak AccountKeeper) getAccountNumberLegacy(ctx context.Context) (uint64, error) {
store := ak.storeService.OpenKVStore(ctx)
b, err := store.Get(types.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
}
// NextAccountNumber returns and increments the global account number counter.
// If the global account number is not set, it initializes it with value 0.
func (ak AccountKeeper) NextAccountNumber(ctx context.Context) uint64 {
n, err := ak.AccountNumber.Next(ctx)
n, err := collections.Item[uint64](ak.AccountNumber).Get(ctx)
if err != nil && errors.Is(err, collections.ErrNotFound) {
// this won't happen in the tip of production network,
// but can happen when query historical states,
// fallback to old key for backward-compatibility.
// for more info, see https://github.com/cosmos/cosmos-sdk/issues/23741
n, err = ak.getAccountNumberLegacy(ctx)
}
if err != nil {
panic(err)
}
if err := ak.AccountNumber.Set(ctx, n+1); err != nil {
panic(err)
}
return n
}

View File

@ -3,9 +3,15 @@ package keeper_test
import (
"testing"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
cmttime "github.com/cometbft/cometbft/types/time"
gogotypes "github.com/cosmos/gogoproto/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"cosmossdk.io/collections"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/baseapp"
@ -32,6 +38,17 @@ var (
randomPermAcc = types.NewEmptyModuleAccount(randomPerm, "random")
)
func getMaccPerms() map[string][]string {
return map[string][]string{
"fee_collector": nil,
"mint": {"minter"},
"bonded_tokens_pool": {"burner", "staking"},
"not_bonded_tokens_pool": {"burner", "staking"},
multiPerm: {"burner", "minter", "staking"},
randomPerm: {"random"},
}
}
type KeeperTestSuite struct {
suite.Suite
@ -51,20 +68,11 @@ func (suite *KeeperTestSuite) SetupTest() {
testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test"))
suite.ctx = testCtx.Ctx.WithHeaderInfo(header.Info{})
maccPerms := map[string][]string{
"fee_collector": nil,
"mint": {"minter"},
"bonded_tokens_pool": {"burner", "staking"},
"not_bonded_tokens_pool": {"burner", "staking"},
multiPerm: {"burner", "minter", "staking"},
randomPerm: {"random"},
}
suite.accountKeeper = keeper.NewAccountKeeper(
suite.encCfg.Codec,
storeService,
types.ProtoBaseAccount,
maccPerms,
getMaccPerms(),
authcodec.NewBech32Codec("cosmos"),
"cosmos",
types.NewModuleAddress("gov").String(),
@ -215,3 +223,104 @@ func (suite *KeeperTestSuite) TestInitGenesis() {
// we expect nextNum to be 2 because we initialize fee_collector as account number 1
suite.Require().Equal(2, int(nextNum))
}
func setupAccountKeeper(t *testing.T) (sdk.Context, keeper.AccountKeeper, store.KVStoreService) {
t.Helper()
key := storetypes.NewKVStoreKey(types.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
encCfg := moduletestutil.MakeTestEncodingConfig()
ak := keeper.NewAccountKeeper(
encCfg.Codec,
storeService,
types.ProtoBaseAccount,
getMaccPerms(),
authcodec.NewBech32Codec("cosmos"),
"cosmos",
types.NewModuleAddress("gov").String(),
)
return ctx, ak, storeService
}
func TestNextAccountNumber(t *testing.T) {
const newNum = uint64(100)
const legacyNum = uint64(50)
legacyVal := &gogotypes.UInt64Value{Value: legacyNum}
ctx, ak, storeService := setupAccountKeeper(t)
testCases := []struct {
name string
setup func()
onNext func()
expects []uint64
}{
{
name: "reset account number to 0 after using legacy key",
setup: func() {
data, err := legacyVal.Marshal()
require.NoError(t, err)
store := storeService.OpenKVStore(ctx)
err = store.Set(types.LegacyGlobalAccountNumberKey, data)
require.NoError(t, err)
},
onNext: func() {
num := uint64(0)
err := ak.AccountNumber.Set(ctx, num)
require.NoError(t, err)
},
expects: []uint64{legacyNum, 0},
},
{
name: "no keys set, account number starts at 0",
setup: func() {},
expects: []uint64{0, 1},
},
{
name: "fallback to legacy key when new key is unset",
setup: func() {
data, err := legacyVal.Marshal()
require.NoError(t, err)
store := storeService.OpenKVStore(ctx)
err = store.Set(types.LegacyGlobalAccountNumberKey, data)
require.NoError(t, err)
// unset new key
err = (collections.Item[uint64])(ak.AccountNumber).Remove(ctx)
require.NoError(t, err)
},
expects: []uint64{legacyNum, legacyNum + 1},
},
{
name: "new key takes precedence over legacy key",
setup: func() {
data, err := legacyVal.Marshal()
require.NoError(t, err)
store := storeService.OpenKVStore(ctx)
err = store.Set(types.LegacyGlobalAccountNumberKey, data)
require.NoError(t, err)
err = ak.AccountNumber.Set(ctx, newNum)
require.NoError(t, err)
},
expects: []uint64{newNum, newNum + 1},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx, ak, storeService = setupAccountKeeper(t)
tc.setup()
nextNum := ak.NextAccountNumber(ctx)
require.Equal(t, tc.expects[0], nextNum)
if tc.onNext != nil {
tc.onNext()
}
nextNum = ak.NextAccountNumber(ctx)
require.Equal(t, tc.expects[1], nextNum)
})
}
}

View File

@ -7,9 +7,11 @@ import (
"cosmossdk.io/collections"
storetypes "cosmossdk.io/core/store"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
var LegacyGlobalAccountNumberKey = []byte("globalAccountNumber")
var LegacyGlobalAccountNumberKey = authtypes.LegacyGlobalAccountNumberKey
func Migrate(ctx context.Context, storeService storetypes.KVStoreService, sequence collections.Sequence) error {
store := storeService.OpenKVStore(ctx)

View File

@ -8,6 +8,8 @@ import (
"cosmossdk.io/collections"
"cosmossdk.io/collections/colltest"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
func TestMigrate(t *testing.T) {
@ -21,7 +23,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)

View File

@ -31,4 +31,7 @@ var (
// UnorderedNoncesKey prefix for the unordered sequence storage.
UnorderedNoncesKey = collections.NewPrefix(90)
// legacy param key for global account number
LegacyGlobalAccountNumberKey = []byte("globalAccountNumber")
)