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 }