diff --git a/app/app.go b/app/app.go index 7247d1280..9f26c8e5e 100644 --- a/app/app.go +++ b/app/app.go @@ -29,6 +29,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" @@ -105,6 +106,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..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)), + 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/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..6cec2fd21 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,12 @@ func (am AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { type AppModule struct { staking.AppModule - keeper *keeper.Keeper - paramsKeeper paramskeeper.Keeper - ss paramtypes.Subspace + cdc codec.Codec + keeper *keeper.Keeper + paramsKeeper paramskeeper.Keeper + ss paramtypes.Subspace + storeKey storetypes.StoreKey + distrStoreKey storetypes.StoreKey } // NewAppModule creates a new AppModule object @@ -56,12 +60,17 @@ func NewAppModule(cdc codec.Codec, bk stakingtypes.BankKeeper, pk paramskeeper.Keeper, ss paramtypes.Subspace, + storeKey storetypes.StoreKey, + distrStoreKey storetypes.StoreKey, ) AppModule { return AppModule{ - AppModule: staking.NewAppModule(cdc, keeper, ak, bk, ss), - keeper: keeper, - paramsKeeper: pk, - ss: ss, + AppModule: staking.NewAppModule(cdc, keeper, ak, bk, ss), + cdc: cdc, + keeper: keeper, + paramsKeeper: pk, + ss: ss, + storeKey: storeKey, + distrStoreKey: distrStoreKey, } } @@ -72,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), + 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 b4c58f888..e37adcf43 100644 --- a/custom/staking/query_server.go +++ b/custom/staking/query_server.go @@ -2,14 +2,23 @@ package staking import ( "context" + "strconv" "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/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" + "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 +27,33 @@ type LegacyQueryServer struct { stakingtypes.QueryServer keeper *keeper.Keeper legacySubspace paramtypes.Subspace + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + distrStoreKey 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 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, legacySubspace paramtypes.Subspace, keeper *keeper.Keeper, + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, + distrStoreKey storetypes.StoreKey, ) stakingtypes.QueryServer { return &LegacyQueryServer{ QueryServer: originalServer, keeper: keeper, legacySubspace: legacySubspace, + cdc: cdc, + storeKey: storeKey, + distrStoreKey: distrStoreKey, } } @@ -111,7 +135,96 @@ 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` 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) { + 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") + } + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + 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) + + delegatorAddrs := make([]sdk.AccAddress, 0) + pageRes, err := query.Paginate(startingInfoStore, req.Pagination, func(key, _ []byte) error { + delAddr, err := parseLengthPrefixedAccAddress(key) + if err != nil { + 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 { + continue + } + + var delegation stakingtypes.Delegation + if err := q.cdc.Unmarshal(delegationBz, &delegation); 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{ + DelegationResponses: delResps, + Pagination: pageRes, + }, 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 } func (q *LegacyQueryServer) ValidatorUnbondingDelegations(ctx context.Context, req *stakingtypes.QueryValidatorUnbondingDelegationsRequest) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) { @@ -147,7 +260,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..10de56c5a --- /dev/null +++ b/custom/staking/query_server_test.go @@ -0,0 +1,449 @@ +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" + "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" + 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" + proto "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/suite" +) + +type ValidatorDelegationsSuite struct { + apptesting.KeeperTestHelper +} + +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 +// 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), s.App.GetKey(distrtypes.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), s.App.GetKey(distrtypes.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), s.App.GetKey(distrtypes.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_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. +// (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), s.App.GetKey(distrtypes.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), s.App.GetKey(distrtypes.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), s.App.GetKey(distrtypes.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=