github.com/koko1123/flow-go-1@v0.29.6/engine/execution/rpc/engine.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "net" 9 "strings" 10 "unicode/utf8" 11 12 grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" 13 "github.com/onflow/flow/protobuf/go/flow/execution" 14 "github.com/rs/zerolog" 15 "google.golang.org/grpc" 16 "google.golang.org/grpc/codes" 17 "google.golang.org/grpc/status" 18 19 "github.com/koko1123/flow-go-1/consensus/hotstuff" 20 "github.com/koko1123/flow-go-1/engine" 21 "github.com/koko1123/flow-go-1/engine/common/rpc" 22 "github.com/koko1123/flow-go-1/engine/common/rpc/convert" 23 "github.com/koko1123/flow-go-1/engine/execution/ingestion" 24 "github.com/koko1123/flow-go-1/model/flow" 25 "github.com/koko1123/flow-go-1/state/protocol" 26 "github.com/koko1123/flow-go-1/storage" 27 "github.com/koko1123/flow-go-1/utils/grpcutils" 28 ) 29 30 // Config defines the configurable options for the gRPC server. 31 type Config struct { 32 ListenAddr string 33 MaxMsgSize int // In bytes 34 RpcMetricsEnabled bool // enable GRPC metrics reporting 35 } 36 37 // Engine implements a gRPC server with a simplified version of the Observation API. 38 type Engine struct { 39 unit *engine.Unit 40 log zerolog.Logger 41 handler *handler // the gRPC service implementation 42 server *grpc.Server // the gRPC server 43 config Config 44 } 45 46 // New returns a new RPC engine. 47 func New( 48 log zerolog.Logger, 49 config Config, 50 e *ingestion.Engine, 51 headers storage.Headers, 52 state protocol.State, 53 events storage.Events, 54 exeResults storage.ExecutionResults, 55 txResults storage.TransactionResults, 56 commits storage.Commits, 57 chainID flow.ChainID, 58 signerIndicesDecoder hotstuff.BlockSignerDecoder, 59 apiRatelimits map[string]int, // the api rate limit (max calls per second) for each of the gRPC API e.g. Ping->100, ExecuteScriptAtBlockID->300 60 apiBurstLimits map[string]int, // the api burst limit (max calls at the same time) for each of the gRPC API e.g. Ping->50, ExecuteScriptAtBlockID->10 61 ) *Engine { 62 log = log.With().Str("engine", "rpc").Logger() 63 if config.MaxMsgSize == 0 { 64 config.MaxMsgSize = grpcutils.DefaultMaxMsgSize 65 } 66 67 serverOptions := []grpc.ServerOption{ 68 grpc.MaxRecvMsgSize(config.MaxMsgSize), 69 grpc.MaxSendMsgSize(config.MaxMsgSize), 70 } 71 72 var interceptors []grpc.UnaryServerInterceptor // ordered list of interceptors 73 // if rpc metrics is enabled, add the grpc metrics interceptor as a server option 74 if config.RpcMetricsEnabled { 75 interceptors = append(interceptors, grpc_prometheus.UnaryServerInterceptor) 76 } 77 78 if len(apiRatelimits) > 0 { 79 // create a rate limit interceptor 80 rateLimitInterceptor := rpc.NewRateLimiterInterceptor(log, apiRatelimits, apiBurstLimits).UnaryServerInterceptor 81 // append the rate limit interceptor to the list of interceptors 82 interceptors = append(interceptors, rateLimitInterceptor) 83 } 84 85 // create a chained unary interceptor 86 chainedInterceptors := grpc.ChainUnaryInterceptor(interceptors...) 87 serverOptions = append(serverOptions, chainedInterceptors) 88 89 server := grpc.NewServer(serverOptions...) 90 91 eng := &Engine{ 92 log: log, 93 unit: engine.NewUnit(), 94 handler: &handler{ 95 engine: e, 96 chain: chainID, 97 headers: headers, 98 state: state, 99 signerIndicesDecoder: signerIndicesDecoder, 100 events: events, 101 exeResults: exeResults, 102 transactionResults: txResults, 103 commits: commits, 104 log: log, 105 }, 106 server: server, 107 config: config, 108 } 109 110 if config.RpcMetricsEnabled { 111 grpc_prometheus.EnableHandlingTimeHistogram() 112 grpc_prometheus.Register(server) 113 } 114 115 execution.RegisterExecutionAPIServer(eng.server, eng.handler) 116 117 return eng 118 } 119 120 // Ready returns a ready channel that is closed once the engine has fully 121 // started. The RPC engine is ready when the gRPC server has successfully 122 // started. 123 func (e *Engine) Ready() <-chan struct{} { 124 e.unit.Launch(e.serve) 125 return e.unit.Ready() 126 } 127 128 // Done returns a done channel that is closed once the engine has fully stopped. 129 // It sends a signal to stop the gRPC server, then closes the channel. 130 func (e *Engine) Done() <-chan struct{} { 131 return e.unit.Done(e.server.GracefulStop) 132 } 133 134 // serve starts the gRPC server . 135 // 136 // When this function returns, the server is considered ready. 137 func (e *Engine) serve() { 138 e.log.Info().Msgf("starting server on address %s", e.config.ListenAddr) 139 140 l, err := net.Listen("tcp", e.config.ListenAddr) 141 if err != nil { 142 e.log.Err(err).Msg("failed to start server") 143 return 144 } 145 146 err = e.server.Serve(l) 147 if err != nil { 148 e.log.Err(err).Msg("fatal error in server") 149 } 150 } 151 152 // handler implements a subset of the Observation API. 153 type handler struct { 154 engine ingestion.IngestRPC 155 chain flow.ChainID 156 headers storage.Headers 157 state protocol.State 158 signerIndicesDecoder hotstuff.BlockSignerDecoder 159 events storage.Events 160 exeResults storage.ExecutionResults 161 transactionResults storage.TransactionResults 162 log zerolog.Logger 163 commits storage.Commits 164 } 165 166 var _ execution.ExecutionAPIServer = &handler{} 167 168 // Ping responds to requests when the server is up. 169 func (h *handler) Ping(_ context.Context, _ *execution.PingRequest) (*execution.PingResponse, error) { 170 return &execution.PingResponse{}, nil 171 } 172 173 func (h *handler) ExecuteScriptAtBlockID( 174 ctx context.Context, 175 req *execution.ExecuteScriptAtBlockIDRequest, 176 ) (*execution.ExecuteScriptAtBlockIDResponse, error) { 177 178 blockID, err := convert.BlockID(req.GetBlockId()) 179 if err != nil { 180 return nil, err 181 } 182 183 value, err := h.engine.ExecuteScriptAtBlockID(ctx, req.GetScript(), req.GetArguments(), blockID) 184 if err != nil { 185 // return code 3 as this passes the litmus test in our context 186 return nil, status.Errorf(codes.InvalidArgument, "failed to execute script: %v", err) 187 } 188 189 res := &execution.ExecuteScriptAtBlockIDResponse{ 190 Value: value, 191 } 192 193 return res, nil 194 } 195 196 func (h *handler) GetRegisterAtBlockID( 197 ctx context.Context, 198 req *execution.GetRegisterAtBlockIDRequest, 199 ) (*execution.GetRegisterAtBlockIDResponse, error) { 200 201 blockID, err := convert.BlockID(req.GetBlockId()) 202 if err != nil { 203 return nil, err 204 } 205 206 owner := req.GetRegisterOwner() 207 key := req.GetRegisterKey() 208 value, err := h.engine.GetRegisterAtBlockID(ctx, owner, key, blockID) 209 210 if err != nil { 211 return nil, status.Errorf(codes.Internal, "failed to collect register (owner : %s, key: %s): %v", hex.EncodeToString(owner), string(key), err) 212 } 213 214 res := &execution.GetRegisterAtBlockIDResponse{ 215 Value: value, 216 } 217 218 return res, nil 219 } 220 221 func (h *handler) GetEventsForBlockIDs(_ context.Context, 222 req *execution.GetEventsForBlockIDsRequest) (*execution.GetEventsForBlockIDsResponse, error) { 223 224 // validate request 225 blockIDs := req.GetBlockIds() 226 flowBlockIDs, err := convert.BlockIDs(blockIDs) 227 if err != nil { 228 return nil, err 229 } 230 reqEvent := req.GetType() 231 eType, err := convert.EventType(reqEvent) 232 if err != nil { 233 return nil, err 234 } 235 236 results := make([]*execution.GetEventsForBlockIDsResponse_Result, len(blockIDs)) 237 238 // collect all the events and create a EventsResponse_Result for each block 239 for i, bID := range flowBlockIDs { 240 // Check if block has been executed 241 if _, err := h.commits.ByBlockID(bID); err != nil { 242 if errors.Is(err, storage.ErrNotFound) { 243 return nil, status.Errorf(codes.NotFound, "state commitment for block ID %s does not exist", bID) 244 } 245 return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", bID) 246 } 247 248 // lookup events 249 blockEvents, err := h.events.ByBlockIDEventType(bID, flow.EventType(eType)) 250 if err != nil { 251 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 252 } 253 254 result, err := h.eventResult(bID, blockEvents) 255 if err != nil { 256 return nil, err 257 } 258 results[i] = result 259 260 } 261 262 return &execution.GetEventsForBlockIDsResponse{ 263 Results: results, 264 }, nil 265 } 266 267 func (h *handler) GetTransactionResult( 268 _ context.Context, 269 req *execution.GetTransactionResultRequest, 270 ) (*execution.GetTransactionResultResponse, error) { 271 272 reqBlockID := req.GetBlockId() 273 blockID, err := convert.BlockID(reqBlockID) 274 if err != nil { 275 return nil, err 276 } 277 278 reqTxID := req.GetTransactionId() 279 txID, err := convert.TransactionID(reqTxID) 280 if err != nil { 281 return nil, err 282 } 283 284 var statusCode uint32 = 0 285 errMsg := "" 286 287 // lookup any transaction error that might have occurred 288 txResult, err := h.transactionResults.ByBlockIDTransactionID(blockID, txID) 289 if err != nil { 290 if errors.Is(err, storage.ErrNotFound) { 291 return nil, status.Error(codes.NotFound, "transaction result not found") 292 } 293 294 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 295 } 296 297 if txResult.ErrorMessage != "" { 298 cadenceErrMessage := txResult.ErrorMessage 299 if !utf8.ValidString(cadenceErrMessage) { 300 h.log.Warn(). 301 Str("block_id", blockID.String()). 302 Str("transaction_id", txID.String()). 303 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 304 Msg("invalid character in Cadence error message") 305 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 306 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 307 } 308 309 statusCode = 1 // for now a statusCode of 1 indicates an error and 0 indicates no error 310 errMsg = cadenceErrMessage 311 } 312 313 // lookup events by block id and transaction ID 314 blockEvents, err := h.events.ByBlockIDTransactionID(blockID, txID) 315 if err != nil { 316 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 317 } 318 319 events := convert.EventsToMessages(blockEvents) 320 321 // compose a response with the events and the transaction error 322 return &execution.GetTransactionResultResponse{ 323 StatusCode: statusCode, 324 ErrorMessage: errMsg, 325 Events: events, 326 }, nil 327 } 328 329 func (h *handler) GetTransactionResultByIndex( 330 _ context.Context, 331 req *execution.GetTransactionByIndexRequest, 332 ) (*execution.GetTransactionResultResponse, error) { 333 334 reqBlockID := req.GetBlockId() 335 blockID, err := convert.BlockID(reqBlockID) 336 if err != nil { 337 return nil, err 338 } 339 340 index := req.GetIndex() 341 342 var statusCode uint32 = 0 343 errMsg := "" 344 345 // lookup any transaction error that might have occurred 346 txResult, err := h.transactionResults.ByBlockIDTransactionIndex(blockID, index) 347 if err != nil { 348 if errors.Is(err, storage.ErrNotFound) { 349 return nil, status.Error(codes.NotFound, "transaction result not found") 350 } 351 352 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 353 } 354 355 if txResult.ErrorMessage != "" { 356 cadenceErrMessage := txResult.ErrorMessage 357 if !utf8.ValidString(cadenceErrMessage) { 358 h.log.Warn(). 359 Str("block_id", blockID.String()). 360 Uint32("index", index). 361 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 362 Msg("invalid character in Cadence error message") 363 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 364 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 365 } 366 367 statusCode = 1 // for now a statusCode of 1 indicates an error and 0 indicates no error 368 errMsg = cadenceErrMessage 369 } 370 371 // lookup events by block id and transaction index 372 txEvents, err := h.events.ByBlockIDTransactionIndex(blockID, index) 373 if err != nil { 374 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 375 } 376 377 events := convert.EventsToMessages(txEvents) 378 379 // compose a response with the events and the transaction error 380 return &execution.GetTransactionResultResponse{ 381 StatusCode: statusCode, 382 ErrorMessage: errMsg, 383 Events: events, 384 }, nil 385 } 386 387 func (h *handler) GetTransactionResultsByBlockID( 388 _ context.Context, 389 req *execution.GetTransactionsByBlockIDRequest, 390 ) (*execution.GetTransactionResultsResponse, error) { 391 392 reqBlockID := req.GetBlockId() 393 blockID, err := convert.BlockID(reqBlockID) 394 if err != nil { 395 return nil, err 396 } 397 398 // Get all tx results 399 txResults, err := h.transactionResults.ByBlockID(blockID) 400 if err != nil { 401 if errors.Is(err, storage.ErrNotFound) { 402 return nil, status.Error(codes.NotFound, "transaction results not found") 403 } 404 405 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 406 } 407 408 // get all events for a block 409 blockEvents, err := h.events.ByBlockID(blockID) 410 if err != nil { 411 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 412 } 413 414 responseTxResults := make([]*execution.GetTransactionResultResponse, len(txResults)) 415 416 eventsByTxIndex := make(map[uint32][]flow.Event, len(txResults)) //we will have at most as many buckets as tx results 417 418 // re-partition events by tx index 419 // it's not documented but events are stored indexed by (blockID, event.TransactionID, event.TransactionIndex, event.EventIndex) 420 // hence they should keep order within a transaction, so we don't sort resulting events slices 421 for _, event := range blockEvents { 422 eventsByTxIndex[event.TransactionIndex] = append(eventsByTxIndex[event.TransactionIndex], event) 423 } 424 425 // match tx results with events 426 for index, txResult := range txResults { 427 var statusCode uint32 = 0 428 errMsg := "" 429 430 txIndex := uint32(index) 431 432 if txResult.ErrorMessage != "" { 433 cadenceErrMessage := txResult.ErrorMessage 434 if !utf8.ValidString(cadenceErrMessage) { 435 h.log.Warn(). 436 Str("block_id", blockID.String()). 437 Uint32("index", txIndex). 438 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 439 Msg("invalid character in Cadence error message") 440 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 441 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 442 } 443 444 statusCode = 1 // for now a statusCode of 1 indicates an error and 0 indicates no error 445 errMsg = cadenceErrMessage 446 } 447 448 events := convert.EventsToMessages(eventsByTxIndex[txIndex]) 449 450 responseTxResults[index] = &execution.GetTransactionResultResponse{ 451 StatusCode: statusCode, 452 ErrorMessage: errMsg, 453 Events: events, 454 } 455 } 456 457 // compose a response 458 return &execution.GetTransactionResultsResponse{ 459 TransactionResults: responseTxResults, 460 }, nil 461 } 462 463 // eventResult creates EventsResponse_Result from flow.Event for the given blockID 464 func (h *handler) eventResult(blockID flow.Identifier, 465 flowEvents []flow.Event) (*execution.GetEventsForBlockIDsResponse_Result, error) { 466 467 // convert events to event message 468 events := convert.EventsToMessages(flowEvents) 469 470 // lookup block 471 header, err := h.headers.ByBlockID(blockID) 472 if err != nil { 473 return nil, status.Errorf(codes.Internal, "failed to lookup block: %v", err) 474 } 475 476 return &execution.GetEventsForBlockIDsResponse_Result{ 477 BlockId: blockID[:], 478 BlockHeight: header.Height, 479 Events: events, 480 }, nil 481 } 482 483 func (h *handler) GetAccountAtBlockID( 484 ctx context.Context, 485 req *execution.GetAccountAtBlockIDRequest, 486 ) (*execution.GetAccountAtBlockIDResponse, error) { 487 488 blockID := req.GetBlockId() 489 blockFlowID, err := convert.BlockID(blockID) 490 if err != nil { 491 return nil, err 492 } 493 494 flowAddress, err := convert.Address(req.GetAddress(), h.chain.Chain()) 495 if err != nil { 496 return nil, err 497 } 498 499 value, err := h.engine.GetAccount(ctx, flowAddress, blockFlowID) 500 if err != nil { 501 return nil, status.Errorf(codes.Internal, "failed to get account: %v", err) 502 } 503 504 if value == nil { 505 return nil, status.Errorf(codes.NotFound, "account with address %s does not exist", flowAddress) 506 } 507 508 account, err := convert.AccountToMessage(value) 509 if err != nil { 510 return nil, status.Errorf(codes.Internal, "failed to convert account to message: %v", err) 511 } 512 513 res := &execution.GetAccountAtBlockIDResponse{ 514 Account: account, 515 } 516 517 return res, nil 518 519 } 520 521 // GetLatestBlockHeader gets the latest sealed or finalized block header. 522 func (h *handler) GetLatestBlockHeader( 523 _ context.Context, 524 req *execution.GetLatestBlockHeaderRequest, 525 ) (*execution.BlockHeaderResponse, error) { 526 var header *flow.Header 527 var err error 528 529 if req.GetIsSealed() { 530 // get the latest seal header from storage 531 header, err = h.state.Sealed().Head() 532 } else { 533 // get the finalized header from state 534 header, err = h.state.Final().Head() 535 } 536 if err != nil { 537 return nil, status.Errorf(codes.NotFound, "not found: %v", err) 538 } 539 540 return h.blockHeaderResponse(header) 541 } 542 543 // GetBlockHeaderByID gets a block header by ID. 544 func (h *handler) GetBlockHeaderByID( 545 _ context.Context, 546 req *execution.GetBlockHeaderByIDRequest, 547 ) (*execution.BlockHeaderResponse, error) { 548 id, err := convert.BlockID(req.GetId()) 549 if err != nil { 550 return nil, err 551 } 552 header, err := h.headers.ByBlockID(id) 553 if err != nil { 554 return nil, status.Errorf(codes.NotFound, "not found: %v", err) 555 } 556 return h.blockHeaderResponse(header) 557 } 558 559 func (h *handler) blockHeaderResponse(header *flow.Header) (*execution.BlockHeaderResponse, error) { 560 signerIDs, err := h.signerIndicesDecoder.DecodeSignerIDs(header) 561 if err != nil { 562 return nil, fmt.Errorf("failed to decode signer indices to Identifiers for block %v: %w", header.ID(), err) 563 } 564 565 msg, err := convert.BlockHeaderToMessage(header, signerIDs) 566 if err != nil { 567 return nil, err 568 } 569 570 return &execution.BlockHeaderResponse{ 571 Block: msg, 572 }, nil 573 }