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  }