github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/keeper/querier.go (about) 1 package keeper 2 3 import ( 4 "context" 5 "encoding/binary" 6 "runtime/debug" 7 8 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 9 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/prefix" 10 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 11 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 12 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/query" 13 "github.com/fibonacci-chain/fbc/x/wasm/proxy" 14 "github.com/fibonacci-chain/fbc/x/wasm/types" 15 "github.com/fibonacci-chain/fbc/x/wasm/watcher" 16 "google.golang.org/grpc/codes" 17 "google.golang.org/grpc/status" 18 ) 19 20 var _ types.QueryServer = &grpcQuerier{} 21 22 type grpcQuerier struct { 23 cdc codec.CodecProxy 24 storeKey sdk.StoreKey 25 keeper types.ViewKeeper 26 queryGasLimit sdk.Gas 27 } 28 29 // NewGrpcQuerier constructor 30 func NewGrpcQuerier(cdc codec.CodecProxy, storeKey sdk.StoreKey, keeper types.ViewKeeper, queryGasLimit sdk.Gas) *grpcQuerier { //nolint:revive 31 return &grpcQuerier{cdc: cdc, storeKey: storeKey, keeper: keeper, queryGasLimit: queryGasLimit} 32 } 33 34 func (q grpcQuerier) ContractInfo(c context.Context, req *types.QueryContractInfoRequest) (*types.QueryContractInfoResponse, error) { 35 if req == nil { 36 return nil, status.Error(codes.InvalidArgument, "empty request") 37 } 38 contractAddr, err := sdk.AccAddressFromBech32(req.Address) 39 if err != nil { 40 return nil, err 41 } 42 43 rsp, err := queryContractInfo(q.UnwrapSDKContext(c), contractAddr, q.keeper) 44 switch { 45 case err != nil: 46 return nil, err 47 case rsp == nil: 48 return nil, types.ErrNotFound 49 } 50 return rsp, nil 51 } 52 53 func (q grpcQuerier) ContractHistory(c context.Context, req *types.QueryContractHistoryRequest) (*types.QueryContractHistoryResponse, error) { 54 if req == nil { 55 return nil, status.Error(codes.InvalidArgument, "empty request") 56 } 57 contractAddr, err := sdk.AccAddressFromBech32(req.Address) 58 if err != nil { 59 return nil, err 60 } 61 62 r := make([]types.ContractCodeHistoryEntry, 0) 63 prefixStore := q.PrefixStore(c, types.GetContractCodeHistoryElementPrefix(contractAddr)) 64 65 pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { 66 if accumulate { 67 var e types.ContractCodeHistoryEntry 68 if err := q.cdc.GetProtocMarshal().Unmarshal(value, &e); err != nil { 69 return false, err 70 } 71 e.Updated = nil // redact 72 r = append(r, e) 73 } 74 return true, nil 75 }) 76 if err != nil { 77 return nil, err 78 } 79 return &types.QueryContractHistoryResponse{ 80 Entries: r, 81 Pagination: pageRes, 82 }, nil 83 } 84 85 // ContractsByCode lists all smart contracts for a code id 86 func (q grpcQuerier) ContractsByCode(c context.Context, req *types.QueryContractsByCodeRequest) (*types.QueryContractsByCodeResponse, error) { 87 if req == nil { 88 return nil, status.Error(codes.InvalidArgument, "empty request") 89 } 90 if req.CodeId == 0 { 91 return nil, sdkerrors.Wrap(types.ErrInvalid, "code id") 92 } 93 94 r := make([]string, 0) 95 prefixStore := q.PrefixStore(c, types.GetContractByCodeIDSecondaryIndexPrefix(req.CodeId)) 96 97 pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { 98 if accumulate { 99 var contractAddr sdk.AccAddress = key[types.AbsoluteTxPositionLen:] 100 r = append(r, contractAddr.String()) 101 } 102 return true, nil 103 }) 104 if err != nil { 105 return nil, err 106 } 107 return &types.QueryContractsByCodeResponse{ 108 Contracts: r, 109 Pagination: pageRes, 110 }, nil 111 } 112 113 func (q grpcQuerier) AllContractState(c context.Context, req *types.QueryAllContractStateRequest) (*types.QueryAllContractStateResponse, error) { 114 if req == nil { 115 return nil, status.Error(codes.InvalidArgument, "empty request") 116 } 117 contractAddr, err := sdk.AccAddressFromBech32(req.Address) 118 if err != nil { 119 return nil, err 120 } 121 122 if !q.keeper.HasContractInfo(q.UnwrapSDKContext(c), contractAddr) { 123 return nil, types.ErrNotFound 124 } 125 126 r := make([]types.Model, 0) 127 prefixStore := q.PrefixStore(c, types.GetContractStorePrefix(contractAddr)) 128 129 pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { 130 if accumulate { 131 r = append(r, types.Model{ 132 Key: key, 133 Value: value, 134 }) 135 } 136 return true, nil 137 }) 138 if err != nil { 139 return nil, err 140 } 141 return &types.QueryAllContractStateResponse{ 142 Models: r, 143 Pagination: pageRes, 144 }, nil 145 } 146 147 func (q grpcQuerier) RawContractState(c context.Context, req *types.QueryRawContractStateRequest) (*types.QueryRawContractStateResponse, error) { 148 if req == nil { 149 return nil, status.Error(codes.InvalidArgument, "empty request") 150 } 151 152 contractAddr, err := sdk.AccAddressFromBech32(req.Address) 153 if err != nil { 154 return nil, err 155 } 156 157 ctx := q.UnwrapSDKContext(c) 158 if !q.keeper.HasContractInfo(ctx, contractAddr) { 159 return nil, types.ErrNotFound 160 } 161 rsp := q.keeper.QueryRaw(ctx, contractAddr, req.QueryData) 162 return &types.QueryRawContractStateResponse{Data: rsp}, nil 163 } 164 165 func (q grpcQuerier) SmartContractState(c context.Context, req *types.QuerySmartContractStateRequest) (rsp *types.QuerySmartContractStateResponse, err error) { 166 if req == nil { 167 return nil, status.Error(codes.InvalidArgument, "empty request") 168 } 169 if err := req.QueryData.ValidateBasic(); err != nil { 170 return nil, status.Error(codes.InvalidArgument, "invalid query data") 171 } 172 contractAddr, err := sdk.AccAddressFromBech32(req.Address) 173 if err != nil { 174 return nil, err 175 } 176 177 ctx := q.UnwrapSDKContext(c) 178 ctx.SetGasMeter(sdk.NewGasMeter(q.queryGasLimit)) 179 180 // recover from out-of-gas panic 181 defer func() { 182 if r := recover(); r != nil { 183 switch rType := r.(type) { 184 case sdk.ErrorOutOfGas: 185 err = sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, 186 "out of gas in location: %v; gasWanted: %d, gasUsed: %d", 187 rType.Descriptor, ctx.GasMeter().Limit(), ctx.GasMeter().GasConsumed(), 188 ) 189 default: 190 err = sdkerrors.ErrPanic 191 } 192 rsp = nil 193 moduleLogger(ctx). 194 Debug("smart query contract", 195 "error", "recovering panic", 196 "contract-address", req.Address, 197 "stacktrace", string(debug.Stack())) 198 } 199 }() 200 201 bz, err := q.keeper.QuerySmart(ctx, contractAddr, req.QueryData) 202 switch { 203 case err != nil: 204 return nil, err 205 case bz == nil: 206 return nil, types.ErrNotFound 207 } 208 return &types.QuerySmartContractStateResponse{Data: bz}, nil 209 } 210 211 func (q grpcQuerier) Code(c context.Context, req *types.QueryCodeRequest) (*types.QueryCodeResponse, error) { 212 213 if req == nil { 214 return nil, status.Error(codes.InvalidArgument, "empty request") 215 } 216 if req.CodeId == 0 { 217 return nil, sdkerrors.Wrap(types.ErrInvalid, "code id") 218 } 219 220 rsp, err := queryCode(q.UnwrapSDKContext(c), req.CodeId, q.keeper) 221 switch { 222 case err != nil: 223 return nil, err 224 case rsp == nil: 225 return nil, types.ErrNotFound 226 } 227 return &types.QueryCodeResponse{ 228 CodeInfoResponse: rsp.CodeInfoResponse, 229 Data: rsp.Data, 230 }, nil 231 } 232 233 func (q grpcQuerier) Codes(c context.Context, req *types.QueryCodesRequest) (*types.QueryCodesResponse, error) { 234 if req == nil { 235 return nil, status.Error(codes.InvalidArgument, "empty request") 236 } 237 238 r := make([]types.CodeInfoResponse, 0) 239 prefixStore := q.PrefixStore(c, types.CodeKeyPrefix) 240 241 pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { 242 if accumulate { 243 var c types.CodeInfo 244 if err := q.cdc.GetProtocMarshal().Unmarshal(value, &c); err != nil { 245 return false, err 246 } 247 r = append(r, types.CodeInfoResponse{ 248 CodeID: binary.BigEndian.Uint64(key), 249 Creator: c.Creator, 250 DataHash: c.CodeHash, 251 InstantiatePermission: c.InstantiateConfig, 252 }) 253 } 254 return true, nil 255 }) 256 if err != nil { 257 return nil, err 258 } 259 return &types.QueryCodesResponse{CodeInfos: r, Pagination: pageRes}, nil 260 } 261 262 func queryContractInfo(ctx sdk.Context, addr sdk.AccAddress, keeper types.ViewKeeper) (*types.QueryContractInfoResponse, error) { 263 info := keeper.GetContractInfo(ctx, addr) 264 if info == nil { 265 return nil, types.ErrNotFound 266 } 267 // redact the Created field (just used for sorting, not part of public API) 268 info.Created = nil 269 return &types.QueryContractInfoResponse{ 270 Address: addr.String(), 271 ContractInfo: *info, 272 }, nil 273 } 274 275 func queryCode(ctx sdk.Context, codeID uint64, keeper types.ViewKeeper) (*types.QueryCodeResponse, error) { 276 if codeID == 0 { 277 return nil, nil 278 } 279 res := keeper.GetCodeInfo(ctx, codeID) 280 if res == nil { 281 // nil, nil leads to 404 in rest handler 282 return nil, nil 283 } 284 info := types.CodeInfoResponse{ 285 CodeID: codeID, 286 Creator: res.Creator, 287 DataHash: res.CodeHash, 288 InstantiatePermission: res.InstantiateConfig, 289 } 290 291 code, err := keeper.GetByteCode(ctx, codeID) 292 if err != nil { 293 return nil, sdkerrors.Wrap(err, "loading wasm code") 294 } 295 296 return &types.QueryCodeResponse{CodeInfoResponse: &info, Data: code}, nil 297 } 298 299 func (q grpcQuerier) PinnedCodes(c context.Context, req *types.QueryPinnedCodesRequest) (*types.QueryPinnedCodesResponse, error) { 300 if req == nil { 301 return nil, status.Error(codes.InvalidArgument, "empty request") 302 } 303 304 r := make([]uint64, 0) 305 prefixStore := q.PrefixStore(c, types.PinnedCodeIndexPrefix) 306 307 pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, _ []byte, accumulate bool) (bool, error) { 308 if accumulate { 309 r = append(r, sdk.BigEndianToUint64(key)) 310 } 311 return true, nil 312 }) 313 if err != nil { 314 return nil, err 315 } 316 return &types.QueryPinnedCodesResponse{ 317 CodeIDs: r, 318 Pagination: pageRes, 319 }, nil 320 } 321 322 func (q grpcQuerier) UnwrapSDKContext(c context.Context) sdk.Context { 323 if watcher.Enable() { 324 return proxy.MakeContext(q.storeKey) 325 } 326 return sdk.UnwrapSDKContext(c) 327 } 328 329 func (q grpcQuerier) PrefixStore(c context.Context, pre []byte) sdk.KVStore { 330 if watcher.Enable() { 331 return watcher.NewReadStore(pre) 332 } 333 ctx := sdk.UnwrapSDKContext(c) 334 return prefix.NewStore(ctx.KVStore(q.storeKey), pre) 335 336 }