github.com/iotexproject/iotex-core@v1.14.1-rc1/api/grpcserver.go (about)

     1  // Copyright (c) 2022 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package api
     7  
     8  import (
     9  	"context"
    10  	"encoding/hex"
    11  	"fmt"
    12  	"math"
    13  	"math/big"
    14  	"net"
    15  	"strconv"
    16  	"time"
    17  
    18  	"github.com/ethereum/go-ethereum/eth/tracers"
    19  	"github.com/ethereum/go-ethereum/eth/tracers/logger"
    20  	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
    21  	grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
    22  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
    23  	"github.com/iotexproject/go-pkgs/hash"
    24  	"github.com/iotexproject/iotex-address/address"
    25  	"github.com/iotexproject/iotex-proto/golang/iotexapi"
    26  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    27  	"github.com/pkg/errors"
    28  	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
    29  	"go.opentelemetry.io/otel/attribute"
    30  	"go.uber.org/zap"
    31  	"google.golang.org/grpc"
    32  	"google.golang.org/grpc/codes"
    33  	"google.golang.org/grpc/health"
    34  	"google.golang.org/grpc/health/grpc_health_v1"
    35  	"google.golang.org/grpc/keepalive"
    36  	"google.golang.org/grpc/reflection"
    37  	"google.golang.org/grpc/status"
    38  	"google.golang.org/protobuf/types/known/timestamppb"
    39  
    40  	"github.com/iotexproject/iotex-core/action"
    41  	"github.com/iotexproject/iotex-core/api/logfilter"
    42  	apitypes "github.com/iotexproject/iotex-core/api/types"
    43  	"github.com/iotexproject/iotex-core/blockchain/block"
    44  	"github.com/iotexproject/iotex-core/pkg/log"
    45  	"github.com/iotexproject/iotex-core/pkg/recovery"
    46  	"github.com/iotexproject/iotex-core/pkg/tracer"
    47  )
    48  
    49  type (
    50  	// GRPCServer contains grpc server
    51  	GRPCServer struct {
    52  		port string
    53  		svr  *grpc.Server
    54  	}
    55  
    56  	// GRPCHandler contains the pointer to api coreservice
    57  	gRPCHandler struct {
    58  		coreService CoreService
    59  	}
    60  )
    61  
    62  // TODO: move this into config
    63  var (
    64  	kaep = keepalive.EnforcementPolicy{
    65  		MinTime:             1 * time.Second, // If a client pings more than once every 1 seconds, terminate the connection
    66  		PermitWithoutStream: true,            // Allow pings even when there are no active streams
    67  	}
    68  	kasp = keepalive.ServerParameters{
    69  		Time:              60 * time.Second, // Ping the client if it is idle for 60 seconds to ensure the connection is still active
    70  		Timeout:           10 * time.Second, // Wait 10 seconds for the ping ack before assuming the connection is dead
    71  		MaxConnectionIdle: 5 * time.Minute,  // If a client is idle for 5 minutes, send a GOAWAY
    72  	}
    73  )
    74  
    75  // RecoveryInterceptor handles panic to a custom error
    76  func RecoveryInterceptor() grpc_recovery.Option {
    77  	return grpc_recovery.WithRecoveryHandler(func(p interface{}) (err error) {
    78  		recovery.LogCrash(p)
    79  		return grpc.Errorf(codes.Unknown, "grpc triggered crash: %v", p)
    80  	})
    81  }
    82  
    83  // NewGRPCServer creates a new grpc server
    84  func NewGRPCServer(core CoreService, grpcPort int) *GRPCServer {
    85  	if grpcPort == 0 {
    86  		return nil
    87  	}
    88  
    89  	gSvr := grpc.NewServer(
    90  		grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
    91  			grpc_prometheus.StreamServerInterceptor,
    92  			otelgrpc.StreamServerInterceptor(),
    93  			grpc_recovery.StreamServerInterceptor(RecoveryInterceptor()),
    94  		)),
    95  		grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
    96  			grpc_prometheus.UnaryServerInterceptor,
    97  			otelgrpc.UnaryServerInterceptor(),
    98  			grpc_recovery.UnaryServerInterceptor(RecoveryInterceptor()),
    99  		)),
   100  		grpc.KeepaliveEnforcementPolicy(kaep),
   101  		grpc.KeepaliveParams(kasp),
   102  	)
   103  
   104  	//serviceName: grpc.health.v1.Health
   105  	grpc_health_v1.RegisterHealthServer(gSvr, health.NewServer())
   106  	iotexapi.RegisterAPIServiceServer(gSvr, newGRPCHandler(core))
   107  	grpc_prometheus.Register(gSvr)
   108  	reflection.Register(gSvr)
   109  	return &GRPCServer{
   110  		port: ":" + strconv.Itoa(grpcPort),
   111  		svr:  gSvr,
   112  	}
   113  }
   114  
   115  // Start starts the GRPC server
   116  func (grpc *GRPCServer) Start(_ context.Context) error {
   117  	lis, err := net.Listen("tcp", grpc.port)
   118  	if err != nil {
   119  		log.L().Error("grpc server failed to listen.", zap.Error(err))
   120  		return errors.Wrap(err, "grpc server failed to listen")
   121  	}
   122  	log.L().Info("grpc server is listening.", zap.String("addr", lis.Addr().String()))
   123  	go func() {
   124  		defer recovery.Recover()
   125  		if err := grpc.svr.Serve(lis); err != nil {
   126  			log.L().Fatal("grpc failed to serve.", zap.Error(err))
   127  		}
   128  	}()
   129  	return nil
   130  }
   131  
   132  // Stop stops the GRPC server
   133  func (grpc *GRPCServer) Stop(_ context.Context) error {
   134  	grpc.svr.Stop()
   135  	return nil
   136  }
   137  
   138  func newGRPCHandler(core CoreService) *gRPCHandler {
   139  	return &gRPCHandler{
   140  		coreService: core,
   141  	}
   142  }
   143  
   144  // SuggestGasPrice suggests gas price
   145  func (svr *gRPCHandler) SuggestGasPrice(ctx context.Context, in *iotexapi.SuggestGasPriceRequest) (*iotexapi.SuggestGasPriceResponse, error) {
   146  	suggestPrice, err := svr.coreService.SuggestGasPrice()
   147  	if err != nil {
   148  		return nil, status.Error(codes.Internal, err.Error())
   149  	}
   150  	return &iotexapi.SuggestGasPriceResponse{GasPrice: suggestPrice}, nil
   151  }
   152  
   153  // GetAccount returns the metadata of an account
   154  func (svr *gRPCHandler) GetAccount(ctx context.Context, in *iotexapi.GetAccountRequest) (*iotexapi.GetAccountResponse, error) {
   155  	span := tracer.SpanFromContext(ctx)
   156  	defer span.End()
   157  	addr, err := address.FromString(in.Address)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	accountMeta, blockIdentifier, err := svr.coreService.Account(addr)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	span.AddEvent("response")
   166  	span.SetAttributes(attribute.String("addr", in.Address))
   167  	return &iotexapi.GetAccountResponse{
   168  		AccountMeta:     accountMeta,
   169  		BlockIdentifier: blockIdentifier,
   170  	}, nil
   171  }
   172  
   173  // GetActions returns actions
   174  func (svr *gRPCHandler) GetActions(ctx context.Context, in *iotexapi.GetActionsRequest) (*iotexapi.GetActionsResponse, error) {
   175  	var (
   176  		ret []*iotexapi.ActionInfo
   177  		err error
   178  	)
   179  	switch {
   180  	case in.GetByIndex() != nil:
   181  		request := in.GetByIndex()
   182  		ret, err = svr.coreService.Actions(request.Start, request.Count)
   183  	case in.GetByHash() != nil:
   184  		var act *iotexapi.ActionInfo
   185  		request := in.GetByHash()
   186  		act, err = svr.coreService.Action(request.ActionHash, request.CheckPending)
   187  		ret = []*iotexapi.ActionInfo{act}
   188  	case in.GetByAddr() != nil:
   189  		request := in.GetByAddr()
   190  		var addr address.Address
   191  		addr, err = address.FromString(request.Address)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		ret, err = svr.coreService.ActionsByAddress(addr, request.Start, request.Count)
   196  	case in.GetUnconfirmedByAddr() != nil:
   197  		request := in.GetUnconfirmedByAddr()
   198  		ret, err = svr.coreService.UnconfirmedActionsByAddress(request.Address, request.Start, request.Count)
   199  	case in.GetByBlk() != nil:
   200  		var (
   201  			request = in.GetByBlk()
   202  			blk     *apitypes.BlockWithReceipts
   203  		)
   204  		blk, err = svr.coreService.BlockByHash(request.BlkHash)
   205  		if err != nil {
   206  			break
   207  		}
   208  		ret, err = actionsInBlock(blk.Block, blk.Receipts, request.Start, request.Count)
   209  	default:
   210  		return nil, status.Error(codes.NotFound, "invalid GetActionsRequest type")
   211  	}
   212  	if err != nil {
   213  		return nil, status.Error(codes.NotFound, err.Error())
   214  	}
   215  	return &iotexapi.GetActionsResponse{
   216  		Total:      uint64(len(ret)),
   217  		ActionInfo: ret,
   218  	}, nil
   219  }
   220  
   221  func actionsInBlock(blk *block.Block, receipts []*action.Receipt, start, count uint64) ([]*iotexapi.ActionInfo, error) {
   222  	var res []*iotexapi.ActionInfo
   223  	if len(blk.Actions) == 0 {
   224  		return res, nil
   225  	}
   226  	if count == 0 {
   227  		return nil, status.Error(codes.InvalidArgument, "count must be greater than zero")
   228  	}
   229  	if start >= uint64(len(blk.Actions)) {
   230  		return nil, status.Error(codes.InvalidArgument, "start exceeds the limit")
   231  	}
   232  
   233  	h := blk.HashBlock()
   234  	blkHash := hex.EncodeToString(h[:])
   235  	blkHeight := blk.Height()
   236  
   237  	lastAction := start + count
   238  	if count == math.MaxUint64 {
   239  		// count = -1 means to get all actions
   240  		lastAction = uint64(len(blk.Actions))
   241  	} else {
   242  		if lastAction >= uint64(len(blk.Actions)) {
   243  			lastAction = uint64(len(blk.Actions))
   244  		}
   245  	}
   246  	for i := start; i < lastAction; i++ {
   247  		selp, receipt := blk.Actions[i], receipts[i]
   248  		actHash, err := selp.Hash()
   249  		if err != nil {
   250  			log.Logger("api").Debug("Skipping action due to hash error", zap.Error(err))
   251  			continue
   252  		}
   253  		gas := new(big.Int).Mul(selp.GasPrice(), big.NewInt(int64(receipt.GasConsumed)))
   254  		sender := selp.SenderAddress()
   255  		res = append(res, &iotexapi.ActionInfo{
   256  			Action:    selp.Proto(),
   257  			ActHash:   hex.EncodeToString(actHash[:]),
   258  			BlkHash:   blkHash,
   259  			Timestamp: blk.Header.BlockHeaderCoreProto().Timestamp,
   260  			BlkHeight: blkHeight,
   261  			Sender:    sender.String(),
   262  			GasFee:    gas.String(),
   263  			Index:     uint32(i),
   264  		})
   265  	}
   266  	return res, nil
   267  }
   268  
   269  // GetBlockMetas returns block metadata
   270  func (svr *gRPCHandler) GetBlockMetas(ctx context.Context, in *iotexapi.GetBlockMetasRequest) (*iotexapi.GetBlockMetasResponse, error) {
   271  	var ret []*iotextypes.BlockMeta
   272  	switch {
   273  	case in.GetByIndex() != nil:
   274  		request := in.GetByIndex()
   275  		blkStores, err := svr.coreService.BlockByHeightRange(request.Start, request.Count)
   276  		if err != nil {
   277  			return nil, status.Error(codes.NotFound, err.Error())
   278  		}
   279  		for _, blkStore := range blkStores {
   280  			ret = append(ret, generateBlockMeta(blkStore))
   281  		}
   282  	case in.GetByHash() != nil:
   283  		blk, err := svr.coreService.BlockByHash(in.GetByHash().BlkHash)
   284  		if err != nil {
   285  			return nil, status.Error(codes.NotFound, err.Error())
   286  		}
   287  		ret = []*iotextypes.BlockMeta{generateBlockMeta(blk)}
   288  	default:
   289  		return nil, status.Error(codes.NotFound, "invalid GetBlockMetasRequest type")
   290  	}
   291  
   292  	return &iotexapi.GetBlockMetasResponse{
   293  		Total:    uint64(len(ret)),
   294  		BlkMetas: ret,
   295  	}, nil
   296  }
   297  
   298  // GetChainMeta returns blockchain metadata
   299  func (svr *gRPCHandler) GetChainMeta(ctx context.Context, in *iotexapi.GetChainMetaRequest) (*iotexapi.GetChainMetaResponse, error) {
   300  	chainMeta, syncStatus, err := svr.coreService.ChainMeta()
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	return &iotexapi.GetChainMetaResponse{ChainMeta: chainMeta, SyncStage: syncStatus}, nil
   305  }
   306  
   307  // GetServerMeta gets the server metadata
   308  func (svr *gRPCHandler) GetServerMeta(ctx context.Context, in *iotexapi.GetServerMetaRequest) (*iotexapi.GetServerMetaResponse, error) {
   309  	packageVersion, packageCommitID, gitStatus, goVersion, buildTime := svr.coreService.ServerMeta()
   310  	return &iotexapi.GetServerMetaResponse{ServerMeta: &iotextypes.ServerMeta{
   311  		PackageVersion:  packageVersion,
   312  		PackageCommitID: packageCommitID,
   313  		GitStatus:       gitStatus,
   314  		GoVersion:       goVersion,
   315  		BuildTime:       buildTime,
   316  	}}, nil
   317  }
   318  
   319  // SendAction is the API to send an action to blockchain.
   320  func (svr *gRPCHandler) SendAction(ctx context.Context, in *iotexapi.SendActionRequest) (*iotexapi.SendActionResponse, error) {
   321  	span := tracer.SpanFromContext(ctx)
   322  	// tags output
   323  	span.SetAttributes(attribute.String("actType", fmt.Sprintf("%T", in.GetAction().GetCore())))
   324  	defer span.End()
   325  	actHash, err := svr.coreService.SendAction(ctx, in.GetAction())
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return &iotexapi.SendActionResponse{ActionHash: actHash}, nil
   330  }
   331  
   332  // GetReceiptByAction gets receipt with corresponding action hash
   333  func (svr *gRPCHandler) GetReceiptByAction(ctx context.Context, in *iotexapi.GetReceiptByActionRequest) (*iotexapi.GetReceiptByActionResponse, error) {
   334  	actHash, err := hash.HexStringToHash256(in.ActionHash)
   335  	if err != nil {
   336  		return nil, status.Error(codes.InvalidArgument, err.Error())
   337  	}
   338  	receipt, err := svr.coreService.ReceiptByActionHash(actHash)
   339  	if err != nil {
   340  		return nil, status.Error(codes.NotFound, err.Error())
   341  	}
   342  	blkHash, err := svr.coreService.BlockHashByBlockHeight(receipt.BlockHeight)
   343  	if err != nil {
   344  		return nil, status.Error(codes.NotFound, err.Error())
   345  	}
   346  
   347  	return &iotexapi.GetReceiptByActionResponse{
   348  		ReceiptInfo: &iotexapi.ReceiptInfo{
   349  			Receipt: receipt.ConvertToReceiptPb(),
   350  			BlkHash: hex.EncodeToString(blkHash[:]),
   351  		},
   352  	}, nil
   353  }
   354  
   355  // ReadContract reads the state in a contract address specified by the slot
   356  func (svr *gRPCHandler) ReadContract(ctx context.Context, in *iotexapi.ReadContractRequest) (*iotexapi.ReadContractResponse, error) {
   357  	from := in.CallerAddress
   358  	if from == action.EmptyAddress {
   359  		from = address.ZeroAddress
   360  	}
   361  	callerAddr, err := address.FromString(from)
   362  	if err != nil {
   363  		return nil, status.Error(codes.InvalidArgument, err.Error())
   364  	}
   365  	sc := &action.Execution{}
   366  	if err := sc.LoadProto(in.GetExecution()); err != nil {
   367  		return nil, status.Error(codes.InvalidArgument, err.Error())
   368  	}
   369  	sc.SetGasLimit(in.GetGasLimit())
   370  
   371  	data, receipt, err := svr.coreService.ReadContract(ctx, callerAddr, sc)
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  	return &iotexapi.ReadContractResponse{
   376  		Data:    data,
   377  		Receipt: receipt,
   378  	}, nil
   379  }
   380  
   381  // ReadState reads state on blockchain
   382  func (svr *gRPCHandler) ReadState(ctx context.Context, in *iotexapi.ReadStateRequest) (*iotexapi.ReadStateResponse, error) {
   383  	return svr.coreService.ReadState(string(in.ProtocolID), in.GetHeight(), in.MethodName, in.Arguments)
   384  }
   385  
   386  // EstimateGasForAction estimates gas for action
   387  func (svr *gRPCHandler) EstimateGasForAction(ctx context.Context, in *iotexapi.EstimateGasForActionRequest) (*iotexapi.EstimateGasForActionResponse, error) {
   388  	estimateGas, err := svr.coreService.EstimateGasForAction(ctx, in.Action)
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	return &iotexapi.EstimateGasForActionResponse{Gas: estimateGas}, nil
   393  }
   394  
   395  // EstimateActionGasConsumption estimate gas consume for action without signature
   396  func (svr *gRPCHandler) EstimateActionGasConsumption(ctx context.Context, in *iotexapi.EstimateActionGasConsumptionRequest) (*iotexapi.EstimateActionGasConsumptionResponse, error) {
   397  	if in.GetExecution() != nil {
   398  		callerAddr, err := address.FromString(in.GetCallerAddress())
   399  		if err != nil {
   400  			return nil, status.Error(codes.InvalidArgument, err.Error())
   401  		}
   402  		sc := &action.Execution{}
   403  		if err := sc.LoadProto(in.GetExecution()); err != nil {
   404  			return nil, status.Error(codes.InvalidArgument, err.Error())
   405  		}
   406  		var (
   407  			gasPrice *big.Int = big.NewInt(0)
   408  			ok       bool
   409  		)
   410  		if in.GetGasPrice() != "" {
   411  			gasPrice, ok = big.NewInt(0).SetString(in.GetGasPrice(), 10)
   412  			if !ok {
   413  				return nil, status.Error(codes.InvalidArgument, "invalid gas price")
   414  			}
   415  		}
   416  		sc.SetGasPrice(gasPrice)
   417  		ret, err := svr.coreService.EstimateExecutionGasConsumption(ctx, sc, callerAddr)
   418  		if err != nil {
   419  			return nil, err
   420  		}
   421  		return &iotexapi.EstimateActionGasConsumptionResponse{Gas: ret}, nil
   422  	}
   423  	var act action.Action
   424  	switch {
   425  	case in.GetTransfer() != nil:
   426  		tmpAct := &action.Transfer{}
   427  		if err := tmpAct.LoadProto(in.GetTransfer()); err != nil {
   428  			return nil, status.Error(codes.InvalidArgument, err.Error())
   429  		}
   430  		act = tmpAct
   431  	case in.GetStakeCreate() != nil:
   432  		tmpAct := &action.CreateStake{}
   433  		if err := tmpAct.LoadProto(in.GetStakeCreate()); err != nil {
   434  			return nil, status.Error(codes.InvalidArgument, err.Error())
   435  		}
   436  		act = tmpAct
   437  	case in.GetStakeUnstake() != nil:
   438  		tmpAct := &action.Unstake{}
   439  		if err := tmpAct.LoadProto(in.GetStakeUnstake()); err != nil {
   440  			return nil, status.Error(codes.InvalidArgument, err.Error())
   441  		}
   442  		act = tmpAct
   443  	case in.GetStakeWithdraw() != nil:
   444  		tmpAct := &action.WithdrawStake{}
   445  		if err := tmpAct.LoadProto(in.GetStakeWithdraw()); err != nil {
   446  			return nil, status.Error(codes.InvalidArgument, err.Error())
   447  		}
   448  		act = tmpAct
   449  	case in.GetStakeAddDeposit() != nil:
   450  		tmpAct := &action.DepositToStake{}
   451  		if err := tmpAct.LoadProto(in.GetStakeAddDeposit()); err != nil {
   452  			return nil, status.Error(codes.InvalidArgument, err.Error())
   453  		}
   454  		act = tmpAct
   455  	case in.GetStakeRestake() != nil:
   456  		tmpAct := &action.Restake{}
   457  		if err := tmpAct.LoadProto(in.GetStakeRestake()); err != nil {
   458  			return nil, status.Error(codes.InvalidArgument, err.Error())
   459  		}
   460  		act = tmpAct
   461  	case in.GetStakeChangeCandidate() != nil:
   462  		tmpAct := &action.ChangeCandidate{}
   463  		if err := tmpAct.LoadProto(in.GetStakeChangeCandidate()); err != nil {
   464  			return nil, status.Error(codes.InvalidArgument, err.Error())
   465  		}
   466  		act = tmpAct
   467  	case in.GetStakeTransferOwnership() != nil:
   468  		tmpAct := &action.TransferStake{}
   469  		if err := tmpAct.LoadProto(in.GetStakeTransferOwnership()); err != nil {
   470  			return nil, status.Error(codes.InvalidArgument, err.Error())
   471  		}
   472  		act = tmpAct
   473  	case in.GetCandidateRegister() != nil:
   474  		tmpAct := &action.CandidateRegister{}
   475  		if err := tmpAct.LoadProto(in.GetCandidateRegister()); err != nil {
   476  			return nil, status.Error(codes.InvalidArgument, err.Error())
   477  		}
   478  		act = tmpAct
   479  	case in.GetCandidateUpdate() != nil:
   480  		tmpAct := &action.CandidateUpdate{}
   481  		if err := tmpAct.LoadProto(in.GetCandidateUpdate()); err != nil {
   482  			return nil, status.Error(codes.InvalidArgument, err.Error())
   483  		}
   484  		act = tmpAct
   485  	default:
   486  		return nil, status.Error(codes.InvalidArgument, "invalid argument")
   487  	}
   488  	estimatedGas, err := svr.coreService.EstimateGasForNonExecution(act)
   489  	if err != nil {
   490  		return nil, status.Error(codes.InvalidArgument, err.Error())
   491  	}
   492  	return &iotexapi.EstimateActionGasConsumptionResponse{Gas: estimatedGas}, nil
   493  }
   494  
   495  // GetEpochMeta gets epoch metadata
   496  func (svr *gRPCHandler) GetEpochMeta(ctx context.Context, in *iotexapi.GetEpochMetaRequest) (*iotexapi.GetEpochMetaResponse, error) {
   497  	epochData, numBlks, blockProducersInfo, err := svr.coreService.EpochMeta(in.EpochNumber)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  	return &iotexapi.GetEpochMetaResponse{
   502  		EpochData:          epochData,
   503  		TotalBlocks:        numBlks,
   504  		BlockProducersInfo: blockProducersInfo,
   505  	}, nil
   506  }
   507  
   508  // GetRawBlocks gets raw block data
   509  func (svr *gRPCHandler) GetRawBlocks(ctx context.Context, in *iotexapi.GetRawBlocksRequest) (*iotexapi.GetRawBlocksResponse, error) {
   510  	ret, err := svr.coreService.RawBlocks(in.StartHeight, in.Count, in.WithReceipts, in.WithTransactionLogs)
   511  	if err != nil {
   512  		return nil, err
   513  	}
   514  	return &iotexapi.GetRawBlocksResponse{Blocks: ret}, nil
   515  }
   516  
   517  // GetLogs get logs filtered by contract address and topics
   518  func (svr *gRPCHandler) GetLogs(ctx context.Context, in *iotexapi.GetLogsRequest) (*iotexapi.GetLogsResponse, error) {
   519  	if in.GetFilter() == nil {
   520  		return nil, status.Error(codes.InvalidArgument, "empty filter")
   521  	}
   522  	var (
   523  		ret = make([]*iotextypes.Log, 0)
   524  	)
   525  	switch {
   526  	case in.GetByBlock() != nil:
   527  		blkHash := hash.BytesToHash256(in.GetByBlock().BlockHash)
   528  		logs, err := svr.coreService.LogsInBlockByHash(logfilter.NewLogFilter(in.GetFilter()), blkHash)
   529  		if err != nil {
   530  			return nil, status.Error(codes.InvalidArgument, err.Error())
   531  		}
   532  		for i := range logs {
   533  			ret = append(ret, toLogPb(logs[i], blkHash))
   534  		}
   535  	case in.GetByRange() != nil:
   536  		req := in.GetByRange()
   537  		logs, hashes, err := svr.coreService.LogsInRange(logfilter.NewLogFilter(in.GetFilter()), req.GetFromBlock(), req.GetToBlock(), req.GetPaginationSize())
   538  		if err != nil {
   539  			return nil, status.Error(codes.InvalidArgument, err.Error())
   540  		}
   541  		for i := range logs {
   542  			ret = append(ret, toLogPb(logs[i], hashes[i]))
   543  		}
   544  	default:
   545  		return nil, status.Error(codes.InvalidArgument, "invalid GetLogsRequest type")
   546  	}
   547  	return &iotexapi.GetLogsResponse{Logs: ret}, nil
   548  }
   549  
   550  func toLogPb(lg *action.Log, blkHash hash.Hash256) *iotextypes.Log {
   551  	logPb := lg.ConvertToLogPb()
   552  	logPb.BlkHash = blkHash[:]
   553  	return logPb
   554  }
   555  
   556  // StreamBlocks streams blocks
   557  func (svr *gRPCHandler) StreamBlocks(_ *iotexapi.StreamBlocksRequest, stream iotexapi.APIService_StreamBlocksServer) error {
   558  	errChan := make(chan error)
   559  	defer close(errChan)
   560  	chainListener := svr.coreService.ChainListener()
   561  	if _, err := chainListener.AddResponder(NewGRPCBlockListener(
   562  		func(resp interface{}) (int, error) {
   563  			return 0, stream.Send(resp.(*iotexapi.StreamBlocksResponse))
   564  		},
   565  		errChan,
   566  	)); err != nil {
   567  		return status.Error(codes.Internal, err.Error())
   568  	}
   569  	err := <-errChan
   570  	if err != nil {
   571  		return status.Error(codes.Aborted, err.Error())
   572  	}
   573  	return nil
   574  }
   575  
   576  // StreamLogs streams logs that match the filter condition
   577  func (svr *gRPCHandler) StreamLogs(in *iotexapi.StreamLogsRequest, stream iotexapi.APIService_StreamLogsServer) error {
   578  	if in.GetFilter() == nil {
   579  		return status.Error(codes.InvalidArgument, "empty filter")
   580  	}
   581  	errChan := make(chan error)
   582  	defer close(errChan)
   583  	chainListener := svr.coreService.ChainListener()
   584  	if _, err := chainListener.AddResponder(NewGRPCLogListener(
   585  		logfilter.NewLogFilter(in.GetFilter()),
   586  		func(in interface{}) (int, error) {
   587  			return 0, stream.Send(in.(*iotexapi.StreamLogsResponse))
   588  		},
   589  		errChan,
   590  	)); err != nil {
   591  		return status.Error(codes.Internal, err.Error())
   592  	}
   593  	err := <-errChan
   594  	if err != nil {
   595  		return status.Error(codes.Aborted, err.Error())
   596  	}
   597  	return nil
   598  }
   599  
   600  // GetElectionBuckets returns the native election buckets.
   601  func (svr *gRPCHandler) GetElectionBuckets(ctx context.Context, in *iotexapi.GetElectionBucketsRequest) (*iotexapi.GetElectionBucketsResponse, error) {
   602  	ret, err := svr.coreService.ElectionBuckets(in.GetEpochNum())
   603  	if err != nil {
   604  		return nil, err
   605  	}
   606  	return &iotexapi.GetElectionBucketsResponse{Buckets: ret}, nil
   607  }
   608  
   609  // GetEvmTransfersByActionHash returns evm transfers by action hash
   610  func (svr *gRPCHandler) GetEvmTransfersByActionHash(ctx context.Context, in *iotexapi.GetEvmTransfersByActionHashRequest) (*iotexapi.GetEvmTransfersByActionHashResponse, error) {
   611  	return nil, status.Error(codes.Unimplemented, "evm transfer index is deprecated, call GetSystemLogByActionHash instead")
   612  }
   613  
   614  // GetEvmTransfersByBlockHeight returns evm transfers by block height
   615  func (svr *gRPCHandler) GetEvmTransfersByBlockHeight(ctx context.Context, in *iotexapi.GetEvmTransfersByBlockHeightRequest) (*iotexapi.GetEvmTransfersByBlockHeightResponse, error) {
   616  	return nil, status.Error(codes.Unimplemented, "evm transfer index is deprecated, call GetSystemLogByBlockHeight instead")
   617  }
   618  
   619  // GetTransactionLogByActionHash returns transaction log by action hash
   620  func (svr *gRPCHandler) GetTransactionLogByActionHash(ctx context.Context, in *iotexapi.GetTransactionLogByActionHashRequest) (*iotexapi.GetTransactionLogByActionHashResponse, error) {
   621  	ret, err := svr.coreService.TransactionLogByActionHash(in.ActionHash)
   622  	if err != nil {
   623  		return nil, err
   624  	}
   625  	return &iotexapi.GetTransactionLogByActionHashResponse{
   626  		TransactionLog: ret,
   627  	}, nil
   628  }
   629  
   630  // GetTransactionLogByBlockHeight returns transaction log by block height
   631  func (svr *gRPCHandler) GetTransactionLogByBlockHeight(ctx context.Context, in *iotexapi.GetTransactionLogByBlockHeightRequest) (*iotexapi.GetTransactionLogByBlockHeightResponse, error) {
   632  	blockIdentifier, transactionLogs, err := svr.coreService.TransactionLogByBlockHeight(in.BlockHeight)
   633  	if err != nil {
   634  		return nil, err
   635  	}
   636  	return &iotexapi.GetTransactionLogByBlockHeightResponse{
   637  		BlockIdentifier: blockIdentifier,
   638  		TransactionLogs: transactionLogs,
   639  	}, nil
   640  }
   641  
   642  // GetActPoolActions returns the all Transaction Identifiers in the mempool
   643  func (svr *gRPCHandler) GetActPoolActions(ctx context.Context, in *iotexapi.GetActPoolActionsRequest) (*iotexapi.GetActPoolActionsResponse, error) {
   644  	acts, err := svr.coreService.ActionsInActPool(in.ActionHashes)
   645  	if err != nil {
   646  		return nil, status.Error(codes.NotFound, err.Error())
   647  	}
   648  	ret := make([]*iotextypes.Action, 0)
   649  	for _, act := range acts {
   650  		ret = append(ret, act.Proto())
   651  	}
   652  	return &iotexapi.GetActPoolActionsResponse{
   653  		Actions: ret,
   654  	}, nil
   655  }
   656  
   657  // ReadContractStorage reads contract's storage
   658  func (svr *gRPCHandler) ReadContractStorage(ctx context.Context, in *iotexapi.ReadContractStorageRequest) (*iotexapi.ReadContractStorageResponse, error) {
   659  	addr, err := address.FromString(in.GetContract())
   660  	if err != nil {
   661  		return nil, status.Error(codes.InvalidArgument, err.Error())
   662  	}
   663  	b, err := svr.coreService.ReadContractStorage(ctx, addr, in.GetKey())
   664  	if err != nil {
   665  		return nil, status.Error(codes.Internal, err.Error())
   666  	}
   667  	return &iotexapi.ReadContractStorageResponse{Data: b}, nil
   668  }
   669  
   670  // TraceTransactionStructLogs get trace transaction struct logs
   671  func (svr *gRPCHandler) TraceTransactionStructLogs(ctx context.Context, in *iotexapi.TraceTransactionStructLogsRequest) (*iotexapi.TraceTransactionStructLogsResponse, error) {
   672  	cfg := &tracers.TraceConfig{
   673  		Config: &logger.Config{
   674  			EnableMemory:     true,
   675  			DisableStack:     false,
   676  			DisableStorage:   false,
   677  			EnableReturnData: true,
   678  		},
   679  	}
   680  	_, _, tracer, err := svr.coreService.TraceTransaction(ctx, in.GetActionHash(), cfg)
   681  	if err != nil {
   682  		return nil, status.Error(codes.Internal, err.Error())
   683  	}
   684  	structLogs := make([]*iotextypes.TransactionStructLog, 0)
   685  	//grpc not support javascript tracing, so we only return native traces
   686  	traces := tracer.(*logger.StructLogger)
   687  	for _, log := range traces.StructLogs() {
   688  		var stack []string
   689  		for _, s := range log.Stack {
   690  			stack = append(stack, s.String())
   691  		}
   692  		structLogs = append(structLogs, &iotextypes.TransactionStructLog{
   693  			Pc:         log.Pc,
   694  			Op:         uint64(log.Op),
   695  			Gas:        log.Gas,
   696  			GasCost:    log.GasCost,
   697  			Memory:     fmt.Sprintf("%#x", log.Memory),
   698  			MemSize:    int32(log.MemorySize),
   699  			Stack:      stack,
   700  			ReturnData: fmt.Sprintf("%#x", log.ReturnData),
   701  			Depth:      int32(log.Depth),
   702  			Refund:     log.RefundCounter,
   703  			OpName:     log.OpName(),
   704  			Error:      log.ErrorString(),
   705  		})
   706  	}
   707  	return &iotexapi.TraceTransactionStructLogsResponse{
   708  		StructLogs: structLogs,
   709  	}, nil
   710  }
   711  
   712  // generateBlockMeta generates BlockMeta from block
   713  func generateBlockMeta(blkStore *apitypes.BlockWithReceipts) *iotextypes.BlockMeta {
   714  	blk := blkStore.Block
   715  	header := blk.Header
   716  	height := header.Height()
   717  	ts := timestamppb.New(header.Timestamp())
   718  	var (
   719  		producerAddress string
   720  		h               hash.Hash256
   721  	)
   722  	if blk.Height() > 0 {
   723  		producerAddress = header.ProducerAddress()
   724  		h = header.HashBlock()
   725  	} else {
   726  		h = block.GenesisHash()
   727  	}
   728  	txRoot := header.TxRoot()
   729  	receiptRoot := header.ReceiptRoot()
   730  	deltaStateDigest := header.DeltaStateDigest()
   731  	prevHash := header.PrevHash()
   732  
   733  	blockMeta := iotextypes.BlockMeta{
   734  		Hash:              hex.EncodeToString(h[:]),
   735  		Height:            height,
   736  		Timestamp:         ts,
   737  		ProducerAddress:   producerAddress,
   738  		TxRoot:            hex.EncodeToString(txRoot[:]),
   739  		ReceiptRoot:       hex.EncodeToString(receiptRoot[:]),
   740  		DeltaStateDigest:  hex.EncodeToString(deltaStateDigest[:]),
   741  		PreviousBlockHash: hex.EncodeToString(prevHash[:]),
   742  	}
   743  	if logsBloom := header.LogsBloomfilter(); logsBloom != nil {
   744  		blockMeta.LogsBloom = hex.EncodeToString(logsBloom.Bytes())
   745  	}
   746  	blockMeta.NumActions = int64(len(blk.Actions))
   747  	blockMeta.TransferAmount = blk.CalculateTransferAmount().String()
   748  	blockMeta.GasLimit, blockMeta.GasUsed = gasLimitAndUsed(blk.Actions, blkStore.Receipts)
   749  	return &blockMeta
   750  }
   751  
   752  func gasLimitAndUsed(acts []*action.SealedEnvelope, receipts []*action.Receipt) (uint64, uint64) {
   753  	var gasLimit, gasUsed uint64
   754  	for _, tx := range acts {
   755  		gasLimit += tx.GasLimit()
   756  	}
   757  	for _, r := range receipts {
   758  		gasUsed += r.GasConsumed
   759  	}
   760  	return gasLimit, gasUsed
   761  }