github.com/Finschia/finschia-sdk@v0.49.1/x/auth/legacy/v043/store.go (about) 1 // Package v043 creates in-place store migrations for fixing tracking 2 // delegations with vesting accounts. 3 // ref: https://github.com/cosmos/cosmos-sdk/issues/8601 4 // ref: https://github.com/cosmos/cosmos-sdk/issues/8812 5 // 6 // The migration script modifies x/auth state, hence lives in the `x/auth/legacy` 7 // folder. However, it needs access to staking and bank state. To avoid 8 // cyclic dependencies, we cannot import those 2 keepers in this file. To solve 9 // this, we use the baseapp router to do inter-module querying, by importing 10 // the `baseapp.QueryRouter grpc.Server`. This is really hacky. 11 // 12 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 13 // 14 // Proposals to refactor this file have been made in: 15 // https://github.com/cosmos/cosmos-sdk/issues/9070 16 // The preferred solution is to use inter-module communication (ADR-033), and 17 // this file will be refactored to use ADR-033 once it's ready. 18 package v043 19 20 import ( 21 "errors" 22 "fmt" 23 24 "github.com/gogo/protobuf/grpc" 25 "github.com/gogo/protobuf/proto" 26 abci "github.com/tendermint/tendermint/abci/types" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/status" 29 30 "github.com/Finschia/finschia-sdk/baseapp" 31 sdk "github.com/Finschia/finschia-sdk/types" 32 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 33 "github.com/Finschia/finschia-sdk/x/auth/types" 34 "github.com/Finschia/finschia-sdk/x/auth/vesting/exported" 35 vestingtypes "github.com/Finschia/finschia-sdk/x/auth/vesting/types" 36 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 37 stakingtypes "github.com/Finschia/finschia-sdk/x/staking/types" 38 ) 39 40 const ( 41 delegatorDelegationPath = "/cosmos.staking.v1beta1.Query/DelegatorDelegations" 42 stakingParamsPath = "/cosmos.staking.v1beta1.Query/Params" 43 delegatorUnbondingDelegationsPath = "/cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations" 44 balancesPath = "/cosmos.bank.v1beta1.Query/AllBalances" 45 ) 46 47 // We use the baseapp.QueryRouter here to do inter-module state querying. 48 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 49 func migrateVestingAccounts(ctx sdk.Context, account types.AccountI, queryServer grpc.Server) (types.AccountI, error) { 50 bondDenom, err := getBondDenom(ctx, queryServer) 51 if err != nil { 52 return nil, err 53 } 54 55 asVesting, ok := account.(exported.VestingAccount) 56 if !ok { 57 return nil, nil 58 } 59 60 addr := account.GetAddress().String() 61 balance, err := getBalance( 62 ctx, 63 addr, 64 queryServer, 65 ) 66 if err != nil { 67 return nil, err 68 } 69 70 delegations, err := getDelegatorDelegationsSum( 71 ctx, 72 addr, 73 queryServer, 74 ) 75 if err != nil { 76 return nil, err 77 } 78 79 unbondingDelegations, err := getDelegatorUnbondingDelegationsSum( 80 ctx, 81 addr, 82 bondDenom, 83 queryServer, 84 ) 85 if err != nil { 86 return nil, err 87 } 88 89 delegations = delegations.Add(unbondingDelegations...) 90 91 asVesting, ok = resetVestingDelegatedBalances(asVesting) 92 if !ok { 93 return nil, nil 94 } 95 96 // balance before any delegation includes balance of delegation 97 for _, coin := range delegations { 98 balance = balance.Add(coin) 99 } 100 101 asVesting.TrackDelegation(ctx.BlockTime(), balance, delegations) 102 103 return asVesting.(types.AccountI), nil 104 } 105 106 func resetVestingDelegatedBalances(evacct exported.VestingAccount) (exported.VestingAccount, bool) { 107 // reset `DelegatedVesting` and `DelegatedFree` to zero 108 df := sdk.NewCoins() 109 dv := sdk.NewCoins() 110 111 switch vacct := evacct.(type) { 112 case *vestingtypes.ContinuousVestingAccount: 113 vacct.DelegatedVesting = dv 114 vacct.DelegatedFree = df 115 return vacct, true 116 case *vestingtypes.DelayedVestingAccount: 117 vacct.DelegatedVesting = dv 118 vacct.DelegatedFree = df 119 return vacct, true 120 case *vestingtypes.PeriodicVestingAccount: 121 vacct.DelegatedVesting = dv 122 vacct.DelegatedFree = df 123 return vacct, true 124 default: 125 return nil, false 126 } 127 } 128 129 // We use the baseapp.QueryRouter here to do inter-module state querying. 130 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 131 func getDelegatorDelegationsSum(ctx sdk.Context, address string, queryServer grpc.Server) (sdk.Coins, error) { 132 querier, ok := queryServer.(*baseapp.GRPCQueryRouter) 133 if !ok { 134 return nil, fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer) 135 } 136 137 queryFn := querier.Route(delegatorDelegationPath) 138 139 q := &stakingtypes.QueryDelegatorDelegationsRequest{ 140 DelegatorAddr: address, 141 } 142 143 b, err := proto.Marshal(q) 144 if err != nil { 145 return nil, fmt.Errorf("cannot marshal staking type query request, %w", err) 146 } 147 req := abci.RequestQuery{ 148 Data: b, 149 Path: delegatorDelegationPath, 150 } 151 resp, err := queryFn(ctx, req) 152 if err != nil { 153 e, ok := status.FromError(err) 154 if ok && e.Code() == codes.NotFound { 155 return nil, nil 156 } 157 return nil, fmt.Errorf("staking query error, %w", err) 158 } 159 160 balance := new(stakingtypes.QueryDelegatorDelegationsResponse) 161 if err := proto.Unmarshal(resp.Value, balance); err != nil { 162 return nil, fmt.Errorf("unable to unmarshal delegator query delegations: %w", err) 163 } 164 165 res := sdk.NewCoins() 166 for _, i := range balance.DelegationResponses { 167 res = res.Add(i.Balance) 168 } 169 170 return res, nil 171 } 172 173 // We use the baseapp.QueryRouter here to do inter-module state querying. 174 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 175 func getDelegatorUnbondingDelegationsSum(ctx sdk.Context, address, bondDenom string, queryServer grpc.Server) (sdk.Coins, error) { 176 querier, ok := queryServer.(*baseapp.GRPCQueryRouter) 177 if !ok { 178 return nil, fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer) 179 } 180 181 queryFn := querier.Route(delegatorUnbondingDelegationsPath) 182 183 q := &stakingtypes.QueryDelegatorUnbondingDelegationsRequest{ 184 DelegatorAddr: address, 185 } 186 187 b, err := proto.Marshal(q) 188 if err != nil { 189 return nil, fmt.Errorf("cannot marshal staking type query request, %w", err) 190 } 191 req := abci.RequestQuery{ 192 Data: b, 193 Path: delegatorUnbondingDelegationsPath, 194 } 195 resp, err := queryFn(ctx, req) 196 if err != nil && !errors.Is(err, sdkerrors.ErrNotFound) { 197 e, ok := status.FromError(err) 198 if ok && e.Code() == codes.NotFound { 199 return nil, nil 200 } 201 return nil, fmt.Errorf("staking query error, %w", err) 202 } 203 204 balance := new(stakingtypes.QueryDelegatorUnbondingDelegationsResponse) 205 if err := proto.Unmarshal(resp.Value, balance); err != nil { 206 return nil, fmt.Errorf("unable to unmarshal delegator query delegations: %w", err) 207 } 208 209 res := sdk.NewCoins() 210 for _, i := range balance.UnbondingResponses { 211 for _, r := range i.Entries { 212 res = res.Add(sdk.NewCoin(bondDenom, r.Balance)) 213 } 214 } 215 216 return res, nil 217 } 218 219 // We use the baseapp.QueryRouter here to do inter-module state querying. 220 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 221 func getBalance(ctx sdk.Context, address string, queryServer grpc.Server) (sdk.Coins, error) { 222 querier, ok := queryServer.(*baseapp.GRPCQueryRouter) 223 if !ok { 224 return nil, fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer) 225 } 226 227 queryFn := querier.Route(balancesPath) 228 229 q := &banktypes.QueryAllBalancesRequest{ 230 Address: address, 231 Pagination: nil, 232 } 233 b, err := proto.Marshal(q) 234 if err != nil { 235 return nil, fmt.Errorf("cannot marshal bank type query request, %w", err) 236 } 237 238 req := abci.RequestQuery{ 239 Data: b, 240 Path: balancesPath, 241 } 242 resp, err := queryFn(ctx, req) 243 if err != nil { 244 return nil, fmt.Errorf("bank query error, %w", err) 245 } 246 balance := new(banktypes.QueryAllBalancesResponse) 247 if err := proto.Unmarshal(resp.Value, balance); err != nil { 248 return nil, fmt.Errorf("unable to unmarshal bank balance response: %w", err) 249 } 250 return balance.Balances, nil 251 } 252 253 // We use the baseapp.QueryRouter here to do inter-module state querying. 254 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 255 func getBondDenom(ctx sdk.Context, queryServer grpc.Server) (string, error) { 256 querier, ok := queryServer.(*baseapp.GRPCQueryRouter) 257 if !ok { 258 return "", fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer) 259 } 260 261 queryFn := querier.Route(stakingParamsPath) 262 263 q := &stakingtypes.QueryParamsRequest{} 264 265 b, err := proto.Marshal(q) 266 if err != nil { 267 return "", fmt.Errorf("cannot marshal staking params query request, %w", err) 268 } 269 req := abci.RequestQuery{ 270 Data: b, 271 Path: stakingParamsPath, 272 } 273 274 resp, err := queryFn(ctx, req) 275 if err != nil { 276 return "", fmt.Errorf("staking query error, %w", err) 277 } 278 279 params := new(stakingtypes.QueryParamsResponse) 280 if err := proto.Unmarshal(resp.Value, params); err != nil { 281 return "", fmt.Errorf("unable to unmarshal delegator query delegations: %w", err) 282 } 283 284 return params.Params.BondDenom, nil 285 } 286 287 // MigrateAccount migrates vesting account to make the DelegatedVesting and DelegatedFree fields correctly 288 // track delegations. 289 // References: https://github.com/cosmos/cosmos-sdk/issues/8601, https://github.com/cosmos/cosmos-sdk/issues/8812 290 // 291 // We use the baseapp.QueryRouter here to do inter-module state querying. 292 // PLEASE DO NOT REPLICATE THIS PATTERN IN YOUR OWN APP. 293 func MigrateAccount(ctx sdk.Context, account types.AccountI, queryServer grpc.Server) (types.AccountI, error) { 294 return migrateVestingAccounts(ctx, account, queryServer) 295 }