Skip to content

Commit 3b90ba4

Browse files
mmsqeyihuangaljo242
authored
fix(auth): support legacy global AccountNumber when query historical state (port #23743) (#24533)
Co-authored-by: HuangYi <[email protected]> Co-authored-by: Alex | Interchain Labs <[email protected]>
1 parent 39bdcc7 commit 3b90ba4

File tree

6 files changed

+158
-13
lines changed

6 files changed

+158
-13
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
105105
* (baseapp) [#24074](https://github.com/cosmos/cosmos-sdk/pull/24074) Use CometBFT's ComputeProtoSizeForTxs in defaultTxSelector.SelectTxForProposal for consistency.
106106
* (cli) [#24090](https://github.com/cosmos/cosmos-sdk/pull/24090) Prune cmd should disable async pruning.
107107
* (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.
108+
* (x/auth) [#23741](https://github.com/cosmos/cosmos-sdk/pull/23741) Support legacy global AccountNumber for legacy compatibility.
108109
* (baseapp) [#24526](https://github.com/cosmos/cosmos-sdk/pull/24526) Fix incorrect retention height when `commitHeight` equals `minRetainBlocks`.
109110

110111
## [v0.50.12](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.12) - 2025-02-20

x/auth/keeper/keeper.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"fmt"
77
"time"
88

9+
gogotypes "github.com/cosmos/gogoproto/types"
10+
911
"cosmossdk.io/collections"
1012
"cosmossdk.io/collections/indexes"
1113
"cosmossdk.io/core/address"
@@ -181,13 +183,39 @@ func (ak AccountKeeper) GetSequence(ctx context.Context, addr sdk.AccAddress) (u
181183
return acc.GetSequence(), nil
182184
}
183185

186+
func (ak AccountKeeper) getAccountNumberLegacy(ctx context.Context) (uint64, error) {
187+
store := ak.storeService.OpenKVStore(ctx)
188+
b, err := store.Get(types.LegacyGlobalAccountNumberKey)
189+
if err != nil {
190+
return 0, fmt.Errorf("failed to get legacy account number: %w", err)
191+
}
192+
v := new(gogotypes.UInt64Value)
193+
if err := v.Unmarshal(b); err != nil {
194+
return 0, fmt.Errorf("failed to unmarshal legacy account number: %w", err)
195+
}
196+
return v.Value, nil
197+
}
198+
184199
// NextAccountNumber returns and increments the global account number counter.
185200
// If the global account number is not set, it initializes it with value 0.
186201
func (ak AccountKeeper) NextAccountNumber(ctx context.Context) uint64 {
187-
n, err := ak.AccountNumber.Next(ctx)
202+
n, err := collections.Item[uint64](ak.AccountNumber).Get(ctx)
203+
if err != nil && errors.Is(err, collections.ErrNotFound) {
204+
// this won't happen in the tip of production network,
205+
// but can happen when query historical states,
206+
// fallback to old key for backward-compatibility.
207+
// for more info, see https://github.com/cosmos/cosmos-sdk/issues/23741
208+
n, err = ak.getAccountNumberLegacy(ctx)
209+
}
210+
188211
if err != nil {
189212
panic(err)
190213
}
214+
215+
if err := ak.AccountNumber.Set(ctx, n+1); err != nil {
216+
panic(err)
217+
}
218+
191219
return n
192220
}
193221

x/auth/keeper/keeper_test.go

+119-10
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ package keeper_test
33
import (
44
"testing"
55

6+
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
7+
cmttime "github.com/cometbft/cometbft/types/time"
8+
gogotypes "github.com/cosmos/gogoproto/types"
9+
"github.com/stretchr/testify/require"
610
"github.com/stretchr/testify/suite"
711

12+
"cosmossdk.io/collections"
813
"cosmossdk.io/core/header"
14+
"cosmossdk.io/core/store"
915
storetypes "cosmossdk.io/store/types"
1016

1117
"github.com/cosmos/cosmos-sdk/baseapp"
@@ -32,6 +38,17 @@ var (
3238
randomPermAcc = types.NewEmptyModuleAccount(randomPerm, "random")
3339
)
3440

41+
func getMaccPerms() map[string][]string {
42+
return map[string][]string{
43+
"fee_collector": nil,
44+
"mint": {"minter"},
45+
"bonded_tokens_pool": {"burner", "staking"},
46+
"not_bonded_tokens_pool": {"burner", "staking"},
47+
multiPerm: {"burner", "minter", "staking"},
48+
randomPerm: {"random"},
49+
}
50+
}
51+
3552
type KeeperTestSuite struct {
3653
suite.Suite
3754

@@ -51,20 +68,11 @@ func (suite *KeeperTestSuite) SetupTest() {
5168
testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test"))
5269
suite.ctx = testCtx.Ctx.WithHeaderInfo(header.Info{})
5370

54-
maccPerms := map[string][]string{
55-
"fee_collector": nil,
56-
"mint": {"minter"},
57-
"bonded_tokens_pool": {"burner", "staking"},
58-
"not_bonded_tokens_pool": {"burner", "staking"},
59-
multiPerm: {"burner", "minter", "staking"},
60-
randomPerm: {"random"},
61-
}
62-
6371
suite.accountKeeper = keeper.NewAccountKeeper(
6472
suite.encCfg.Codec,
6573
storeService,
6674
types.ProtoBaseAccount,
67-
maccPerms,
75+
getMaccPerms(),
6876
authcodec.NewBech32Codec("cosmos"),
6977
"cosmos",
7078
types.NewModuleAddress("gov").String(),
@@ -215,3 +223,104 @@ func (suite *KeeperTestSuite) TestInitGenesis() {
215223
// we expect nextNum to be 2 because we initialize fee_collector as account number 1
216224
suite.Require().Equal(2, int(nextNum))
217225
}
226+
227+
func setupAccountKeeper(t *testing.T) (sdk.Context, keeper.AccountKeeper, store.KVStoreService) {
228+
t.Helper()
229+
key := storetypes.NewKVStoreKey(types.StoreKey)
230+
storeService := runtime.NewKVStoreService(key)
231+
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
232+
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
233+
encCfg := moduletestutil.MakeTestEncodingConfig()
234+
235+
ak := keeper.NewAccountKeeper(
236+
encCfg.Codec,
237+
storeService,
238+
types.ProtoBaseAccount,
239+
getMaccPerms(),
240+
authcodec.NewBech32Codec("cosmos"),
241+
"cosmos",
242+
types.NewModuleAddress("gov").String(),
243+
)
244+
245+
return ctx, ak, storeService
246+
}
247+
248+
func TestNextAccountNumber(t *testing.T) {
249+
const newNum = uint64(100)
250+
const legacyNum = uint64(50)
251+
legacyVal := &gogotypes.UInt64Value{Value: legacyNum}
252+
ctx, ak, storeService := setupAccountKeeper(t)
253+
testCases := []struct {
254+
name string
255+
setup func()
256+
onNext func()
257+
expects []uint64
258+
}{
259+
{
260+
name: "reset account number to 0 after using legacy key",
261+
setup: func() {
262+
data, err := legacyVal.Marshal()
263+
require.NoError(t, err)
264+
store := storeService.OpenKVStore(ctx)
265+
err = store.Set(types.LegacyGlobalAccountNumberKey, data)
266+
require.NoError(t, err)
267+
},
268+
onNext: func() {
269+
num := uint64(0)
270+
err := ak.AccountNumber.Set(ctx, num)
271+
require.NoError(t, err)
272+
},
273+
expects: []uint64{legacyNum, 0},
274+
},
275+
{
276+
name: "no keys set, account number starts at 0",
277+
setup: func() {},
278+
expects: []uint64{0, 1},
279+
},
280+
{
281+
name: "fallback to legacy key when new key is unset",
282+
setup: func() {
283+
data, err := legacyVal.Marshal()
284+
require.NoError(t, err)
285+
store := storeService.OpenKVStore(ctx)
286+
err = store.Set(types.LegacyGlobalAccountNumberKey, data)
287+
require.NoError(t, err)
288+
289+
// unset new key
290+
err = (collections.Item[uint64])(ak.AccountNumber).Remove(ctx)
291+
require.NoError(t, err)
292+
},
293+
expects: []uint64{legacyNum, legacyNum + 1},
294+
},
295+
{
296+
name: "new key takes precedence over legacy key",
297+
setup: func() {
298+
data, err := legacyVal.Marshal()
299+
require.NoError(t, err)
300+
store := storeService.OpenKVStore(ctx)
301+
err = store.Set(types.LegacyGlobalAccountNumberKey, data)
302+
require.NoError(t, err)
303+
304+
err = ak.AccountNumber.Set(ctx, newNum)
305+
require.NoError(t, err)
306+
},
307+
expects: []uint64{newNum, newNum + 1},
308+
},
309+
}
310+
311+
for _, tc := range testCases {
312+
t.Run(tc.name, func(t *testing.T) {
313+
ctx, ak, storeService = setupAccountKeeper(t)
314+
tc.setup()
315+
nextNum := ak.NextAccountNumber(ctx)
316+
require.Equal(t, tc.expects[0], nextNum)
317+
318+
if tc.onNext != nil {
319+
tc.onNext()
320+
}
321+
322+
nextNum = ak.NextAccountNumber(ctx)
323+
require.Equal(t, tc.expects[1], nextNum)
324+
})
325+
}
326+
}

x/auth/migrations/v5/migrate.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import (
77

88
"cosmossdk.io/collections"
99
storetypes "cosmossdk.io/core/store"
10+
11+
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1012
)
1113

12-
var LegacyGlobalAccountNumberKey = []byte("globalAccountNumber")
14+
var LegacyGlobalAccountNumberKey = authtypes.LegacyGlobalAccountNumberKey
1315

1416
func Migrate(ctx context.Context, storeService storetypes.KVStoreService, sequence collections.Sequence) error {
1517
store := storeService.OpenKVStore(ctx)

x/auth/migrations/v5/migrate_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88

99
"cosmossdk.io/collections"
1010
"cosmossdk.io/collections/colltest"
11+
12+
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1113
)
1214

1315
func TestMigrate(t *testing.T) {
@@ -21,7 +23,7 @@ func TestMigrate(t *testing.T) {
2123
legacySeqBytes, err := (&types.UInt64Value{Value: wantValue}).Marshal()
2224
require.NoError(t, err)
2325

24-
err = kv.OpenKVStore(ctx).Set(LegacyGlobalAccountNumberKey, legacySeqBytes)
26+
err = kv.OpenKVStore(ctx).Set(authtypes.LegacyGlobalAccountNumberKey, legacySeqBytes)
2527
require.NoError(t, err)
2628

2729
err = Migrate(ctx, kv, seq)

x/auth/types/keys.go

+3
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ var (
3131

3232
// UnorderedNoncesKey prefix for the unordered sequence storage.
3333
UnorderedNoncesKey = collections.NewPrefix(90)
34+
35+
// legacy param key for global account number
36+
LegacyGlobalAccountNumberKey = []byte("globalAccountNumber")
3437
)

0 commit comments

Comments
 (0)