From 23b7c06aa6d1e6cbe64ac288bdbe9fcd39b09c17 Mon Sep 17 00:00:00 2001 From: DevOrbitlabs Date: Sat, 25 Apr 2026 22:44:36 +0700 Subject: [PATCH 1/5] fix: ibc handling in wasm (#78) * - fix ibc handling in wasm (#77) - remove fork code to use upstream - remove unused code * Problem: cometbft not up to date * add v14_2 * bump version * bump version * bump version * fix legacy query staking (#79) * fix legacy query staking * bet * fix legacy params for validator delegation fallback * Bet * bet --------- Co-authored-by: Khanh Hoa Co-authored-by: Tuan Tran * consistent alias --------- Co-authored-by: StrathCole <7449529+StrathCole@users.noreply.github.com> Co-authored-by: Kien Trinh <51135161+kien6034@users.noreply.github.com> Co-authored-by: Tuan Tran --- app/app.go | 4 + app/keepers/keepers.go | 7 +- app/modules.go | 2 +- app/upgrades/v14_2/constants.go | 21 ++ app/upgrades/v14_2/upgrades.go | 24 ++ custom/staking/module.go | 8 +- custom/staking/query_server.go | 147 +++++++++- custom/staking/query_server_test.go | 407 +++++++++++++++++++++++++++ custom/upgrade/legacy/height.go | 31 ++ custom/wasm/keeper/handler_plugin.go | 128 ++------- custom/wasm/query_server.go | 10 - custom/wasm/simulation/operations.go | 47 ---- go.mod | 8 +- go.sum | 16 +- scripts/upgrade-test.sh | 4 +- tests/interchaintest/go.mod | 8 +- tests/interchaintest/go.sum | 16 +- 17 files changed, 686 insertions(+), 202 deletions(-) create mode 100644 app/upgrades/v14_2/constants.go create mode 100644 app/upgrades/v14_2/upgrades.go create mode 100644 custom/staking/query_server_test.go delete mode 100644 custom/wasm/simulation/operations.go diff --git a/app/app.go b/app/app.go index 7247d1280..512948d00 100644 --- a/app/app.go +++ b/app/app.go @@ -18,6 +18,7 @@ import ( "github.com/classic-terra/core/v4/app/keepers" appmempool "github.com/classic-terra/core/v4/app/mempool" terraappparams "github.com/classic-terra/core/v4/app/params" + // upgrades "github.com/classic-terra/core/v4/app/upgrades" // v9 had been used by tax2gas and has to be skipped @@ -29,6 +30,7 @@ import ( v13 "github.com/classic-terra/core/v4/app/upgrades/v13" v13_1 "github.com/classic-terra/core/v4/app/upgrades/v13_1" v14_1 "github.com/classic-terra/core/v4/app/upgrades/v14_1" + v14_2 "github.com/classic-terra/core/v4/app/upgrades/v14_2" v2 "github.com/classic-terra/core/v4/app/upgrades/v2" v3 "github.com/classic-terra/core/v4/app/upgrades/v3" v4 "github.com/classic-terra/core/v4/app/upgrades/v4" @@ -41,6 +43,7 @@ import ( v8_1 "github.com/classic-terra/core/v4/app/upgrades/v8_1" v8_2 "github.com/classic-terra/core/v4/app/upgrades/v8_2" v8_3 "github.com/classic-terra/core/v4/app/upgrades/v8_3" + // unnamed import of statik for swagger UI support _ "github.com/classic-terra/core/v4/client/docs/statik" customante "github.com/classic-terra/core/v4/custom/auth/ante" @@ -105,6 +108,7 @@ var ( v13.Upgrade, v13_1.Upgrade, v14_1.Upgrade, + v14_2.Upgrade, } // Forks defines forks to be applied to the network diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index e3df7e6a2..84280bcf6 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -385,13 +385,10 @@ func NewAppKeepers( wasmMsgHandler := customwasmkeeper.NewMessageHandler( bApp.MsgServiceRouter(), + &appKeepers.WasmKeeper, appKeepers.IBCHooksWrapper, - appKeepers.IBCKeeper.ChannelKeeper, + appKeepers.IBCKeeper.ChannelKeeperV2, appKeepers.BankKeeper, - appKeepers.TaxExemptionKeeper, - appKeepers.TreasuryKeeper, - appKeepers.AccountKeeper, - appKeepers.TaxKeeper, appCodec, appKeepers.TransferKeeper, ) diff --git a/app/modules.go b/app/modules.go index ce5605ec8..07243a9f3 100644 --- a/app/modules.go +++ b/app/modules.go @@ -156,7 +156,7 @@ func appModules( mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName), app.InterfaceRegistry()), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), - customstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.ParamsKeeper, app.GetSubspace(stakingtypes.ModuleName)), + customstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.ParamsKeeper, app.GetSubspace(stakingtypes.ModuleName), app.GetKey(stakingtypes.StoreKey)), upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), evidence.NewAppModule(app.EvidenceKeeper), params.NewAppModule(app.ParamsKeeper), diff --git a/app/upgrades/v14_2/constants.go b/app/upgrades/v14_2/constants.go new file mode 100644 index 000000000..24d969a82 --- /dev/null +++ b/app/upgrades/v14_2/constants.go @@ -0,0 +1,21 @@ +//nolint:revive +package v14_2 + +import ( + store "cosmossdk.io/store/types" + "github.com/classic-terra/core/v4/app/upgrades" +) + +const UpgradeName = "v14_2" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateV142UpgradeHandler, + // Add new stores introduced since the last upgrade here. If there are + // no new stores for this upgrade, leave this empty. + StoreUpgrades: store.StoreUpgrades{ + Added: []string{}, + Deleted: []string{}, + Renamed: []store.StoreRename{}, + }, +} diff --git a/app/upgrades/v14_2/upgrades.go b/app/upgrades/v14_2/upgrades.go new file mode 100644 index 000000000..35154c5fe --- /dev/null +++ b/app/upgrades/v14_2/upgrades.go @@ -0,0 +1,24 @@ +//nolint:revive +package v14_2 + +import ( + "context" + + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/classic-terra/core/v4/app/keepers" + "github.com/classic-terra/core/v4/app/upgrades" + "github.com/cosmos/cosmos-sdk/types/module" +) + +// CreateV142UpgradeHandler wires module migrations for v14_2. +// Add any one-off migration logic here before/after RunMigrations if needed. +func CreateV142UpgradeHandler( + mm *module.Manager, + cfg module.Configurator, + _ upgrades.BaseAppParamManager, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + return mm.RunMigrations(ctx, cfg, fromVM) + } +} diff --git a/custom/staking/module.go b/custom/staking/module.go index 446b790ef..002c07b45 100644 --- a/custom/staking/module.go +++ b/custom/staking/module.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + storetypes "cosmossdk.io/store/types" customtypes "github.com/classic-terra/core/v4/custom/staking/types" core "github.com/classic-terra/core/v4/types" "github.com/cosmos/cosmos-sdk/codec" @@ -44,9 +45,11 @@ func (am AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { type AppModule struct { staking.AppModule + cdc codec.Codec keeper *keeper.Keeper paramsKeeper paramskeeper.Keeper ss paramtypes.Subspace + storeKey storetypes.StoreKey } // NewAppModule creates a new AppModule object @@ -56,12 +59,15 @@ func NewAppModule(cdc codec.Codec, bk stakingtypes.BankKeeper, pk paramskeeper.Keeper, ss paramtypes.Subspace, + storeKey storetypes.StoreKey, ) AppModule { return AppModule{ AppModule: staking.NewAppModule(cdc, keeper, ak, bk, ss), + cdc: cdc, keeper: keeper, paramsKeeper: pk, ss: ss, + storeKey: storeKey, } } @@ -72,7 +78,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { querier := keeper.Querier{Keeper: am.keeper} stakingtypes.RegisterQueryServer( cfg.QueryServer(), - NewLegacyQueryServer(querier, am.ss, am.keeper), + NewLegacyQueryServer(querier, am.ss, am.keeper, am.cdc, am.storeKey), ) m := keeper.NewMigrator(am.keeper, am.ss) diff --git a/custom/staking/query_server.go b/custom/staking/query_server.go index b4c58f888..767df219a 100644 --- a/custom/staking/query_server.go +++ b/custom/staking/query_server.go @@ -2,14 +2,22 @@ package staking import ( "context" + "strconv" + "strings" "cosmossdk.io/math" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" legacytypes "github.com/classic-terra/core/v4/custom/staking/types" legacyupgrade "github.com/classic-terra/core/v4/custom/upgrade/legacy" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // LegacyQueryServer wraps the staking QueryServer and sets legacy parameters for pre-upgrade height queries @@ -18,18 +26,29 @@ type LegacyQueryServer struct { stakingtypes.QueryServer keeper *keeper.Keeper legacySubspace paramtypes.Subspace + cdc codec.BinaryCodec + storeKey storetypes.StoreKey } -// NewLegacyQueryServer creates a new LegacyQueryServer instance +// NewLegacyQueryServer creates a new LegacyQueryServer instance. +// +// `cdc` and `storeKey` are required for the pre-v5-staking-migration +// ValidatorDelegations fallback path, which scans the primary DelegationKey +// (0x31) prefix directly when the SDK's reverse-index (0x71) hasn't been +// backfilled at the queried height. func NewLegacyQueryServer( originalServer stakingtypes.QueryServer, legacySubspace paramtypes.Subspace, keeper *keeper.Keeper, + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, ) stakingtypes.QueryServer { return &LegacyQueryServer{ QueryServer: originalServer, keeper: keeper, legacySubspace: legacySubspace, + cdc: cdc, + storeKey: storeKey, } } @@ -111,7 +130,92 @@ func (q *LegacyQueryServer) Validator(ctx context.Context, req *stakingtypes.Que } func (q *LegacyQueryServer) ValidatorDelegations(ctx context.Context, req *stakingtypes.QueryValidatorDelegationsRequest) (*stakingtypes.QueryValidatorDelegationsResponse, error) { - return q.QueryServer.ValidatorDelegations(q.ensureLegacyParams(ctx), req) + ensuredCtx := q.ensureLegacyParams(ctx) + sdkCtx := sdk.UnwrapSDKContext(ensuredCtx) + if legacyupgrade.IsPreStakingV5(sdkCtx.ChainID(), sdkCtx.BlockHeight()) { + return q.validatorDelegationsLegacy(sdkCtx, req) + } + return q.QueryServer.ValidatorDelegations(ensuredCtx, req) +} + +// validatorDelegationsLegacy reproduces cosmos-sdk's unexported +// `getValidatorDelegationsLegacy` (x/staking/keeper/grpc_query.go): it scans +// the primary DelegationKey (0x31) prefix and filters by validator. Used for +// archive queries at heights before the v4→v5 staking migration backfilled the +// DelegationByValIndexKey (0x71) reverse-index that the SDK's default +// ValidatorDelegations now relies on. +func (q *LegacyQueryServer) validatorDelegationsLegacy( + ctx sdk.Context, req *stakingtypes.QueryValidatorDelegationsRequest, +) (*stakingtypes.QueryValidatorDelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.ValidatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") + } + if _, err := sdk.ValAddressFromBech32(req.ValidatorAddr); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + store := ctx.KVStore(q.storeKey) + delStore := prefix.NewStore(store, stakingtypes.DelegationKey) + + dels, pageRes, err := query.GenericFilteredPaginate( + q.cdc, delStore, req.Pagination, + func(_ []byte, d *stakingtypes.Delegation) (*stakingtypes.Delegation, error) { + if !strings.EqualFold(d.GetValidatorAddr(), req.ValidatorAddr) { + return nil, nil + } + return d, nil + }, + func() *stakingtypes.Delegation { return &stakingtypes.Delegation{} }, + ) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + delegations := make(stakingtypes.Delegations, 0, len(dels)) + for _, d := range dels { + delegations = append(delegations, *d) + } + + delResps, err := q.delegationsToDelegationResponses(ctx, delegations) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &stakingtypes.QueryValidatorDelegationsResponse{ + DelegationResponses: delResps, + Pagination: pageRes, + }, nil +} + +// delegationsToDelegationResponses mirrors the unexported helper of the same +// name in cosmos-sdk's staking keeper: it looks up the validator for each +// delegation and converts shares to bonded balance. +func (q *LegacyQueryServer) delegationsToDelegationResponses( + ctx sdk.Context, delegations stakingtypes.Delegations, +) (stakingtypes.DelegationResponses, error) { + bondDenom, err := q.keeper.BondDenom(ctx) + if err != nil { + return nil, err + } + resps := make(stakingtypes.DelegationResponses, 0, len(delegations)) + for _, d := range delegations { + valAddr, err := sdk.ValAddressFromBech32(d.GetValidatorAddr()) + if err != nil { + return nil, err + } + val, err := q.keeper.GetValidator(ctx, valAddr) + if err != nil { + return nil, err + } + balance := val.TokensFromShares(d.Shares).TruncateInt() + resps = append(resps, stakingtypes.NewDelegationResp( + d.GetDelegatorAddr(), d.GetValidatorAddr(), d.Shares, sdk.NewCoin(bondDenom, balance), + )) + } + return resps, nil } func (q *LegacyQueryServer) ValidatorUnbondingDelegations(ctx context.Context, req *stakingtypes.QueryValidatorUnbondingDelegationsRequest) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) { @@ -147,7 +251,44 @@ func (q *LegacyQueryServer) DelegatorValidator(ctx context.Context, req *staking } func (q *LegacyQueryServer) HistoricalInfo(ctx context.Context, req *stakingtypes.QueryHistoricalInfoRequest) (*stakingtypes.QueryHistoricalInfoResponse, error) { - return q.QueryServer.HistoricalInfo(q.ensureLegacyParams(ctx), req) + ensuredCtx := q.ensureLegacyParams(ctx) + sdkCtx := sdk.UnwrapSDKContext(ensuredCtx) + if legacyupgrade.IsPreStakingV5(sdkCtx.ChainID(), sdkCtx.BlockHeight()) { + return q.historicalInfoLegacy(sdkCtx, req) + } + return q.QueryServer.HistoricalInfo(ensuredCtx, req) +} + +// historicalInfoLegacy reads HistoricalInfo using the pre-v5-staking-migration +// key encoding: prefix 0x50 followed by the ASCII-decimal height string. The +// v5 migration (cosmos-sdk@v0.53.6/x/staking/migrations/v5/store.go:39) re-keys +// every entry to a big-endian uint64; before that migration ran (block +// 28214400 on Columbus, 28917279 on Rebel-2) IAVL state contains only the old +// string-format keys, so the SDK's GetHistoricalInfo — which constructs the +// new binary key — misses and returns NotFound. +func (q *LegacyQueryServer) historicalInfoLegacy( + ctx sdk.Context, req *stakingtypes.QueryHistoricalInfoRequest, +) (*stakingtypes.QueryHistoricalInfoResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + if req.Height < 0 { + return nil, status.Error(codes.InvalidArgument, "height cannot be negative") + } + + store := ctx.KVStore(q.storeKey) + legacyKey := append([]byte{}, stakingtypes.HistoricalInfoKey...) + legacyKey = append(legacyKey, []byte(strconv.FormatInt(req.Height, 10))...) + bz := store.Get(legacyKey) + if bz == nil { + return nil, status.Errorf(codes.NotFound, "historical info for height %d not found", req.Height) + } + + var hi stakingtypes.HistoricalInfo + if err := q.cdc.Unmarshal(bz, &hi); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return &stakingtypes.QueryHistoricalInfoResponse{Hist: &hi}, nil } func (q *LegacyQueryServer) Pool(ctx context.Context, req *stakingtypes.QueryPoolRequest) (*stakingtypes.QueryPoolResponse, error) { diff --git a/custom/staking/query_server_test.go b/custom/staking/query_server_test.go new file mode 100644 index 000000000..65b249a80 --- /dev/null +++ b/custom/staking/query_server_test.go @@ -0,0 +1,407 @@ +package staking_test + +import ( + "encoding/binary" + "strconv" + "testing" + "time" + + "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" + apptesting "github.com/classic-terra/core/v4/app/testing" + customstaking "github.com/classic-terra/core/v4/custom/staking" + "github.com/classic-terra/core/v4/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/testutil" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/suite" +) + +type ValidatorDelegationsSuite struct { + apptesting.KeeperTestHelper +} + +func TestValidatorDelegationsSuite(t *testing.T) { + suite.Run(t, new(ValidatorDelegationsSuite)) +} + +// seedValidatorWithDelegations creates `numVals` validators (so no single one +// exceeds the 20% voting-power cap enforced by the custom staking hook), +// then has `numDels` distinct delegators each delegate 1_000_000 uluna to the +// FIRST validator. Returns that validator's address. +func (s *ValidatorDelegationsSuite) seedValidatorWithDelegations(numVals, numDels int) sdk.ValAddress { + // Pre-fund the not-bonded-pool with the total self-stake so + // TestingUpdateValidator finds the tokens it expects. + valOwners := s.RandomAccountAddresses(numVals) + for _, o := range valOwners { + s.FundAcc(o, sdk.NewCoins(sdk.NewInt64Coin("uluna", 1_000_000))) + s.Require().NoError(s.App.BankKeeper.DelegateCoinsFromAccountToModule( + s.Ctx, o, stakingtypes.NotBondedPoolName, + sdk.NewCoins(sdk.NewInt64Coin("uluna", 1_000_000)), + )) + } + + valAddrs := simtestutil.ConvertAddrsToValAddrs(valOwners) + pks := simtestutil.CreateTestPubKeys(numVals) + vals := make([]stakingtypes.Validator, numVals) + for i := range vals { + v := testutil.NewValidator(s.T(), valAddrs[i], pks[i]) + v, _ = v.AddTokensFromDel(math.NewInt(1_000_000)) + v = stakingkeeper.TestingUpdateValidator(s.App.StakingKeeper, s.Ctx, v, true) + // Distribution rewards state is normally initialized in CreateValidator; + // TestingUpdateValidator skips that path, so do it manually. + s.Require().NoError(s.App.DistrKeeper.Hooks().AfterValidatorCreated(s.Ctx, valAddrs[i])) + vals[i] = v + } + + // Delegators all stake to vals[0]. Each delegation is 1M, total stake + // across the chain ends up at numVals*1M + numDels*1M; the cap-hook + // requires vals[0] tokens / total <= 20%, so caller should pick numVals + // large enough to keep the ratio under the threshold. + addrDels := s.RandomAccountAddresses(numDels) + for _, d := range addrDels { + s.FundAcc(d, sdk.NewCoins(sdk.NewInt64Coin("uluna", 1_000_000))) + _, err := s.App.StakingKeeper.Delegate(s.Ctx, d, math.NewInt(1_000_000), stakingtypes.Unbonded, vals[0], true) + s.Require().NoError(err) + } + + _, err := s.App.StakingKeeper.ApplyAndReturnValidatorSetUpdates(s.Ctx) + s.Require().NoError(err) + + return valAddrs[0] +} + +// dropReverseIndex deletes every entry under the staking module's +// DelegationByValIndexKey (0x71) prefix. +// +// This simulates the IAVL state at heights *before* the cosmos-sdk staking +// v4→v5 migration ran (the migration that backfills 0x71 from the primary +// DelegationKey 0x31). Pre-migration archive state contains delegations under +// 0x31 but nothing under 0x71 — which is what causes the empty query result +// reported on the public archive LCDs at heights below 28214400. +func (s *ValidatorDelegationsSuite) dropReverseIndex() { + storeKey := s.App.GetKey(stakingtypes.StoreKey) + store := s.Ctx.KVStore(storeKey) + + iter := storetypes.KVStorePrefixIterator(store, stakingtypes.DelegationByValIndexKey) + defer iter.Close() + + var keys [][]byte + for ; iter.Valid(); iter.Next() { + k := make([]byte, len(iter.Key())) + copy(k, iter.Key()) + keys = append(keys, k) + } + for _, k := range keys { + store.Delete(k) + } +} + +// TestValidatorDelegations_ReproducesArchiveBug reproduces the symptom seen on +// public Terra Classic archive LCDs at pre-v5-staking-migration heights: +// ValidatorDelegations returns an empty list because the SDK query iterates +// over the 0x71 reverse-index, which has no entries in pre-migration IAVL +// state. +// +// Pre-fix: expect empty result (bug present). +// Post-fix: expect populated result (fix routes to a primary-key scan when the +// +// queried height is below MainnetStakingV5Height for Columbus). +func (s *ValidatorDelegationsSuite) TestValidatorDelegations_ReproducesArchiveBug() { + s.Setup(s.T(), types.ColumbusChainID) + + // 30 validators × 1M + 5 × 1M = 35M; vals[0] has 6M = 17.1% < 20% cap. + valAddr := s.seedValidatorWithDelegations(30, 5) + + // Build the LegacyQueryServer the same way custom/staking/module.go does. + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + ) + + req := &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: valAddr.String()} + + // Use a query height above the v8 upgrade height so ensureLegacyParams + // takes the LegacyHandlingNone path and doesn't try to read non-existent + // legacy params from the subspace. + queryCtx := s.Ctx.WithBlockHeight(28214399) + + // Sanity: with the reverse-index intact the query returns all 5 delegations. + resp, err := qs.ValidatorDelegations(queryCtx, req) + s.Require().NoError(err) + s.Require().Len(resp.DelegationResponses, 5, "sanity: index intact, should return all delegations") + + // Simulate pre-migration archive state by wiping the 0x71 reverse-index. + s.dropReverseIndex() + + resp, err = qs.ValidatorDelegations(queryCtx, req) + s.Require().NoError(err) + + // THIS is the assertion that fails before the fix and passes after it. + s.Require().Len( + resp.DelegationResponses, 5, + "pre-migration height must still return delegations (regression of archive-LCD bug)", + ) +} + +// TestValidatorDelegations_LegacyPathKeepsLegacyParams covers the overlap +// between the pre-staking-v5 fallback window and the older staking-param legacy +// window. The fallback still needs ensureLegacyParams before it builds +// DelegationResponse balances. +func (s *ValidatorDelegationsSuite) TestValidatorDelegations_LegacyPathKeepsLegacyParams() { + s.Setup(s.T(), types.ColumbusChainID) + + valAddr := s.seedValidatorWithDelegations(30, 1) + + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + ) + + params, err := s.App.StakingKeeper.GetParams(s.Ctx) + s.Require().NoError(err) + + legacyParams := params + legacyParams.BondDenom = types.MicroLunaDenom + ss.SetParamSet(s.Ctx, &legacyParams) + + currentParams := params + currentParams.BondDenom = "stake" + s.Require().NoError(s.App.StakingKeeper.SetParams(s.Ctx, currentParams)) + + s.dropReverseIndex() + + req := &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: valAddr.String()} + queryCtx := s.Ctx.WithBlockHeight(18302999) + resp, err := qs.ValidatorDelegations(queryCtx, req) + s.Require().NoError(err) + s.Require().Len(resp.DelegationResponses, 1) + s.Require().Equal(types.MicroLunaDenom, resp.DelegationResponses[0].Balance.Denom) +} + +// TestValidatorDelegations_LegacyPathPaginates exercises the legacy-iteration +// path through pagination: it walks all delegations across multiple pages and +// asserts that (a) every page returns delegations matching only the requested +// validator, (b) the union of pages equals the full delegation set, and +// (c) walking terminates with an empty next_key. +func (s *ValidatorDelegationsSuite) TestValidatorDelegations_LegacyPathPaginates() { + s.Setup(s.T(), types.ColumbusChainID) + + totalDelegators := 12 + valAddr := s.seedValidatorWithDelegations(30, totalDelegators) + + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + ) + + // Drop the reverse-index and force the legacy path. + s.dropReverseIndex() + queryCtx := s.Ctx.WithBlockHeight(28214399) + + pageSize := uint64(5) + seen := make(map[string]struct{}) + var nextKey []byte + + for pages := 0; pages < 10; pages++ { + req := &stakingtypes.QueryValidatorDelegationsRequest{ + ValidatorAddr: valAddr.String(), + Pagination: &query.PageRequest{Key: nextKey, Limit: pageSize}, + } + resp, err := qs.ValidatorDelegations(queryCtx, req) + s.Require().NoError(err) + s.Require().NotNil(resp.Pagination) + + for _, d := range resp.DelegationResponses { + s.Require().Equal(valAddr.String(), d.Delegation.ValidatorAddress, + "page must only contain delegations for the queried validator") + seen[d.Delegation.DelegatorAddress] = struct{}{} + } + + if len(resp.Pagination.NextKey) == 0 { + break + } + nextKey = resp.Pagination.NextKey + } + + s.Require().Len(seen, totalDelegators, + "paginated walk must surface every delegation exactly once") +} + +// TestValidatorDelegations_PostMigrationUsesIndex ensures the fix doesn't change +// behavior at chain-head heights: with the reverse-index intact and queried at +// a post-v5-staking-migration height, the SDK's normal indexed path runs. +// (We assert this indirectly by dropping the index at a post-migration height +// and confirming the wrapper does NOT fall back to legacy iteration — i.e. +// returns empty, just as the unwrapped SDK query would.) +func (s *ValidatorDelegationsSuite) TestValidatorDelegations_PostMigrationUsesIndex() { + s.Setup(s.T(), types.ColumbusChainID) + + valAddr := s.seedValidatorWithDelegations(30, 5) + + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + ) + + req := &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: valAddr.String()} + postCtx := s.Ctx.WithBlockHeight(28214400) + + s.dropReverseIndex() + resp, err := qs.ValidatorDelegations(postCtx, req) + s.Require().NoError(err) + s.Require().Len( + resp.DelegationResponses, 0, + "at post-migration heights the legacy fallback must NOT trigger", + ) +} + +// rekeyHistoricalInfoToLegacyFormat takes every entry in the staking store +// under the HistoricalInfoKey (0x50) prefix, deletes the new big-endian +// uint64 key form, and re-writes the same value under the pre-v5-migration +// key form (`0x50 || strconv.FormatInt(height, 10)`). This simulates the +// IAVL state at heights *before* the v5 staking migration ran. +// +// Mirrors cosmos-sdk@v0.53.6/x/staking/migrations/v5/store.go:39 in reverse. +func (s *ValidatorDelegationsSuite) rekeyHistoricalInfoToLegacyFormat() { + storeKey := s.App.GetKey(stakingtypes.StoreKey) + store := s.Ctx.KVStore(storeKey) + + iter := storetypes.KVStorePrefixIterator(store, stakingtypes.HistoricalInfoKey) + defer iter.Close() + + type entry struct { + oldKey []byte + height int64 + value []byte + } + var entries []entry + for ; iter.Valid(); iter.Next() { + fullKey := append([]byte{}, iter.Key()...) // already includes 0x50 prefix + val := append([]byte{}, iter.Value()...) + // new format: 0x50 || 8-byte big-endian height + if len(fullKey) != 1+8 { + continue + } + h := int64(binary.BigEndian.Uint64(fullKey[1:])) + entries = append(entries, entry{oldKey: fullKey, height: h, value: val}) + } + + for _, e := range entries { + store.Delete(e.oldKey) + legacyKey := append([]byte{}, stakingtypes.HistoricalInfoKey...) + legacyKey = append(legacyKey, []byte(strconv.FormatInt(e.height, 10))...) + store.Set(legacyKey, e.value) + } +} + +// TestHistoricalInfo_ReproducesArchiveBug verifies the parallel bug to +// ValidatorDelegations: ValidatorDelegations relies on the 0x71 reverse-index +// that the v5 migration backfills, while HistoricalInfo relies on +// big-endian-uint64 keys that the same migration writes (re-keying from +// ASCII-decimal). On pre-migration archive heights the IAVL state holds only +// the old string-format keys, so the SDK's GetHistoricalInfo returns NotFound +// — confirmed live against archive-lcd.galacticshift.io at height 28214399 +// vs 28214400. +// +// Pre-fix: expect NotFound (bug present). +// Post-fix: expect HistoricalInfo populated (legacy reader hits string keys). +func (s *ValidatorDelegationsSuite) TestHistoricalInfo_ReproducesArchiveBug() { + s.Setup(s.T(), types.ColumbusChainID) + s.seedValidatorWithDelegations(30, 1) // populate the validator set + + // Stage a HistoricalInfo entry. Use a height the staking module would + // not currently retain via its own pruning — we're driving the state + // directly to validate the read path, not exercising BeginBlocker. + const targetHeight = int64(28210000) + vals, err := s.App.StakingKeeper.GetAllValidators(s.Ctx) + s.Require().NoError(err) + s.Require().NotEmpty(vals, "test setup should bond at least one validator") + + hi := stakingtypes.HistoricalInfo{ + Header: cmtproto.Header{ + ChainID: types.ColumbusChainID, + Height: targetHeight, + Time: time.Unix(1700000000, 0).UTC(), + }, + Valset: vals, + } + s.Require().NoError(s.App.StakingKeeper.SetHistoricalInfo(s.Ctx, targetHeight, &hi)) + + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + ) + + req := &stakingtypes.QueryHistoricalInfoRequest{Height: targetHeight} + + // Sanity: at a post-migration height the SDK's indexed path reads + // big-endian-uint64 keys (which is what SetHistoricalInfo just wrote). + postCtx := s.Ctx.WithBlockHeight(28214400) + resp, err := qs.HistoricalInfo(postCtx, req) + s.Require().NoError(err, "sanity: post-migration indexed read should succeed") + s.Require().NotNil(resp.Hist) + s.Require().Equal(targetHeight, resp.Hist.Header.Height) + + // Simulate pre-migration archive state: rewrite every 0x50 entry from + // big-endian to string-format keys. + s.rekeyHistoricalInfoToLegacyFormat() + + preCtx := s.Ctx.WithBlockHeight(28214399) + resp, err = qs.HistoricalInfo(preCtx, req) + + // THIS is the assertion that fails before the fix and passes after it. + s.Require().NoError(err, "pre-migration height must still return historical info (regression of HistoricalInfo bug)") + s.Require().NotNil(resp.Hist) + s.Require().Equal(targetHeight, resp.Hist.Header.Height) + s.Require().Equal(types.ColumbusChainID, resp.Hist.Header.ChainID) +} + +// TestHistoricalInfo_PostMigrationUsesIndex confirms the legacy reader is NOT +// used at chain-head heights: at BlockHeight = MainnetStakingV5Height the +// wrapper goes through the SDK's GetHistoricalInfo, which expects the binary +// key format. After we rewrite to the legacy format, the post-migration call +// must miss — proving the height gate short-circuits correctly. +func (s *ValidatorDelegationsSuite) TestHistoricalInfo_PostMigrationUsesIndex() { + s.Setup(s.T(), types.ColumbusChainID) + s.seedValidatorWithDelegations(30, 1) + + const targetHeight = int64(28210000) + vals, err := s.App.StakingKeeper.GetAllValidators(s.Ctx) + s.Require().NoError(err) + + hi := stakingtypes.HistoricalInfo{ + Header: cmtproto.Header{ChainID: types.ColumbusChainID, Height: targetHeight, Time: time.Unix(1700000000, 0).UTC()}, + Valset: vals, + } + s.Require().NoError(s.App.StakingKeeper.SetHistoricalInfo(s.Ctx, targetHeight, &hi)) + + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + ) + + // Rewrite into legacy string-format keys; with the height gate at + // post-migration the wrapper must NOT consult the legacy reader. + s.rekeyHistoricalInfoToLegacyFormat() + + postCtx := s.Ctx.WithBlockHeight(28214400) + _, err = qs.HistoricalInfo(postCtx, &stakingtypes.QueryHistoricalInfoRequest{Height: targetHeight}) + s.Require().Error(err, "at post-migration heights the legacy reader must NOT be consulted") +} diff --git a/custom/upgrade/legacy/height.go b/custom/upgrade/legacy/height.go index 4f929b5ce..5ce045c87 100644 --- a/custom/upgrade/legacy/height.go +++ b/custom/upgrade/legacy/height.go @@ -9,6 +9,19 @@ const ( TestnetUpgradeHeightV2 = int64(19354000) // rebel-2 testnet upgrade height to v8 LegacyUpgradeHeightV1 = int64(0) // This is not included in the local testing as it would need v3 as a basis LegacyUpgradeHeightV2 = int64(70) // Local testing upgrade height to v8 (using upgrade-test-multi.sh script) + + // MainnetStakingV5Height / TestnetStakingV5Height: heights at which the + // cosmos-sdk staking v4→v5 migration ran on each chain. That migration + // backfills the DelegationByValIndexKey (0x71) reverse-index from the + // primary DelegationKey (0x31). Below these heights there are no entries + // under 0x71, so the SDK's ValidatorDelegations query returns empty + // unless we route the read through the primary key. + // + // Columbus boundary observed empirically on archive LCDs. + // Rebel-2 boundary corresponds to the v14 (sdk-53 + ibc-v2) upgrade + // scheduled by proposal #165 (luncdash.com/governance/165). + MainnetStakingV5Height = int64(28214400) + TestnetStakingV5Height = int64(28917279) ) // LegacyHandlingVersion represents different versions of legacy handling @@ -23,6 +36,24 @@ const ( LegacyHandlingV2 ) +// IsPreStakingV5 reports whether `blockHeight` falls in the window where the +// cosmos-sdk staking v4→v5 reverse-index (DelegationByValIndexKey, 0x71) had +// not yet been backfilled. ValidatorDelegations queries on these heights must +// fall back to a primary-key (DelegationKey, 0x31) iteration; the indexed path +// returns empty. +func IsPreStakingV5(chainID string, blockHeight int64) bool { + if blockHeight <= 0 { + return false + } + switch chainID { + case core.ColumbusChainID: + return blockHeight < MainnetStakingV5Height + case core.RebelChainID: + return TestnetStakingV5Height > 0 && blockHeight < TestnetStakingV5Height + } + return false +} + // GetLegacyHandling returns the appropriate legacy handling version based on the chain ID and block height func GetLegacyHandling(chainID string, blockHeight int64) LegacyHandlingVersion { if blockHeight == 0 { diff --git a/custom/wasm/keeper/handler_plugin.go b/custom/wasm/keeper/handler_plugin.go index 8bf5ff3bc..241070da4 100644 --- a/custom/wasm/keeper/handler_plugin.go +++ b/custom/wasm/keeper/handler_plugin.go @@ -1,134 +1,44 @@ package keeper import ( - errorsmod "cosmossdk.io/errors" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" wasmvmtypes "github.com/CosmWasm/wasmvm/v3/types" - taxkeeper "github.com/classic-terra/core/v4/x/tax/keeper" taxtypes "github.com/classic-terra/core/v4/x/tax/types" - taxexemptionkeeper "github.com/classic-terra/core/v4/x/taxexemption/keeper" - treasurykeeper "github.com/classic-terra/core/v4/x/treasury/keeper" - "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" bankKeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" ) -// msgEncoder is an extension point to customize encodings -type msgEncoder interface { - // Encode converts wasmvm message to n cosmos message types - Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Msg, error) -} - -// MessageRouter ADR 031 request type routing -type MessageRouter interface { - Handler(msg sdk.Msg) baseapp.MsgServiceHandler -} - -// SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed. -type SDKMessageHandler struct { - router MessageRouter - encoders msgEncoder - treasuryKeeper treasurykeeper.Keeper - accountKeeper authkeeper.AccountKeeper - bankKeeper bankKeeper.Keeper - taxexemptionKeeper taxexemptionkeeper.Keeper - taxKeeper taxkeeper.Keeper -} - func NewMessageHandler( - router MessageRouter, + router wasmkeeper.MessageRouter, + wasmKeeper wasmtypes.IBCContractKeeper, ics4Wrapper wasmtypes.ICS4Wrapper, - channelKeeper wasmtypes.ChannelKeeper, + channelKeeperV2 wasmtypes.ChannelKeeperV2, bankKeeper bankKeeper.Keeper, - taxexemptionKeeper taxexemptionkeeper.Keeper, - treasuryKeeper treasurykeeper.Keeper, - accountKeeper authkeeper.AccountKeeper, - taxKeeper taxkeeper.Keeper, - unpacker codectypes.AnyUnpacker, + cdc codec.Codec, portSource wasmtypes.ICS20TransferPortSource, customEncoders ...*wasmkeeper.MessageEncoders, ) wasmkeeper.Messenger { - encoders := wasmkeeper.DefaultEncoders(unpacker, portSource) + encoders := wasmkeeper.DefaultEncoders(cdc, portSource) for _, e := range customEncoders { encoders = encoders.Merge(e) } + sdkHandler := wasmkeeper.NewSDKMessageHandler(cdc, router, encoders) + wrappedSDKHandler := wasmkeeper.MessageHandlerFunc(func( + ctx sdk.Context, + contractAddr sdk.AccAddress, + contractIBCPortID string, + msg wasmvmtypes.CosmosMsg, + ) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, err error) { + ctx = ctx.WithValue(taxtypes.ContextKeyTaxReverseCharge, true) + return sdkHandler.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) + }) return wasmkeeper.NewMessageHandlerChain( - NewSDKMessageHandler(router, encoders, taxexemptionKeeper, treasuryKeeper, accountKeeper, bankKeeper, taxKeeper), + wrappedSDKHandler, + wasmkeeper.NewIBCRawPacketHandler(ics4Wrapper, wasmKeeper), + wasmkeeper.NewIBC2RawPacketHandler(channelKeeperV2), wasmkeeper.NewBurnCoinMessageHandler(bankKeeper), ) } - -func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder, taxexemptionKeeper taxexemptionkeeper.Keeper, treasuryKeeper treasurykeeper.Keeper, accountKeeper authkeeper.AccountKeeper, bankKeeper bankKeeper.Keeper, taxKeeper taxkeeper.Keeper) SDKMessageHandler { - return SDKMessageHandler{ - router: router, - encoders: encoders, - treasuryKeeper: treasuryKeeper, - taxexemptionKeeper: taxexemptionKeeper, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, - taxKeeper: taxKeeper, - } -} - -func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, msgs [][]*codectypes.Any, err error) { - sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg) - if err != nil { - return nil, nil, nil, err - } - - // contract handling is ALWAYS reverse charged - ctx = ctx.WithValue(taxtypes.ContextKeyTaxReverseCharge, true) - - for _, sdkMsg := range sdkMsgs { - // Charge tax on result msg - // we set simulate to false here as it is not available and we don't need to - // increase the tax amount for simulation inside of wasm - res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg) - if err != nil { - return nil, nil, nil, err - } - // append data - data = append(data, res.Data) - // append events - sdkEvents := make([]sdk.Event, len(res.Events)) - for i := range res.Events { - sdkEvents[i] = sdk.Event(res.Events[i]) - } - events = append(events, sdkEvents...) - // no additional msg responses to return from SDK handler - } - return events, data, nil, nil -} - -func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) { - if msgValidate, ok := msg.(sdk.HasValidateBasic); ok { - if err := msgValidate.ValidateBasic(); err != nil { - return nil, err - } - } - // make sure this account can send it - if msgSigners, ok := msg.(sdk.LegacyMsg); ok { - for _, acct := range msgSigners.GetSigners() { - if !acct.Equals(contractAddr) { - return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "contract doesn't have permission") - } - } - } - - // find the handler and execute it - if handler := h.router.Handler(msg); handler != nil { - // ADR 031 request type routing - msgResult, err := handler(ctx, msg) - return msgResult, err - } - // legacy sdk.Msg routing - // Assuming that the app developer has migrated all their Msgs to - // proto messages and has registered all `Msg services`, then this - // path should never be called, because all those Msgs should be - // registered within the `msgServiceRouter` already. - return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) -} diff --git a/custom/wasm/query_server.go b/custom/wasm/query_server.go index 264fa3e04..26b146723 100644 --- a/custom/wasm/query_server.go +++ b/custom/wasm/query_server.go @@ -11,16 +11,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var ( - LegacyParamStoreKeyUploadAccess = []byte("uploadAccess") - LegacyParamStoreKeyInstantiateAccess = []byte("instantiateAccess") -) - -// LegacyWasmParams is a wrapper around wasmtypes.Params that implements ParamSet -type LegacyWasmParams struct { - wasmtypes.Params -} - // LegacyQueryServer wraps the wasm QueryServer and sets legacy parameters for pre-upgrade height queries type LegacyQueryServer struct { // Embed the original query server to inherit all methods diff --git a/custom/wasm/simulation/operations.go b/custom/wasm/simulation/operations.go deleted file mode 100644 index 797b74b20..000000000 --- a/custom/wasm/simulation/operations.go +++ /dev/null @@ -1,47 +0,0 @@ -package simulation - -import ( - "github.com/CosmWasm/wasmd/x/wasm/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -// Simulation operation weights constants -// -//nolint:gosec -const ( - OpWeightMsgStoreCode = "op_weight_msg_store_code" - OpWeightMsgInstantiateContract = "op_weight_msg_instantiate_contract" - OpWeightMsgExecuteContract = "op_weight_msg_execute_contract" - OpWeightMsgUpdateAdmin = "op_weight_msg_update_admin" - OpWeightMsgClearAdmin = "op_weight_msg_clear_admin" - OpWeightMsgMigrateContract = "op_weight_msg_migrate_contract" - OpReflectContractPath = "op_reflect_contract_path" -) - -// WasmKeeper is a subset of the wasm keeper used by simulations -type WasmKeeper interface { - GetParams(ctx sdk.Context) types.Params - GetAuthority() string - IterateCodeInfos(ctx sdk.Context, cb func(uint64, types.CodeInfo) bool) - IterateContractInfo(ctx sdk.Context, cb func(sdk.AccAddress, types.ContractInfo) bool) - QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) - PeekAutoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 -} -type BankKeeper interface { - simulation.BankKeeper - IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool -} - -// WeightedOperations returns all the operations from the module with their respective weights -func WeightedOperations( - simstate *module.SimulationState, - ak types.AccountKeeper, - bk BankKeeper, - wasmKeeper WasmKeeper, -) simulation.WeightedOperations { - // SDK v0.50 migration: disable custom wasm simulation ops for now - // to keep the application buildable. Re-enable with updated APIs later. - return simulation.WeightedOperations{} -} diff --git a/go.mod b/go.mod index 469b79520..4cf698979 100644 --- a/go.mod +++ b/go.mod @@ -209,16 +209,16 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/lib/pq v1.10.9 // indirect + github.com/lib/pq v1.12.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/minio/highwayhash v1.0.3 // indirect + github.com/minio/highwayhash v1.0.4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect + github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -227,7 +227,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/cors v1.11.1 // indirect github.com/rs/zerolog v1.34.0 // indirect - github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/sasha-s/go-deadlock v0.3.9 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/viper v1.21.0 github.com/subosito/gotenv v1.6.0 // indirect diff --git a/go.sum b/go.sum index 3c325daeb..f32ce01e0 100644 --- a/go.sum +++ b/go.sum @@ -1265,8 +1265,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= +github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linxGnu/grocksdb v1.10.1 h1:YX6gUcKvSC3d0s9DaqgbU+CRkZHzlELgHu1Z/kmtslg= @@ -1302,8 +1302,8 @@ github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFS github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= -github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/minio/highwayhash v1.0.4 h1:asJizugGgchQod2ja9NJlGOWq4s7KsAWr5XUc9Clgl4= +github.com/minio/highwayhash v1.0.4/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -1387,8 +1387,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -1476,8 +1476,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= -github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/sasha-s/go-deadlock v0.3.9 h1:fiaT9rB7g5sr5ddNZvlwheclN9IP86eFW9WgqlEQV+w= +github.com/sasha-s/go-deadlock v0.3.9/go.mod h1:KuZj51ZFmx42q/mPaYbRk0P1xcwe697zsJKE03vD4/Y= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM= github.com/shamaton/msgpack/v2 v2.2.3/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= diff --git a/scripts/upgrade-test.sh b/scripts/upgrade-test.sh index 848bf60d4..848cf3d03 100755 --- a/scripts/upgrade-test.sh +++ b/scripts/upgrade-test.sh @@ -5,12 +5,12 @@ FORK=${FORK:-"false"} # $(curl --silent "https://api.github.com/repos/classic-terra/core/releases/latest" | jq -r '.tag_name') -OLD_VERSION=${OLD_VERSION:-v3.6.2} +OLD_VERSION=${OLD_VERSION:-v4.0.0} HOME=mytestnet ROOT=$(pwd) DENOM=uluna CHAIN_ID=localterra -SOFTWARE_UPGRADE_NAME=${SOFTWARE_UPGRADE_NAME:-"v14_1"} +SOFTWARE_UPGRADE_NAME=${SOFTWARE_UPGRADE_NAME:-"v14_2"} ADDITIONAL_PRE_SCRIPTS=${ADDITIONAL_PRE_SCRIPTS:-""} ADDITIONAL_AFTER_SCRIPTS=${ADDITIONAL_AFTER_SCRIPTS:-""} GAS_PRICE=${GAS_PRICE:-"30uluna"} diff --git a/tests/interchaintest/go.mod b/tests/interchaintest/go.mod index 8dc8e2357..d2114a32f 100644 --- a/tests/interchaintest/go.mod +++ b/tests/interchaintest/go.mod @@ -162,12 +162,12 @@ require ( github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/lib/pq v1.10.9 // indirect + github.com/lib/pq v1.12.0 // indirect github.com/linxGnu/grocksdb v1.10.1 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/minio/highwayhash v1.0.3 // indirect + github.com/minio/highwayhash v1.0.4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/moby v27.5.1+incompatible // indirect @@ -179,7 +179,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect + github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -193,7 +193,7 @@ require ( github.com/rs/cors v1.11.1 // indirect github.com/rs/zerolog v1.34.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/sasha-s/go-deadlock v0.3.9 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect diff --git a/tests/interchaintest/go.sum b/tests/interchaintest/go.sum index d2532251c..5c6dd8b9b 100644 --- a/tests/interchaintest/go.sum +++ b/tests/interchaintest/go.sum @@ -1339,8 +1339,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= +github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linxGnu/grocksdb v1.10.1 h1:YX6gUcKvSC3d0s9DaqgbU+CRkZHzlELgHu1Z/kmtslg= @@ -1376,8 +1376,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= -github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/minio/highwayhash v1.0.4 h1:asJizugGgchQod2ja9NJlGOWq4s7KsAWr5XUc9Clgl4= +github.com/minio/highwayhash v1.0.4/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -1474,8 +1474,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -1579,8 +1579,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= -github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/sasha-s/go-deadlock v0.3.9 h1:fiaT9rB7g5sr5ddNZvlwheclN9IP86eFW9WgqlEQV+w= +github.com/sasha-s/go-deadlock v0.3.9/go.mod h1:KuZj51ZFmx42q/mPaYbRk0P1xcwe697zsJKE03vD4/Y= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM= github.com/shamaton/msgpack/v2 v2.2.3/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= From 4689c4477274c8a6562adeb5d08a7fc026d8a71d Mon Sep 17 00:00:00 2001 From: Khanh Hoa Date: Sun, 26 Apr 2026 11:39:59 +0700 Subject: [PATCH 2/5] fix lint --- app/app.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/app.go b/app/app.go index 512948d00..9f26c8e5e 100644 --- a/app/app.go +++ b/app/app.go @@ -18,7 +18,6 @@ import ( "github.com/classic-terra/core/v4/app/keepers" appmempool "github.com/classic-terra/core/v4/app/mempool" terraappparams "github.com/classic-terra/core/v4/app/params" - // upgrades "github.com/classic-terra/core/v4/app/upgrades" // v9 had been used by tax2gas and has to be skipped @@ -43,7 +42,6 @@ import ( v8_1 "github.com/classic-terra/core/v4/app/upgrades/v8_1" v8_2 "github.com/classic-terra/core/v4/app/upgrades/v8_2" v8_3 "github.com/classic-terra/core/v4/app/upgrades/v8_3" - // unnamed import of statik for swagger UI support _ "github.com/classic-terra/core/v4/client/docs/statik" customante "github.com/classic-terra/core/v4/custom/auth/ante" From c31253cb9776dd503b588109117f19efed9e00e6 Mon Sep 17 00:00:00 2001 From: StrathCole Date: Sun, 26 Apr 2026 10:38:25 +0200 Subject: [PATCH 3/5] fix legacy staking query timeout --- app/modules.go | 2 +- custom/staking/module.go | 27 +++++----- custom/staking/query_server.go | 82 +++++++++++++++++++---------- custom/staking/query_server_test.go | 13 ++--- 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/app/modules.go b/app/modules.go index 07243a9f3..ed4556b56 100644 --- a/app/modules.go +++ b/app/modules.go @@ -156,7 +156,7 @@ func appModules( mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName), app.InterfaceRegistry()), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), - customstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.ParamsKeeper, app.GetSubspace(stakingtypes.ModuleName), app.GetKey(stakingtypes.StoreKey)), + customstaking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.ParamsKeeper, app.GetSubspace(stakingtypes.ModuleName), app.GetKey(stakingtypes.StoreKey), app.GetKey(distrtypes.StoreKey)), upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), evidence.NewAppModule(app.EvidenceKeeper), params.NewAppModule(app.ParamsKeeper), diff --git a/custom/staking/module.go b/custom/staking/module.go index 002c07b45..6cec2fd21 100644 --- a/custom/staking/module.go +++ b/custom/staking/module.go @@ -45,11 +45,12 @@ func (am AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { type AppModule struct { staking.AppModule - cdc codec.Codec - keeper *keeper.Keeper - paramsKeeper paramskeeper.Keeper - ss paramtypes.Subspace - storeKey storetypes.StoreKey + cdc codec.Codec + keeper *keeper.Keeper + paramsKeeper paramskeeper.Keeper + ss paramtypes.Subspace + storeKey storetypes.StoreKey + distrStoreKey storetypes.StoreKey } // NewAppModule creates a new AppModule object @@ -60,14 +61,16 @@ func NewAppModule(cdc codec.Codec, pk paramskeeper.Keeper, ss paramtypes.Subspace, storeKey storetypes.StoreKey, + distrStoreKey storetypes.StoreKey, ) AppModule { return AppModule{ - AppModule: staking.NewAppModule(cdc, keeper, ak, bk, ss), - cdc: cdc, - keeper: keeper, - paramsKeeper: pk, - ss: ss, - storeKey: storeKey, + AppModule: staking.NewAppModule(cdc, keeper, ak, bk, ss), + cdc: cdc, + keeper: keeper, + paramsKeeper: pk, + ss: ss, + storeKey: storeKey, + distrStoreKey: distrStoreKey, } } @@ -78,7 +81,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { querier := keeper.Querier{Keeper: am.keeper} stakingtypes.RegisterQueryServer( cfg.QueryServer(), - NewLegacyQueryServer(querier, am.ss, am.keeper, am.cdc, am.storeKey), + NewLegacyQueryServer(querier, am.ss, am.keeper, am.cdc, am.storeKey, am.distrStoreKey), ) m := keeper.NewMigrator(am.keeper, am.ss) diff --git a/custom/staking/query_server.go b/custom/staking/query_server.go index 767df219a..f56a8b58d 100644 --- a/custom/staking/query_server.go +++ b/custom/staking/query_server.go @@ -3,7 +3,6 @@ package staking import ( "context" "strconv" - "strings" "cosmossdk.io/math" "cosmossdk.io/store/prefix" @@ -12,7 +11,9 @@ import ( legacyupgrade "github.com/classic-terra/core/v4/custom/upgrade/legacy" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/types/query" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -28,13 +29,15 @@ type LegacyQueryServer struct { legacySubspace paramtypes.Subspace cdc codec.BinaryCodec storeKey storetypes.StoreKey + distrStoreKey storetypes.StoreKey } // NewLegacyQueryServer creates a new LegacyQueryServer instance. // // `cdc` and `storeKey` are required for the pre-v5-staking-migration -// ValidatorDelegations fallback path, which scans the primary DelegationKey -// (0x31) prefix directly when the SDK's reverse-index (0x71) hasn't been +// ValidatorDelegations fallback path, which uses x/distribution's +// DelegatorStartingInfo prefix (0x04 || valAddr || delAddr) to enumerate a +// validator's delegators when staking's reverse-index (0x71) hasn't been // backfilled at the queried height. func NewLegacyQueryServer( originalServer stakingtypes.QueryServer, @@ -42,6 +45,7 @@ func NewLegacyQueryServer( keeper *keeper.Keeper, cdc codec.BinaryCodec, storeKey storetypes.StoreKey, + distrStoreKey storetypes.StoreKey, ) stakingtypes.QueryServer { return &LegacyQueryServer{ QueryServer: originalServer, @@ -49,6 +53,7 @@ func NewLegacyQueryServer( legacySubspace: legacySubspace, cdc: cdc, storeKey: storeKey, + distrStoreKey: distrStoreKey, } } @@ -139,11 +144,12 @@ func (q *LegacyQueryServer) ValidatorDelegations(ctx context.Context, req *staki } // validatorDelegationsLegacy reproduces cosmos-sdk's unexported -// `getValidatorDelegationsLegacy` (x/staking/keeper/grpc_query.go): it scans -// the primary DelegationKey (0x31) prefix and filters by validator. Used for -// archive queries at heights before the v4→v5 staking migration backfilled the -// DelegationByValIndexKey (0x71) reverse-index that the SDK's default -// ValidatorDelegations now relies on. +// `getValidatorDelegationsLegacy` semantics for archive heights before the +// v4→v5 staking migration. Instead of scanning every staking delegation under +// 0x31, it walks x/distribution's DelegatorStartingInfo prefix for the target +// validator, then fetches each exact staking delegation by (delegator, +// validator). This keeps the query validator-scoped even when staking's 0x71 +// reverse-index does not exist yet. func (q *LegacyQueryServer) validatorDelegationsLegacy( ctx sdk.Context, req *stakingtypes.QueryValidatorDelegationsRequest, ) (*stakingtypes.QueryValidatorDelegationsResponse, error) { @@ -153,32 +159,43 @@ func (q *LegacyQueryServer) validatorDelegationsLegacy( if req.ValidatorAddr == "" { return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") } - if _, err := sdk.ValAddressFromBech32(req.ValidatorAddr); err != nil { + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } - store := ctx.KVStore(q.storeKey) - delStore := prefix.NewStore(store, stakingtypes.DelegationKey) - - dels, pageRes, err := query.GenericFilteredPaginate( - q.cdc, delStore, req.Pagination, - func(_ []byte, d *stakingtypes.Delegation) (*stakingtypes.Delegation, error) { - if !strings.EqualFold(d.GetValidatorAddr(), req.ValidatorAddr) { - return nil, nil - } - return d, nil - }, - func() *stakingtypes.Delegation { return &stakingtypes.Delegation{} }, - ) + stakingStore := ctx.KVStore(q.storeKey) + distrStore := ctx.KVStore(q.distrStoreKey) + startingInfoPrefix := append([]byte{}, distrtypes.DelegatorStartingInfoPrefix...) + startingInfoPrefix = append(startingInfoPrefix, address.MustLengthPrefix(valAddr.Bytes())...) + startingInfoStore := prefix.NewStore(distrStore, startingInfoPrefix) + + delegations := make(stakingtypes.Delegations, 0) + pageRes, err := query.FilteredPaginate(startingInfoStore, req.Pagination, func(key, _ []byte, accumulate bool) (bool, error) { + delAddr, err := parseLengthPrefixedAccAddress(key) + if err != nil { + return false, err + } + + delegationBz := stakingStore.Get(stakingtypes.GetDelegationKey(delAddr, valAddr)) + if delegationBz == nil { + return false, nil + } + if !accumulate { + return true, nil + } + + var delegation stakingtypes.Delegation + if err := q.cdc.Unmarshal(delegationBz, &delegation); err != nil { + return false, err + } + delegations = append(delegations, delegation) + return true, nil + }) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - delegations := make(stakingtypes.Delegations, 0, len(dels)) - for _, d := range dels { - delegations = append(delegations, *d) - } - delResps, err := q.delegationsToDelegationResponses(ctx, delegations) if err != nil { return nil, status.Error(codes.Internal, err.Error()) @@ -190,6 +207,17 @@ func (q *LegacyQueryServer) validatorDelegationsLegacy( }, nil } +func parseLengthPrefixedAccAddress(bz []byte) (sdk.AccAddress, error) { + if len(bz) == 0 { + return nil, status.Error(codes.Internal, "empty delegator key") + } + addrLen := int(bz[0]) + if len(bz) != 1+addrLen { + return nil, status.Error(codes.Internal, "invalid delegator key length") + } + return sdk.AccAddress(bz[1:]), nil +} + // delegationsToDelegationResponses mirrors the unexported helper of the same // name in cosmos-sdk's staking keeper: it looks up the validator for each // delegation and converts shares to bonded balance. diff --git a/custom/staking/query_server_test.go b/custom/staking/query_server_test.go index 65b249a80..559172efc 100644 --- a/custom/staking/query_server_test.go +++ b/custom/staking/query_server_test.go @@ -15,6 +15,7 @@ import ( simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/testutil" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -122,7 +123,7 @@ func (s *ValidatorDelegationsSuite) TestValidatorDelegations_ReproducesArchiveBu ss := s.App.GetSubspace(stakingtypes.ModuleName) qs := customstaking.NewLegacyQueryServer( querier, ss, s.App.StakingKeeper, - s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), ) req := &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: valAddr.String()} @@ -163,7 +164,7 @@ func (s *ValidatorDelegationsSuite) TestValidatorDelegations_LegacyPathKeepsLega ss := s.App.GetSubspace(stakingtypes.ModuleName) qs := customstaking.NewLegacyQueryServer( querier, ss, s.App.StakingKeeper, - s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), ) params, err := s.App.StakingKeeper.GetParams(s.Ctx) @@ -202,7 +203,7 @@ func (s *ValidatorDelegationsSuite) TestValidatorDelegations_LegacyPathPaginates ss := s.App.GetSubspace(stakingtypes.ModuleName) qs := customstaking.NewLegacyQueryServer( querier, ss, s.App.StakingKeeper, - s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), ) // Drop the reverse-index and force the legacy path. @@ -253,7 +254,7 @@ func (s *ValidatorDelegationsSuite) TestValidatorDelegations_PostMigrationUsesIn ss := s.App.GetSubspace(stakingtypes.ModuleName) qs := customstaking.NewLegacyQueryServer( querier, ss, s.App.StakingKeeper, - s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), ) req := &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: valAddr.String()} @@ -344,7 +345,7 @@ func (s *ValidatorDelegationsSuite) TestHistoricalInfo_ReproducesArchiveBug() { ss := s.App.GetSubspace(stakingtypes.ModuleName) qs := customstaking.NewLegacyQueryServer( querier, ss, s.App.StakingKeeper, - s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), ) req := &stakingtypes.QueryHistoricalInfoRequest{Height: targetHeight} @@ -394,7 +395,7 @@ func (s *ValidatorDelegationsSuite) TestHistoricalInfo_PostMigrationUsesIndex() ss := s.App.GetSubspace(stakingtypes.ModuleName) qs := customstaking.NewLegacyQueryServer( querier, ss, s.App.StakingKeeper, - s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), + s.App.AppCodec(), s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), ) // Rewrite into legacy string-format keys; with the height gate at From b0a6f9a58ba9cebd61bca483d08143c85c5ca77e Mon Sep 17 00:00:00 2001 From: StrathCole Date: Sun, 26 Apr 2026 10:50:53 +0200 Subject: [PATCH 4/5] add pagination --- custom/staking/query_server.go | 43 +++++++++++++++++------------ custom/staking/query_server_test.go | 41 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/custom/staking/query_server.go b/custom/staking/query_server.go index f56a8b58d..eea486739 100644 --- a/custom/staking/query_server.go +++ b/custom/staking/query_server.go @@ -170,35 +170,44 @@ func (q *LegacyQueryServer) validatorDelegationsLegacy( startingInfoPrefix = append(startingInfoPrefix, address.MustLengthPrefix(valAddr.Bytes())...) startingInfoStore := prefix.NewStore(distrStore, startingInfoPrefix) - delegations := make(stakingtypes.Delegations, 0) - pageRes, err := query.FilteredPaginate(startingInfoStore, req.Pagination, func(key, _ []byte, accumulate bool) (bool, error) { + delegatorAddrs := make([]sdk.AccAddress, 0) + pageRes, err := query.Paginate(startingInfoStore, req.Pagination, func(key, _ []byte) error { delAddr, err := parseLengthPrefixedAccAddress(key) if err != nil { - return false, err + return err } + delegatorAddrs = append(delegatorAddrs, delAddr) + return nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + bondDenom, err := q.keeper.BondDenom(ctx) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + validator, err := q.keeper.GetValidator(ctx, valAddr) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + delResps := make(stakingtypes.DelegationResponses, 0, len(delegatorAddrs)) + for _, delAddr := range delegatorAddrs { delegationBz := stakingStore.Get(stakingtypes.GetDelegationKey(delAddr, valAddr)) if delegationBz == nil { - return false, nil - } - if !accumulate { - return true, nil + continue } var delegation stakingtypes.Delegation if err := q.cdc.Unmarshal(delegationBz, &delegation); err != nil { - return false, err + return nil, status.Error(codes.Internal, err.Error()) } - delegations = append(delegations, delegation) - return true, nil - }) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - delResps, err := q.delegationsToDelegationResponses(ctx, delegations) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) + balance := validator.TokensFromShares(delegation.Shares).TruncateInt() + delResps = append(delResps, stakingtypes.NewDelegationResp( + delegation.GetDelegatorAddr(), delegation.GetValidatorAddr(), delegation.Shares, sdk.NewCoin(bondDenom, balance), + )) } return &stakingtypes.QueryValidatorDelegationsResponse{ diff --git a/custom/staking/query_server_test.go b/custom/staking/query_server_test.go index 559172efc..eed9b63ba 100644 --- a/custom/staking/query_server_test.go +++ b/custom/staking/query_server_test.go @@ -12,6 +12,8 @@ import ( customstaking "github.com/classic-terra/core/v4/custom/staking" "github.com/classic-terra/core/v4/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + proto "github.com/cosmos/gogoproto/proto" + "github.com/cosmos/cosmos-sdk/codec" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -30,6 +32,16 @@ func TestValidatorDelegationsSuite(t *testing.T) { suite.Run(t, new(ValidatorDelegationsSuite)) } +type countingCodec struct { + codec.BinaryCodec + unmarshalCalls int +} + +func (c *countingCodec) Unmarshal(bz []byte, ptr proto.Message) error { + c.unmarshalCalls++ + return c.BinaryCodec.Unmarshal(bz, ptr) +} + // seedValidatorWithDelegations creates `numVals` validators (so no single one // exceeds the 20% voting-power cap enforced by the custom staking hook), // then has `numDels` distinct delegators each delegate 1_000_000 uluna to the @@ -239,6 +251,35 @@ func (s *ValidatorDelegationsSuite) TestValidatorDelegations_LegacyPathPaginates "paginated walk must surface every delegation exactly once") } +// TestValidatorDelegations_LegacyPathDefaultPaginationOnlyLoadsFirstPage +// guards the timeout mode from archive LCDs: nil pagination defaults to +// limit=100 with count_total=true, so the legacy path must count all matches +// without loading every delegation value. +func (s *ValidatorDelegationsSuite) TestValidatorDelegations_LegacyPathDefaultPaginationOnlyLoadsFirstPage() { + s.Setup(s.T(), types.ColumbusChainID) + + const totalDelegations = 101 + valAddr := s.seedValidatorWithDelegations(410, totalDelegations) + + querier := stakingkeeper.Querier{Keeper: s.App.StakingKeeper} + ss := s.App.GetSubspace(stakingtypes.ModuleName) + counting := &countingCodec{BinaryCodec: s.App.AppCodec()} + qs := customstaking.NewLegacyQueryServer( + querier, ss, s.App.StakingKeeper, + counting, s.App.GetKey(stakingtypes.StoreKey), s.App.GetKey(distrtypes.StoreKey), + ) + + s.dropReverseIndex() + resp, err := qs.ValidatorDelegations(s.Ctx.WithBlockHeight(28214399), &stakingtypes.QueryValidatorDelegationsRequest{ValidatorAddr: valAddr.String()}) + s.Require().NoError(err) + s.Require().Len(resp.DelegationResponses, 100) + s.Require().NotNil(resp.Pagination) + s.Require().Equal(uint64(totalDelegations), resp.Pagination.Total) + s.Require().NotEmpty(resp.Pagination.NextKey) + s.Require().Equal(100, counting.unmarshalCalls, + "legacy fallback should only unmarshal the current page of delegations") +} + // TestValidatorDelegations_PostMigrationUsesIndex ensures the fix doesn't change // behavior at chain-head heights: with the reverse-index intact and queried at // a post-v5-staking-migration height, the SDK's normal indexed path runs. From b35e93ad5fb70b6e36e872eb09dcdc8b0bbd9e6c Mon Sep 17 00:00:00 2001 From: kien6034 Date: Sun, 26 Apr 2026 10:36:01 +0000 Subject: [PATCH 5/5] bet --- custom/staking/query_server.go | 28 ---------------------------- custom/staking/query_server_test.go | 2 +- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/custom/staking/query_server.go b/custom/staking/query_server.go index eea486739..e37adcf43 100644 --- a/custom/staking/query_server.go +++ b/custom/staking/query_server.go @@ -227,34 +227,6 @@ func parseLengthPrefixedAccAddress(bz []byte) (sdk.AccAddress, error) { return sdk.AccAddress(bz[1:]), nil } -// delegationsToDelegationResponses mirrors the unexported helper of the same -// name in cosmos-sdk's staking keeper: it looks up the validator for each -// delegation and converts shares to bonded balance. -func (q *LegacyQueryServer) delegationsToDelegationResponses( - ctx sdk.Context, delegations stakingtypes.Delegations, -) (stakingtypes.DelegationResponses, error) { - bondDenom, err := q.keeper.BondDenom(ctx) - if err != nil { - return nil, err - } - resps := make(stakingtypes.DelegationResponses, 0, len(delegations)) - for _, d := range delegations { - valAddr, err := sdk.ValAddressFromBech32(d.GetValidatorAddr()) - if err != nil { - return nil, err - } - val, err := q.keeper.GetValidator(ctx, valAddr) - if err != nil { - return nil, err - } - balance := val.TokensFromShares(d.Shares).TruncateInt() - resps = append(resps, stakingtypes.NewDelegationResp( - d.GetDelegatorAddr(), d.GetValidatorAddr(), d.Shares, sdk.NewCoin(bondDenom, balance), - )) - } - return resps, nil -} - func (q *LegacyQueryServer) ValidatorUnbondingDelegations(ctx context.Context, req *stakingtypes.QueryValidatorUnbondingDelegationsRequest) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) { return q.QueryServer.ValidatorUnbondingDelegations(q.ensureLegacyParams(ctx), req) } diff --git a/custom/staking/query_server_test.go b/custom/staking/query_server_test.go index eed9b63ba..10de56c5a 100644 --- a/custom/staking/query_server_test.go +++ b/custom/staking/query_server_test.go @@ -12,7 +12,6 @@ import ( customstaking "github.com/classic-terra/core/v4/custom/staking" "github.com/classic-terra/core/v4/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - proto "github.com/cosmos/gogoproto/proto" "github.com/cosmos/cosmos-sdk/codec" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,6 +20,7 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/testutil" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + proto "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/suite" )