github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/keeper/querier.go (about)

     1  package keeper
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     7  
     8  	ethcmn "github.com/ethereum/go-ethereum/common"
     9  	"github.com/ethereum/go-ethereum/crypto"
    10  	apptypes "github.com/fibonacci-chain/fbc/app/types"
    11  	"github.com/fibonacci-chain/fbc/app/utils"
    12  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt"
    14  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    15  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    16  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    17  	"github.com/fibonacci-chain/fbc/x/evm/types"
    18  )
    19  
    20  // NewQuerier is the module level router for state queries
    21  func NewQuerier(keeper Keeper) sdk.Querier {
    22  	return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
    23  		if len(path) < 1 {
    24  			return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
    25  				"Insufficient parameters, at least 1 parameter is required")
    26  		}
    27  
    28  		switch path[0] {
    29  		case types.QueryBalance:
    30  			return queryBalance(ctx, path, keeper)
    31  		case types.QueryBlockNumber:
    32  			return queryBlockNumber(ctx, keeper)
    33  		case types.QueryStorage:
    34  			return queryStorage(ctx, path, keeper)
    35  		case types.QueryStorageProof:
    36  			return queryStorageProof(ctx, path, keeper, req.Height)
    37  		case types.QueryStorageRoot:
    38  			return queryStorageRootHash(ctx, path, keeper, req.Height)
    39  		case types.QueryStorageByKey:
    40  			return queryStorageByKey(ctx, path, keeper)
    41  		case types.QueryCode:
    42  			return queryCode(ctx, path, keeper)
    43  		case types.QueryCodeByHash:
    44  			return queryCodeByHash(ctx, path, keeper)
    45  		case types.QueryHashToHeight:
    46  			return queryHashToHeight(ctx, path, keeper)
    47  		case types.QueryBloom:
    48  			return queryBlockBloom(ctx, path, keeper)
    49  		case types.QueryAccount:
    50  			return queryAccount(ctx, path, keeper)
    51  		case types.QueryExportAccount:
    52  			return queryExportAccount(ctx, path, keeper)
    53  		case types.QueryParameters:
    54  			return queryParams(ctx, keeper)
    55  		case types.QueryHeightToHash:
    56  			return queryHeightToHash(ctx, path, keeper)
    57  		case types.QuerySection:
    58  			return querySection(ctx, path, keeper)
    59  		case types.QueryContractDeploymentWhitelist:
    60  			return queryContractDeploymentWhitelist(ctx, keeper)
    61  		case types.QueryContractBlockedList:
    62  			return queryContractBlockedList(ctx, keeper)
    63  		case types.QueryContractMethodBlockedList:
    64  			return queryContractMethodBlockedList(ctx, keeper)
    65  		case types.QuerySysContractAddress:
    66  			return querySysContractAddress(ctx, keeper)
    67  		default:
    68  			return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint")
    69  		}
    70  	}
    71  }
    72  
    73  func queryContractMethodBlockedList(ctx sdk.Context, keeper Keeper) (res []byte, err sdk.Error) {
    74  	blockedList := types.CreateEmptyCommitStateDB(keeper.GeneratePureCSDBParams(), ctx).GetContractMethodBlockedList()
    75  	res, errUnmarshal := codec.MarshalJSONIndent(types.ModuleCdc, blockedList)
    76  	if errUnmarshal != nil {
    77  		return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errUnmarshal.Error()))
    78  	}
    79  
    80  	return res, nil
    81  }
    82  
    83  func queryContractBlockedList(ctx sdk.Context, keeper Keeper) (res []byte, err sdk.Error) {
    84  	blockedList := types.CreateEmptyCommitStateDB(keeper.GeneratePureCSDBParams(), ctx).GetContractBlockedList()
    85  	res, errUnmarshal := codec.MarshalJSONIndent(types.ModuleCdc, blockedList)
    86  	if errUnmarshal != nil {
    87  		return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errUnmarshal.Error()))
    88  	}
    89  
    90  	return res, nil
    91  }
    92  
    93  func queryContractDeploymentWhitelist(ctx sdk.Context, keeper Keeper) (res []byte, err sdk.Error) {
    94  	whitelist := types.CreateEmptyCommitStateDB(keeper.GeneratePureCSDBParams(), ctx).GetContractDeploymentWhitelist()
    95  	res, errUnmarshal := codec.MarshalJSONIndent(types.ModuleCdc, whitelist)
    96  	if errUnmarshal != nil {
    97  		return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errUnmarshal.Error()))
    98  	}
    99  
   100  	return res, nil
   101  }
   102  
   103  func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   104  	if len(path) < 2 {
   105  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   106  			"Insufficient parameters, at least 2 parameters is required")
   107  	}
   108  
   109  	addr := ethcmn.HexToAddress(path[1])
   110  	balance := keeper.GetBalance(ctx, addr)
   111  	balanceStr, err := utils.MarshalBigInt(balance)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	res := types.QueryResBalance{Balance: balanceStr}
   117  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   118  	if err != nil {
   119  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   120  	}
   121  
   122  	return bz, nil
   123  }
   124  
   125  func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, error) {
   126  	num := ctx.BlockHeight()
   127  	bnRes := types.QueryResBlockNumber{Number: num}
   128  	bz, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
   129  	if err != nil {
   130  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   131  	}
   132  
   133  	return bz, nil
   134  }
   135  
   136  func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   137  	if len(path) < 3 {
   138  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   139  			"Insufficient parameters, at least 3 parameters is required")
   140  	}
   141  
   142  	addr := ethcmn.HexToAddress(path[1])
   143  	key := ethcmn.HexToHash(path[2])
   144  	val := keeper.GetState(ctx, addr, key)
   145  	res := types.QueryResStorage{Value: val.Bytes()}
   146  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   147  	if err != nil {
   148  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   149  	}
   150  	return bz, nil
   151  }
   152  
   153  func queryStorageProof(ctx sdk.Context, path []string, keeper Keeper, height int64) ([]byte, error) {
   154  	if len(path) < 3 {
   155  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   156  			"Insufficient parameters, at least 3 parameters is required")
   157  	}
   158  
   159  	addr := ethcmn.HexToAddress(path[1])
   160  	storageRootHash, err := queryStorageRootBytesInHeight(keeper, addr, height)
   161  	if err != nil {
   162  		return nil, fmt.Errorf("get %s storage root hash failed: %s", addr, err.Error())
   163  	}
   164  
   165  	// check if storageRootHash is empty
   166  	var res types.QueryResStorageProof
   167  	if storageRootHash == nil {
   168  		res = types.QueryResStorageProof{Value: []byte{}, Proof: [][]byte{}}
   169  	} else {
   170  		// open storage trie base on storage root hash
   171  		storageTrie, err := keeper.db.OpenTrie(ethcmn.BytesToHash(storageRootHash))
   172  		if err != nil {
   173  			return nil, fmt.Errorf("open %s storage trie failed: %s", addr, err.Error())
   174  		}
   175  
   176  		// append key
   177  		key := ethcmn.HexToHash(path[2])
   178  		val, err := storageTrie.TryGet(crypto.Keccak256(append(addr.Bytes(), key.Bytes()...)))
   179  		if err != nil {
   180  			return nil, fmt.Errorf("get %s storage in location %s failed: %s", addr, key, err.Error())
   181  		}
   182  		// check if value is found
   183  		if val == nil {
   184  			res = types.QueryResStorageProof{Value: []byte{}, Proof: [][]byte{}}
   185  		} else {
   186  			var proof mpt.ProofList
   187  			if err = storageTrie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof); err != nil {
   188  				return nil, fmt.Errorf("trie generate proof failed: %s", err.Error())
   189  			}
   190  			res = types.QueryResStorageProof{Value: val, Proof: proof}
   191  		}
   192  	}
   193  
   194  	// marshal result
   195  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   196  	if err != nil {
   197  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   198  	}
   199  	return bz, nil
   200  }
   201  
   202  func queryStorageByKey(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   203  	if len(path) < 3 {
   204  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   205  			"Insufficient parameters, at least 3 parameters is required")
   206  	}
   207  
   208  	addr := ethcmn.HexToAddress(path[1])
   209  	key := ethcmn.HexToHash(path[2])
   210  	val := keeper.GetStateByKey(ctx, addr, key)
   211  	res := types.QueryResStorage{Value: val.Bytes()}
   212  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   213  	if err != nil {
   214  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   215  	}
   216  	return bz, nil
   217  }
   218  
   219  func queryStorageRootHash(ctx sdk.Context, path []string, keeper Keeper, height int64) ([]byte, error) {
   220  	if len(path) < 2 {
   221  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   222  			"Insufficient parameters, at least 1 parameters is required")
   223  	}
   224  
   225  	addr := ethcmn.HexToAddress(path[1])
   226  	storageRootHash, err := queryStorageRootBytesInHeight(keeper, addr, height)
   227  	if err != nil {
   228  		return nil, fmt.Errorf("get %s storage root hash failed: %s", addr, err.Error())
   229  	}
   230  
   231  	if storageRootHash == nil {
   232  		return mpt.EmptyRootHashBytes, nil
   233  	} else {
   234  		return storageRootHash, nil
   235  	}
   236  }
   237  
   238  func queryStorageRootBytesInHeight(keeper Keeper, addr ethcmn.Address, height int64) ([]byte, error) {
   239  	// query evm tire root hash based on height
   240  	evmRootHash := keeper.GetMptRootHash(uint64(height))
   241  	if evmRootHash == mpt.NilHash {
   242  		return nil, fmt.Errorf("header %d not found", height)
   243  	}
   244  
   245  	// query storage root hash base on address in evmTrie
   246  	evmTrie, err := keeper.db.OpenTrie(evmRootHash)
   247  	if err != nil {
   248  		return nil, fmt.Errorf("open evm trie failed: %s", err.Error())
   249  	}
   250  	storageRootHash, err := evmTrie.TryGet(addr.Bytes())
   251  	if err != nil {
   252  		return nil, fmt.Errorf("get %s storage root hash failed: %s", addr, err.Error())
   253  	}
   254  	return storageRootHash, nil
   255  }
   256  
   257  func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   258  	if len(path) < 2 {
   259  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   260  			"Insufficient parameters, at least 2 parameters is required")
   261  	}
   262  
   263  	addr := ethcmn.HexToAddress(path[1])
   264  	so := keeper.GetOrNewStateObject(ctx, addr)
   265  	code := keeper.GetCodeByHash(ctx, ethcmn.BytesToHash(so.CodeHash()))
   266  	res := types.QueryResCode{Code: code}
   267  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   268  	if err != nil {
   269  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   270  	}
   271  
   272  	return bz, nil
   273  }
   274  
   275  func queryCodeByHash(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   276  	if len(path) < 2 {
   277  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   278  			"Insufficient parameters, at least 2 parameters is required")
   279  	}
   280  
   281  	hash := ethcmn.HexToHash(path[1])
   282  	code := keeper.GetCodeByHash(ctx, hash)
   283  	res := types.QueryResCode{Code: code}
   284  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   285  	if err != nil {
   286  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   287  	}
   288  
   289  	return bz, nil
   290  }
   291  
   292  func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   293  	if len(path) < 2 {
   294  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   295  			"Insufficient parameters, at least 2 parameters is required")
   296  	}
   297  
   298  	blockHash := ethcmn.HexToHash(path[1])
   299  	blockNumber, found := keeper.GetBlockHeight(ctx, blockHash)
   300  	if !found {
   301  		return []byte{}, sdkerrors.Wrap(types.ErrKeyNotFound, fmt.Sprintf("block height not found for hash %s", path[1]))
   302  	}
   303  
   304  	res := types.QueryResBlockNumber{Number: blockNumber}
   305  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   306  	if err != nil {
   307  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   308  	}
   309  
   310  	return bz, nil
   311  }
   312  
   313  func queryBlockBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   314  	if len(path) < 2 {
   315  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   316  			"Insufficient parameters, at least 2 parameters is required")
   317  	}
   318  
   319  	num, err := strconv.ParseInt(path[1], 10, 64)
   320  	if err != nil {
   321  		return nil, sdkerrors.Wrap(types.ErrStrConvertFailed, fmt.Sprintf("could not unmarshal block height: %s", err))
   322  	}
   323  
   324  	copyCtx := ctx
   325  	copyCtx.SetBlockHeight(num)
   326  	bloom := keeper.GetBlockBloom(copyCtx, num)
   327  	res := types.QueryBloomFilter{Bloom: bloom}
   328  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   329  	if err != nil {
   330  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   331  	}
   332  
   333  	return bz, nil
   334  }
   335  
   336  func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   337  	if len(path) < 2 {
   338  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   339  			"Insufficient parameters, at least 2 parameters is required")
   340  	}
   341  
   342  	addr := ethcmn.HexToAddress(path[1])
   343  	res, err := resolveEthAccount(ctx, keeper, addr)
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  	bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
   348  	if err != nil {
   349  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   350  	}
   351  	return bz, nil
   352  }
   353  
   354  func resolveEthAccount(ctx sdk.Context, k Keeper, addr ethcmn.Address) (*types.QueryResAccount, error) {
   355  	codeHash := mpt.EmptyCodeHashBytes
   356  	account := k.accountKeeper.GetAccount(ctx, addr.Bytes())
   357  	if account == nil {
   358  		return &types.QueryResAccount{Nonce: uint64(0), CodeHash: codeHash, Balance: "0x0"}, nil
   359  	}
   360  	ethAccount := account.(*apptypes.EthAccount)
   361  	if ethAccount == nil {
   362  		return &types.QueryResAccount{Nonce: uint64(0), CodeHash: codeHash, Balance: "0x0"}, nil
   363  	}
   364  
   365  	// get balance
   366  	balance := ethAccount.Balance(sdk.DefaultBondDenom).BigInt()
   367  	if balance == nil {
   368  		balance = sdk.ZeroInt().BigInt()
   369  	}
   370  	balanceStr, err := utils.MarshalBigInt(balance)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  
   375  	// get codeHash
   376  	if ethAccount.CodeHash != nil {
   377  		codeHash = ethAccount.CodeHash
   378  	}
   379  
   380  	//return
   381  	return &types.QueryResAccount{
   382  		Balance:  balanceStr,
   383  		CodeHash: codeHash,
   384  		Nonce:    ethAccount.Sequence,
   385  	}, nil
   386  }
   387  
   388  func queryExportAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   389  	if len(path) < 2 {
   390  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   391  			"Insufficient parameters, at least 2 parameters is required")
   392  	}
   393  
   394  	hexAddress := path[1]
   395  	addr := ethcmn.HexToAddress(hexAddress)
   396  
   397  	var storage types.Storage
   398  	err := keeper.ForEachStorage(ctx, addr, func(key, value ethcmn.Hash) bool {
   399  		storage = append(storage, types.NewState(key, value))
   400  		return false
   401  	})
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  
   406  	res := types.GenesisAccount{
   407  		Address: hexAddress,
   408  		Code:    keeper.GetCode(ctx, addr),
   409  		Storage: storage,
   410  	}
   411  
   412  	// TODO: codec.MarshalJSONIndent doesn't call the String() method of types properly
   413  	bz, err := json.MarshalIndent(res, "", "\t")
   414  	if err != nil {
   415  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
   416  	}
   417  
   418  	return bz, nil
   419  }
   420  
   421  func queryParams(ctx sdk.Context, keeper Keeper) (res []byte, err sdk.Error) {
   422  	params := keeper.GetParams(ctx)
   423  	res, errUnmarshal := codec.MarshalJSONIndent(types.ModuleCdc, params)
   424  	if errUnmarshal != nil {
   425  		return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", errUnmarshal.Error()))
   426  	}
   427  	return res, nil
   428  }
   429  
   430  func queryHeightToHash(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   431  	if len(path) < 2 {
   432  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   433  			"Insufficient parameters, at least 2 parameters is required")
   434  	}
   435  
   436  	height, err := strconv.Atoi(path[1])
   437  	if err != nil {
   438  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   439  			"Insufficient parameters, params[1] convert to int failed")
   440  	}
   441  	hash := keeper.GetHeightHash(ctx, uint64(height))
   442  
   443  	return hash.Bytes(), nil
   444  }
   445  
   446  func querySection(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
   447  	if !types.GetEnableBloomFilter() {
   448  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   449  			"disable bloom filter")
   450  	}
   451  
   452  	if len(path) != 1 {
   453  		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
   454  			"wrong parameters, need no parameters")
   455  	}
   456  
   457  	res, err := json.Marshal(types.GetIndexer().StoredSection())
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  
   462  	return res, nil
   463  }