github.com/onflow/flow-go@v0.33.17/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 "github.com/onflow/flow/protobuf/go/flow/entities" 13 14 grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" 15 "github.com/onflow/flow/protobuf/go/flow/execution" 16 "github.com/rs/zerolog" 17 "google.golang.org/grpc" 18 "google.golang.org/grpc/codes" 19 _ "google.golang.org/grpc/encoding/gzip" // required for gRPC compression 20 "google.golang.org/grpc/status" 21 22 "github.com/onflow/flow-go/consensus/hotstuff" 23 "github.com/onflow/flow-go/engine" 24 _ "github.com/onflow/flow-go/engine/common/grpc/compressor/deflate" // required for gRPC compression 25 _ "github.com/onflow/flow-go/engine/common/grpc/compressor/snappy" // required for gRPC compression 26 "github.com/onflow/flow-go/engine/common/rpc" 27 "github.com/onflow/flow-go/engine/common/rpc/convert" 28 exeEng "github.com/onflow/flow-go/engine/execution" 29 "github.com/onflow/flow-go/engine/execution/state" 30 fvmerrors "github.com/onflow/flow-go/fvm/errors" 31 "github.com/onflow/flow-go/model/flow" 32 "github.com/onflow/flow-go/state/protocol" 33 "github.com/onflow/flow-go/storage" 34 ) 35 36 const DefaultMaxBlockRange = 300 37 38 // Config defines the configurable options for the gRPC server. 39 type Config struct { 40 ListenAddr string 41 MaxMsgSize uint // In bytes 42 RpcMetricsEnabled bool // enable GRPC metrics reporting 43 } 44 45 // Engine implements a gRPC server with a simplified version of the Observation API. 46 type Engine struct { 47 unit *engine.Unit 48 log zerolog.Logger 49 handler *handler // the gRPC service implementation 50 server *grpc.Server // the gRPC server 51 config Config 52 } 53 54 // New returns a new RPC engine. 55 func New( 56 log zerolog.Logger, 57 config Config, 58 scriptsExecutor exeEng.ScriptExecutor, 59 headers storage.Headers, 60 state protocol.State, 61 events storage.Events, 62 exeResults storage.ExecutionResults, 63 txResults storage.TransactionResults, 64 commits storage.Commits, 65 chainID flow.ChainID, 66 signerIndicesDecoder hotstuff.BlockSignerDecoder, 67 apiRatelimits map[string]int, // the api rate limit (max calls per second) for each of the gRPC API e.g. Ping->100, ExecuteScriptAtBlockID->300 68 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 69 ) *Engine { 70 log = log.With().Str("engine", "rpc").Logger() 71 serverOptions := []grpc.ServerOption{ 72 grpc.MaxRecvMsgSize(int(config.MaxMsgSize)), 73 grpc.MaxSendMsgSize(int(config.MaxMsgSize)), 74 } 75 76 var interceptors []grpc.UnaryServerInterceptor // ordered list of interceptors 77 // if rpc metrics is enabled, add the grpc metrics interceptor as a server option 78 if config.RpcMetricsEnabled { 79 interceptors = append(interceptors, grpc_prometheus.UnaryServerInterceptor) 80 } 81 82 if len(apiRatelimits) > 0 { 83 // create a rate limit interceptor 84 rateLimitInterceptor := rpc.NewRateLimiterInterceptor(log, apiRatelimits, apiBurstLimits).UnaryServerInterceptor 85 // append the rate limit interceptor to the list of interceptors 86 interceptors = append(interceptors, rateLimitInterceptor) 87 } 88 89 // create a chained unary interceptor 90 chainedInterceptors := grpc.ChainUnaryInterceptor(interceptors...) 91 serverOptions = append(serverOptions, chainedInterceptors) 92 93 server := grpc.NewServer(serverOptions...) 94 95 eng := &Engine{ 96 log: log, 97 unit: engine.NewUnit(), 98 handler: &handler{ 99 engine: scriptsExecutor, 100 chain: chainID, 101 headers: headers, 102 state: state, 103 signerIndicesDecoder: signerIndicesDecoder, 104 events: events, 105 exeResults: exeResults, 106 transactionResults: txResults, 107 commits: commits, 108 log: log, 109 maxBlockRange: DefaultMaxBlockRange, 110 }, 111 server: server, 112 config: config, 113 } 114 115 if config.RpcMetricsEnabled { 116 grpc_prometheus.EnableHandlingTimeHistogram() 117 grpc_prometheus.Register(server) 118 } 119 120 execution.RegisterExecutionAPIServer(eng.server, eng.handler) 121 122 return eng 123 } 124 125 // Ready returns a ready channel that is closed once the engine has fully 126 // started. The RPC engine is ready when the gRPC server has successfully 127 // started. 128 func (e *Engine) Ready() <-chan struct{} { 129 e.unit.Launch(e.serve) 130 return e.unit.Ready() 131 } 132 133 // Done returns a done channel that is closed once the engine has fully stopped. 134 // It sends a signal to stop the gRPC server, then closes the channel. 135 func (e *Engine) Done() <-chan struct{} { 136 return e.unit.Done(e.server.GracefulStop) 137 } 138 139 // serve starts the gRPC server . 140 // 141 // When this function returns, the server is considered ready. 142 func (e *Engine) serve() { 143 e.log.Info().Msgf("starting server on address %s", e.config.ListenAddr) 144 145 l, err := net.Listen("tcp", e.config.ListenAddr) 146 if err != nil { 147 e.log.Err(err).Msg("failed to start server") 148 return 149 } 150 151 err = e.server.Serve(l) 152 if err != nil { 153 e.log.Err(err).Msg("fatal error in server") 154 } 155 } 156 157 // handler implements a subset of the Observation API. 158 type handler struct { 159 engine exeEng.ScriptExecutor 160 chain flow.ChainID 161 headers storage.Headers 162 state protocol.State 163 signerIndicesDecoder hotstuff.BlockSignerDecoder 164 events storage.Events 165 exeResults storage.ExecutionResults 166 transactionResults storage.TransactionResults 167 log zerolog.Logger 168 commits storage.Commits 169 maxBlockRange int 170 } 171 172 var _ execution.ExecutionAPIServer = (*handler)(nil) 173 174 // Ping responds to requests when the server is up. 175 func (h *handler) Ping( 176 _ context.Context, 177 _ *execution.PingRequest, 178 ) (*execution.PingResponse, error) { 179 return &execution.PingResponse{}, nil 180 } 181 182 func (h *handler) ExecuteScriptAtBlockID( 183 ctx context.Context, 184 req *execution.ExecuteScriptAtBlockIDRequest, 185 ) (*execution.ExecuteScriptAtBlockIDResponse, error) { 186 187 blockID, err := convert.BlockID(req.GetBlockId()) 188 if err != nil { 189 return nil, err 190 } 191 192 // return a more user friendly error if block has not been executed 193 if _, err = h.commits.ByBlockID(blockID); err != nil { 194 if errors.Is(err, storage.ErrNotFound) { 195 return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockID) 196 } 197 return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) 198 } 199 200 value, err := h.engine.ExecuteScriptAtBlockID(ctx, req.GetScript(), req.GetArguments(), blockID) 201 if err != nil { 202 // todo check the error code instead 203 // return code 3 as this passes the litmus test in our context 204 return nil, status.Errorf(codes.InvalidArgument, "failed to execute script: %v", err) 205 } 206 207 res := &execution.ExecuteScriptAtBlockIDResponse{ 208 Value: value, 209 } 210 211 return res, nil 212 } 213 214 func (h *handler) GetRegisterAtBlockID( 215 ctx context.Context, 216 req *execution.GetRegisterAtBlockIDRequest, 217 ) (*execution.GetRegisterAtBlockIDResponse, error) { 218 219 blockID, err := convert.BlockID(req.GetBlockId()) 220 if err != nil { 221 return nil, err 222 } 223 224 owner := req.GetRegisterOwner() 225 key := req.GetRegisterKey() 226 value, err := h.engine.GetRegisterAtBlockID(ctx, owner, key, blockID) 227 228 if err != nil { 229 return nil, status.Errorf(codes.Internal, "failed to collect register (owner : %s, key: %s): %v", hex.EncodeToString(owner), string(key), err) 230 } 231 232 res := &execution.GetRegisterAtBlockIDResponse{ 233 Value: value, 234 } 235 236 return res, nil 237 } 238 239 func (h *handler) GetEventsForBlockIDs( 240 _ context.Context, 241 req *execution.GetEventsForBlockIDsRequest, 242 ) (*execution.GetEventsForBlockIDsResponse, error) { 243 244 // validate request 245 blockIDs := req.GetBlockIds() 246 flowBlockIDs, err := convert.BlockIDs(blockIDs) 247 if err != nil { 248 return nil, err 249 } 250 reqEvent := req.GetType() 251 eType, err := convert.EventType(reqEvent) 252 if err != nil { 253 return nil, err 254 } 255 256 if len(blockIDs) > h.maxBlockRange { 257 return nil, status.Errorf(codes.InvalidArgument, "too many block IDs requested: %d > %d", len(blockIDs), h.maxBlockRange) 258 } 259 260 results := make([]*execution.GetEventsForBlockIDsResponse_Result, len(blockIDs)) 261 262 // collect all the events and create a EventsResponse_Result for each block 263 for i, bID := range flowBlockIDs { 264 // Check if block has been executed 265 if _, err := h.commits.ByBlockID(bID); err != nil { 266 if errors.Is(err, storage.ErrNotFound) { 267 return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", bID) 268 } 269 return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", bID) 270 } 271 272 // lookup events 273 blockEvents, err := h.events.ByBlockIDEventType(bID, flow.EventType(eType)) 274 if err != nil { 275 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 276 } 277 278 result, err := h.eventResult(bID, blockEvents) 279 if err != nil { 280 return nil, err 281 } 282 results[i] = result 283 284 } 285 286 return &execution.GetEventsForBlockIDsResponse{ 287 Results: results, 288 EventEncodingVersion: entities.EventEncodingVersion_CCF_V0, 289 }, nil 290 } 291 292 func (h *handler) GetTransactionResult( 293 _ context.Context, 294 req *execution.GetTransactionResultRequest, 295 ) (*execution.GetTransactionResultResponse, error) { 296 297 reqBlockID := req.GetBlockId() 298 blockID, err := convert.BlockID(reqBlockID) 299 if err != nil { 300 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 301 } 302 303 reqTxID := req.GetTransactionId() 304 txID, err := convert.TransactionID(reqTxID) 305 if err != nil { 306 return nil, status.Errorf(codes.InvalidArgument, "invalid transactionID: %v", err) 307 } 308 309 var statusCode uint32 = 0 310 errMsg := "" 311 312 // lookup any transaction error that might have occurred 313 txResult, err := h.transactionResults.ByBlockIDTransactionID(blockID, txID) 314 if err != nil { 315 if errors.Is(err, storage.ErrNotFound) { 316 return nil, status.Error(codes.NotFound, "transaction result not found") 317 } 318 319 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 320 } 321 322 if txResult.ErrorMessage != "" { 323 cadenceErrMessage := txResult.ErrorMessage 324 if !utf8.ValidString(cadenceErrMessage) { 325 h.log.Warn(). 326 Str("block_id", blockID.String()). 327 Str("transaction_id", txID.String()). 328 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 329 Msg("invalid character in Cadence error message") 330 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 331 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 332 } 333 334 statusCode = 1 // for now a statusCode of 1 indicates an error and 0 indicates no error 335 errMsg = cadenceErrMessage 336 } 337 338 // lookup events by block id and transaction ID 339 blockEvents, err := h.events.ByBlockIDTransactionID(blockID, txID) 340 if err != nil { 341 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 342 } 343 344 events := convert.EventsToMessages(blockEvents) 345 346 // compose a response with the events and the transaction error 347 return &execution.GetTransactionResultResponse{ 348 StatusCode: statusCode, 349 ErrorMessage: errMsg, 350 Events: events, 351 EventEncodingVersion: entities.EventEncodingVersion_CCF_V0, 352 }, nil 353 } 354 355 func (h *handler) GetTransactionResultByIndex( 356 _ context.Context, 357 req *execution.GetTransactionByIndexRequest, 358 ) (*execution.GetTransactionResultResponse, error) { 359 360 reqBlockID := req.GetBlockId() 361 blockID, err := convert.BlockID(reqBlockID) 362 if err != nil { 363 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 364 } 365 366 index := req.GetIndex() 367 368 var statusCode uint32 = 0 369 errMsg := "" 370 371 // lookup any transaction error that might have occurred 372 txResult, err := h.transactionResults.ByBlockIDTransactionIndex(blockID, index) 373 if err != nil { 374 if errors.Is(err, storage.ErrNotFound) { 375 return nil, status.Error(codes.NotFound, "transaction result not found") 376 } 377 378 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 379 } 380 381 if txResult.ErrorMessage != "" { 382 cadenceErrMessage := txResult.ErrorMessage 383 if !utf8.ValidString(cadenceErrMessage) { 384 h.log.Warn(). 385 Str("block_id", blockID.String()). 386 Uint32("index", index). 387 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 388 Msg("invalid character in Cadence error message") 389 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 390 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 391 } 392 393 statusCode = 1 // for now a statusCode of 1 indicates an error and 0 indicates no error 394 errMsg = cadenceErrMessage 395 } 396 397 // lookup events by block id and transaction index 398 txEvents, err := h.events.ByBlockIDTransactionIndex(blockID, index) 399 if err != nil { 400 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 401 } 402 403 events := convert.EventsToMessages(txEvents) 404 405 // compose a response with the events and the transaction error 406 return &execution.GetTransactionResultResponse{ 407 StatusCode: statusCode, 408 ErrorMessage: errMsg, 409 Events: events, 410 EventEncodingVersion: entities.EventEncodingVersion_CCF_V0, 411 }, nil 412 } 413 414 func (h *handler) GetTransactionResultsByBlockID( 415 _ context.Context, 416 req *execution.GetTransactionsByBlockIDRequest, 417 ) (*execution.GetTransactionResultsResponse, error) { 418 419 reqBlockID := req.GetBlockId() 420 blockID, err := convert.BlockID(reqBlockID) 421 if err != nil { 422 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 423 } 424 425 // must verify block was locally executed first since transactionResults.ByBlockID will return 426 // an empty slice if block does not exist 427 if _, err = h.commits.ByBlockID(blockID); err != nil { 428 if errors.Is(err, storage.ErrNotFound) { 429 return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockID) 430 } 431 return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) 432 } 433 434 // Get all tx results 435 txResults, err := h.transactionResults.ByBlockID(blockID) 436 if err != nil { 437 if errors.Is(err, storage.ErrNotFound) { 438 return nil, status.Error(codes.NotFound, "transaction results not found") 439 } 440 441 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 442 } 443 444 // get all events for a block 445 blockEvents, err := h.events.ByBlockID(blockID) 446 if err != nil { 447 return nil, status.Errorf(codes.Internal, "failed to get events for block: %v", err) 448 } 449 450 responseTxResults := make([]*execution.GetTransactionResultResponse, len(txResults)) 451 452 eventsByTxIndex := make(map[uint32][]flow.Event, len(txResults)) //we will have at most as many buckets as tx results 453 454 // re-partition events by tx index 455 // it's not documented but events are stored indexed by (blockID, event.TransactionID, event.TransactionIndex, event.EventIndex) 456 // hence they should keep order within a transaction, so we don't sort resulting events slices 457 for _, event := range blockEvents { 458 eventsByTxIndex[event.TransactionIndex] = append(eventsByTxIndex[event.TransactionIndex], event) 459 } 460 461 // match tx results with events 462 for index, txResult := range txResults { 463 var statusCode uint32 = 0 464 errMsg := "" 465 466 txIndex := uint32(index) 467 468 if txResult.ErrorMessage != "" { 469 cadenceErrMessage := txResult.ErrorMessage 470 if !utf8.ValidString(cadenceErrMessage) { 471 h.log.Warn(). 472 Str("block_id", blockID.String()). 473 Uint32("index", txIndex). 474 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 475 Msg("invalid character in Cadence error message") 476 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 477 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 478 } 479 480 statusCode = 1 // for now a statusCode of 1 indicates an error and 0 indicates no error 481 errMsg = cadenceErrMessage 482 } 483 484 events := convert.EventsToMessages(eventsByTxIndex[txIndex]) 485 486 responseTxResults[index] = &execution.GetTransactionResultResponse{ 487 StatusCode: statusCode, 488 ErrorMessage: errMsg, 489 Events: events, 490 } 491 } 492 493 // compose a response 494 return &execution.GetTransactionResultsResponse{ 495 TransactionResults: responseTxResults, 496 EventEncodingVersion: entities.EventEncodingVersion_CCF_V0, 497 }, nil 498 } 499 500 // GetTransactionErrorMessage implements a grpc handler for getting a transaction error message by block ID and tx ID. 501 // Expected error codes during normal operations: 502 // - codes.InvalidArgument - invalid blockID, tx ID. 503 // - codes.NotFound - transaction result by tx ID not found. 504 func (h *handler) GetTransactionErrorMessage( 505 _ context.Context, 506 req *execution.GetTransactionErrorMessageRequest, 507 ) (*execution.GetTransactionErrorMessageResponse, error) { 508 reqBlockID := req.GetBlockId() 509 blockID, err := convert.BlockID(reqBlockID) 510 if err != nil { 511 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 512 } 513 514 reqTxID := req.GetTransactionId() 515 txID, err := convert.TransactionID(reqTxID) 516 if err != nil { 517 return nil, status.Errorf(codes.InvalidArgument, "invalid transactionID: %v", err) 518 } 519 520 // lookup any transaction error that might have occurred 521 txResult, err := h.transactionResults.ByBlockIDTransactionID(blockID, txID) 522 if err != nil { 523 if errors.Is(err, storage.ErrNotFound) { 524 return nil, status.Error(codes.NotFound, "transaction result not found") 525 } 526 527 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 528 } 529 530 result := &execution.GetTransactionErrorMessageResponse{ 531 TransactionId: convert.IdentifierToMessage(txResult.TransactionID), 532 } 533 534 if len(txResult.ErrorMessage) > 0 { 535 cadenceErrMessage := txResult.ErrorMessage 536 if !utf8.ValidString(cadenceErrMessage) { 537 h.log.Warn(). 538 Str("block_id", blockID.String()). 539 Str("transaction_id", txID.String()). 540 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 541 Msg("invalid character in Cadence error message") 542 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 543 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 544 } 545 result.ErrorMessage = cadenceErrMessage 546 } 547 return result, nil 548 } 549 550 // GetTransactionErrorMessageByIndex implements a grpc handler for getting a transaction error message by block ID and tx index. 551 // Expected error codes during normal operations: 552 // - codes.InvalidArgument - invalid blockID. 553 // - codes.NotFound - transaction result at index not found. 554 func (h *handler) GetTransactionErrorMessageByIndex( 555 _ context.Context, 556 req *execution.GetTransactionErrorMessageByIndexRequest, 557 ) (*execution.GetTransactionErrorMessageResponse, error) { 558 reqBlockID := req.GetBlockId() 559 blockID, err := convert.BlockID(reqBlockID) 560 if err != nil { 561 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 562 } 563 564 index := req.GetIndex() 565 566 // lookup any transaction error that might have occurred 567 txResult, err := h.transactionResults.ByBlockIDTransactionIndex(blockID, index) 568 if err != nil { 569 if errors.Is(err, storage.ErrNotFound) { 570 return nil, status.Error(codes.NotFound, "transaction result not found") 571 } 572 573 return nil, status.Errorf(codes.Internal, "failed to get transaction result: %v", err) 574 } 575 576 result := &execution.GetTransactionErrorMessageResponse{ 577 TransactionId: convert.IdentifierToMessage(txResult.TransactionID), 578 } 579 580 if len(txResult.ErrorMessage) > 0 { 581 cadenceErrMessage := txResult.ErrorMessage 582 if !utf8.ValidString(cadenceErrMessage) { 583 h.log.Warn(). 584 Str("block_id", blockID.String()). 585 Str("transaction_id", txResult.TransactionID.String()). 586 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 587 Msg("invalid character in Cadence error message") 588 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 589 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 590 } 591 result.ErrorMessage = cadenceErrMessage 592 } 593 return result, nil 594 } 595 596 // GetTransactionErrorMessagesByBlockID implements a grpc handler for getting transaction error messages by block ID. 597 // Only failed transactions will be returned. 598 // Expected error codes during normal operations: 599 // - codes.InvalidArgument - invalid blockID. 600 // - codes.NotFound - block was not executed or was pruned. 601 func (h *handler) GetTransactionErrorMessagesByBlockID( 602 _ context.Context, 603 req *execution.GetTransactionErrorMessagesByBlockIDRequest, 604 ) (*execution.GetTransactionErrorMessagesResponse, error) { 605 reqBlockID := req.GetBlockId() 606 blockID, err := convert.BlockID(reqBlockID) 607 if err != nil { 608 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 609 } 610 611 // must verify block was locally executed first since transactionResults.ByBlockID will return 612 // an empty slice if block does not exist 613 if _, err = h.commits.ByBlockID(blockID); err != nil { 614 if errors.Is(err, storage.ErrNotFound) { 615 return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockID) 616 } 617 return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockID) 618 } 619 620 // Get all tx results 621 txResults, err := h.transactionResults.ByBlockID(blockID) 622 if err != nil { 623 if errors.Is(err, storage.ErrNotFound) { 624 return nil, status.Error(codes.NotFound, "transaction results not found") 625 } 626 627 return nil, status.Errorf(codes.Internal, "failed to get transaction results: %v", err) 628 } 629 630 var results []*execution.GetTransactionErrorMessagesResponse_Result 631 for index, txResult := range txResults { 632 if len(txResult.ErrorMessage) == 0 { 633 continue 634 } 635 txIndex := uint32(index) 636 cadenceErrMessage := txResult.ErrorMessage 637 if !utf8.ValidString(cadenceErrMessage) { 638 h.log.Warn(). 639 Str("block_id", blockID.String()). 640 Uint32("index", txIndex). 641 Str("error_mgs", fmt.Sprintf("%q", cadenceErrMessage)). 642 Msg("invalid character in Cadence error message") 643 // convert non UTF-8 string to a UTF-8 string for safe GRPC marshaling 644 cadenceErrMessage = strings.ToValidUTF8(txResult.ErrorMessage, "?") 645 } 646 results = append(results, &execution.GetTransactionErrorMessagesResponse_Result{ 647 TransactionId: convert.IdentifierToMessage(txResult.TransactionID), 648 Index: txIndex, 649 ErrorMessage: cadenceErrMessage, 650 }) 651 } 652 653 return &execution.GetTransactionErrorMessagesResponse{ 654 Results: results, 655 }, nil 656 } 657 658 // eventResult creates EventsResponse_Result from flow.Event for the given blockID 659 func (h *handler) eventResult( 660 blockID flow.Identifier, 661 flowEvents []flow.Event, 662 ) (*execution.GetEventsForBlockIDsResponse_Result, error) { 663 664 // convert events to event message 665 events := convert.EventsToMessages(flowEvents) 666 667 // lookup block 668 header, err := h.headers.ByBlockID(blockID) 669 if err != nil { 670 return nil, status.Errorf(codes.Internal, "failed to lookup block: %v", err) 671 } 672 673 return &execution.GetEventsForBlockIDsResponse_Result{ 674 BlockId: blockID[:], 675 BlockHeight: header.Height, 676 Events: events, 677 }, nil 678 } 679 680 func (h *handler) GetAccountAtBlockID( 681 ctx context.Context, 682 req *execution.GetAccountAtBlockIDRequest, 683 ) (*execution.GetAccountAtBlockIDResponse, error) { 684 685 blockID := req.GetBlockId() 686 blockFlowID, err := convert.BlockID(blockID) 687 if err != nil { 688 return nil, status.Errorf(codes.InvalidArgument, "invalid blockID: %v", err) 689 } 690 691 flowAddress, err := convert.Address(req.GetAddress(), h.chain.Chain()) 692 if err != nil { 693 return nil, status.Errorf(codes.InvalidArgument, "invalid address: %v", err) 694 } 695 696 // return a more user friendly error if block has not been executed 697 if _, err = h.commits.ByBlockID(blockFlowID); err != nil { 698 if errors.Is(err, storage.ErrNotFound) { 699 return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockFlowID) 700 } 701 return nil, status.Errorf(codes.Internal, "state commitment for block ID %s could not be retrieved", blockFlowID) 702 } 703 704 value, err := h.engine.GetAccount(ctx, flowAddress, blockFlowID) 705 if err != nil { 706 if errors.Is(err, state.ErrExecutionStatePruned) { 707 return nil, status.Errorf(codes.OutOfRange, "state for block ID %s not available", blockFlowID) 708 } 709 if errors.Is(err, state.ErrNotExecuted) { 710 return nil, status.Errorf(codes.NotFound, "block %s has not been executed by node or was pruned", blockFlowID) 711 } 712 if errors.Is(err, storage.ErrNotFound) { 713 return nil, status.Errorf(codes.NotFound, "block %s not found", blockFlowID) 714 } 715 if fvmerrors.IsAccountNotFoundError(err) { 716 return nil, status.Errorf(codes.NotFound, "account not found") 717 } 718 return nil, status.Errorf(codes.Internal, "failed to get account: %v", err) 719 } 720 721 if value == nil { 722 return nil, status.Errorf(codes.NotFound, "account with address %s does not exist", flowAddress) 723 } 724 725 account, err := convert.AccountToMessage(value) 726 if err != nil { 727 return nil, status.Errorf(codes.Internal, "failed to convert account to message: %v", err) 728 } 729 730 res := &execution.GetAccountAtBlockIDResponse{ 731 Account: account, 732 } 733 734 return res, nil 735 736 } 737 738 // GetLatestBlockHeader gets the latest sealed or finalized block header. 739 func (h *handler) GetLatestBlockHeader( 740 _ context.Context, 741 req *execution.GetLatestBlockHeaderRequest, 742 ) (*execution.BlockHeaderResponse, error) { 743 var header *flow.Header 744 var err error 745 746 if req.GetIsSealed() { 747 // get the latest seal header from storage 748 header, err = h.state.Sealed().Head() 749 } else { 750 // get the finalized header from state 751 header, err = h.state.Final().Head() 752 } 753 if err != nil { 754 // this header MUST exist in the db, otherwise the node likely has inconsistent state. 755 // Don't crash as a result of an external API request, but other components will likely panic. 756 h.log.Err(err).Msg("failed to get latest block header. potentially inconsistent protocol state.") 757 return nil, status.Errorf(codes.Internal, "unable to get latest header: %v", err) 758 } 759 760 return h.blockHeaderResponse(header) 761 } 762 763 // GetBlockHeaderByID gets a block header by ID. 764 func (h *handler) GetBlockHeaderByID( 765 _ context.Context, 766 req *execution.GetBlockHeaderByIDRequest, 767 ) (*execution.BlockHeaderResponse, error) { 768 id, err := convert.BlockID(req.GetId()) 769 if err != nil { 770 return nil, err 771 } 772 header, err := h.headers.ByBlockID(id) 773 if err != nil { 774 return nil, status.Errorf(codes.NotFound, "not found: %v", err) 775 } 776 return h.blockHeaderResponse(header) 777 } 778 779 func (h *handler) blockHeaderResponse(header *flow.Header) (*execution.BlockHeaderResponse, error) { 780 signerIDs, err := h.signerIndicesDecoder.DecodeSignerIDs(header) 781 if err != nil { 782 // the block was retrieved from local storage - so no errors are expected 783 return nil, fmt.Errorf("failed to decode signer indices to Identifiers for block %v: %w", header.ID(), err) 784 } 785 786 msg, err := convert.BlockHeaderToMessage(header, signerIDs) 787 if err != nil { 788 return nil, err 789 } 790 791 return &execution.BlockHeaderResponse{ 792 Block: msg, 793 }, nil 794 }