@@ -2,14 +2,22 @@ package staking
22
33import (
44 "context"
5+ "strconv"
6+ "strings"
57
68 "cosmossdk.io/math"
9+ "cosmossdk.io/store/prefix"
10+ storetypes "cosmossdk.io/store/types"
711 legacytypes "github.com/classic-terra/core/v4/custom/staking/types"
812 legacyupgrade "github.com/classic-terra/core/v4/custom/upgrade/legacy"
13+ "github.com/cosmos/cosmos-sdk/codec"
914 sdk "github.com/cosmos/cosmos-sdk/types"
15+ "github.com/cosmos/cosmos-sdk/types/query"
1016 paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
1117 "github.com/cosmos/cosmos-sdk/x/staking/keeper"
1218 stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
19+ "google.golang.org/grpc/codes"
20+ "google.golang.org/grpc/status"
1321)
1422
1523// LegacyQueryServer wraps the staking QueryServer and sets legacy parameters for pre-upgrade height queries
@@ -18,18 +26,29 @@ type LegacyQueryServer struct {
1826 stakingtypes.QueryServer
1927 keeper * keeper.Keeper
2028 legacySubspace paramtypes.Subspace
29+ cdc codec.BinaryCodec
30+ storeKey storetypes.StoreKey
2131}
2232
23- // NewLegacyQueryServer creates a new LegacyQueryServer instance
33+ // NewLegacyQueryServer creates a new LegacyQueryServer instance.
34+ //
35+ // `cdc` and `storeKey` are required for the pre-v5-staking-migration
36+ // ValidatorDelegations fallback path, which scans the primary DelegationKey
37+ // (0x31) prefix directly when the SDK's reverse-index (0x71) hasn't been
38+ // backfilled at the queried height.
2439func NewLegacyQueryServer (
2540 originalServer stakingtypes.QueryServer ,
2641 legacySubspace paramtypes.Subspace ,
2742 keeper * keeper.Keeper ,
43+ cdc codec.BinaryCodec ,
44+ storeKey storetypes.StoreKey ,
2845) stakingtypes.QueryServer {
2946 return & LegacyQueryServer {
3047 QueryServer : originalServer ,
3148 keeper : keeper ,
3249 legacySubspace : legacySubspace ,
50+ cdc : cdc ,
51+ storeKey : storeKey ,
3352 }
3453}
3554
@@ -111,7 +130,92 @@ func (q *LegacyQueryServer) Validator(ctx context.Context, req *stakingtypes.Que
111130}
112131
113132func (q * LegacyQueryServer ) ValidatorDelegations (ctx context.Context , req * stakingtypes.QueryValidatorDelegationsRequest ) (* stakingtypes.QueryValidatorDelegationsResponse , error ) {
114- return q .QueryServer .ValidatorDelegations (q .ensureLegacyParams (ctx ), req )
133+ ensuredCtx := q .ensureLegacyParams (ctx )
134+ sdkCtx := sdk .UnwrapSDKContext (ensuredCtx )
135+ if legacyupgrade .IsPreStakingV5 (sdkCtx .ChainID (), sdkCtx .BlockHeight ()) {
136+ return q .validatorDelegationsLegacy (sdkCtx , req )
137+ }
138+ return q .QueryServer .ValidatorDelegations (ensuredCtx , req )
139+ }
140+
141+ // validatorDelegationsLegacy reproduces cosmos-sdk's unexported
142+ // `getValidatorDelegationsLegacy` (x/staking/keeper/grpc_query.go): it scans
143+ // the primary DelegationKey (0x31) prefix and filters by validator. Used for
144+ // archive queries at heights before the v4→v5 staking migration backfilled the
145+ // DelegationByValIndexKey (0x71) reverse-index that the SDK's default
146+ // ValidatorDelegations now relies on.
147+ func (q * LegacyQueryServer ) validatorDelegationsLegacy (
148+ ctx sdk.Context , req * stakingtypes.QueryValidatorDelegationsRequest ,
149+ ) (* stakingtypes.QueryValidatorDelegationsResponse , error ) {
150+ if req == nil {
151+ return nil , status .Error (codes .InvalidArgument , "empty request" )
152+ }
153+ if req .ValidatorAddr == "" {
154+ return nil , status .Error (codes .InvalidArgument , "validator address cannot be empty" )
155+ }
156+ if _ , err := sdk .ValAddressFromBech32 (req .ValidatorAddr ); err != nil {
157+ return nil , status .Error (codes .InvalidArgument , err .Error ())
158+ }
159+
160+ store := ctx .KVStore (q .storeKey )
161+ delStore := prefix .NewStore (store , stakingtypes .DelegationKey )
162+
163+ dels , pageRes , err := query .GenericFilteredPaginate (
164+ q .cdc , delStore , req .Pagination ,
165+ func (_ []byte , d * stakingtypes.Delegation ) (* stakingtypes.Delegation , error ) {
166+ if ! strings .EqualFold (d .GetValidatorAddr (), req .ValidatorAddr ) {
167+ return nil , nil
168+ }
169+ return d , nil
170+ },
171+ func () * stakingtypes.Delegation { return & stakingtypes.Delegation {} },
172+ )
173+ if err != nil {
174+ return nil , status .Error (codes .Internal , err .Error ())
175+ }
176+
177+ delegations := make (stakingtypes.Delegations , 0 , len (dels ))
178+ for _ , d := range dels {
179+ delegations = append (delegations , * d )
180+ }
181+
182+ delResps , err := q .delegationsToDelegationResponses (ctx , delegations )
183+ if err != nil {
184+ return nil , status .Error (codes .Internal , err .Error ())
185+ }
186+
187+ return & stakingtypes.QueryValidatorDelegationsResponse {
188+ DelegationResponses : delResps ,
189+ Pagination : pageRes ,
190+ }, nil
191+ }
192+
193+ // delegationsToDelegationResponses mirrors the unexported helper of the same
194+ // name in cosmos-sdk's staking keeper: it looks up the validator for each
195+ // delegation and converts shares to bonded balance.
196+ func (q * LegacyQueryServer ) delegationsToDelegationResponses (
197+ ctx sdk.Context , delegations stakingtypes.Delegations ,
198+ ) (stakingtypes.DelegationResponses , error ) {
199+ bondDenom , err := q .keeper .BondDenom (ctx )
200+ if err != nil {
201+ return nil , err
202+ }
203+ resps := make (stakingtypes.DelegationResponses , 0 , len (delegations ))
204+ for _ , d := range delegations {
205+ valAddr , err := sdk .ValAddressFromBech32 (d .GetValidatorAddr ())
206+ if err != nil {
207+ return nil , err
208+ }
209+ val , err := q .keeper .GetValidator (ctx , valAddr )
210+ if err != nil {
211+ return nil , err
212+ }
213+ balance := val .TokensFromShares (d .Shares ).TruncateInt ()
214+ resps = append (resps , stakingtypes .NewDelegationResp (
215+ d .GetDelegatorAddr (), d .GetValidatorAddr (), d .Shares , sdk .NewCoin (bondDenom , balance ),
216+ ))
217+ }
218+ return resps , nil
115219}
116220
117221func (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
147251}
148252
149253func (q * LegacyQueryServer ) HistoricalInfo (ctx context.Context , req * stakingtypes.QueryHistoricalInfoRequest ) (* stakingtypes.QueryHistoricalInfoResponse , error ) {
150- return q .QueryServer .HistoricalInfo (q .ensureLegacyParams (ctx ), req )
254+ ensuredCtx := q .ensureLegacyParams (ctx )
255+ sdkCtx := sdk .UnwrapSDKContext (ensuredCtx )
256+ if legacyupgrade .IsPreStakingV5 (sdkCtx .ChainID (), sdkCtx .BlockHeight ()) {
257+ return q .historicalInfoLegacy (sdkCtx , req )
258+ }
259+ return q .QueryServer .HistoricalInfo (ensuredCtx , req )
260+ }
261+
262+ // historicalInfoLegacy reads HistoricalInfo using the pre-v5-staking-migration
263+ // key encoding: prefix 0x50 followed by the ASCII-decimal height string. The
264+ // v5 migration (cosmos-sdk@v0.53.6/x/staking/migrations/v5/store.go:39) re-keys
265+ // every entry to a big-endian uint64; before that migration ran (block
266+ // 28214400 on Columbus, 28917279 on Rebel-2) IAVL state contains only the old
267+ // string-format keys, so the SDK's GetHistoricalInfo — which constructs the
268+ // new binary key — misses and returns NotFound.
269+ func (q * LegacyQueryServer ) historicalInfoLegacy (
270+ ctx sdk.Context , req * stakingtypes.QueryHistoricalInfoRequest ,
271+ ) (* stakingtypes.QueryHistoricalInfoResponse , error ) {
272+ if req == nil {
273+ return nil , status .Error (codes .InvalidArgument , "empty request" )
274+ }
275+ if req .Height < 0 {
276+ return nil , status .Error (codes .InvalidArgument , "height cannot be negative" )
277+ }
278+
279+ store := ctx .KVStore (q .storeKey )
280+ legacyKey := append ([]byte {}, stakingtypes .HistoricalInfoKey ... )
281+ legacyKey = append (legacyKey , []byte (strconv .FormatInt (req .Height , 10 ))... )
282+ bz := store .Get (legacyKey )
283+ if bz == nil {
284+ return nil , status .Errorf (codes .NotFound , "historical info for height %d not found" , req .Height )
285+ }
286+
287+ var hi stakingtypes.HistoricalInfo
288+ if err := q .cdc .Unmarshal (bz , & hi ); err != nil {
289+ return nil , status .Error (codes .Internal , err .Error ())
290+ }
291+ return & stakingtypes.QueryHistoricalInfoResponse {Hist : & hi }, nil
151292}
152293
153294func (q * LegacyQueryServer ) Pool (ctx context.Context , req * stakingtypes.QueryPoolRequest ) (* stakingtypes.QueryPoolResponse , error ) {
0 commit comments