github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/state_stream/backend/handler.go (about) 1 package backend 2 3 import ( 4 "context" 5 6 "google.golang.org/grpc/codes" 7 "google.golang.org/grpc/status" 8 "google.golang.org/protobuf/types/known/timestamppb" 9 10 "github.com/onflow/flow/protobuf/go/flow/entities" 11 "github.com/onflow/flow/protobuf/go/flow/executiondata" 12 13 "github.com/onflow/flow-go/engine/access/state_stream" 14 "github.com/onflow/flow-go/engine/access/subscription" 15 "github.com/onflow/flow-go/engine/common/rpc" 16 "github.com/onflow/flow-go/engine/common/rpc/convert" 17 "github.com/onflow/flow-go/model/flow" 18 "github.com/onflow/flow-go/module/counters" 19 ) 20 21 type Handler struct { 22 subscription.StreamingData 23 24 api state_stream.API 25 chain flow.Chain 26 27 eventFilterConfig state_stream.EventFilterConfig 28 defaultHeartbeatInterval uint64 29 } 30 31 // sendSubscribeEventsResponseFunc is a callback function used to send 32 // SubscribeEventsResponse to the client stream. 33 type sendSubscribeEventsResponseFunc func(*executiondata.SubscribeEventsResponse) error 34 35 // sendSubscribeExecutionDataResponseFunc is a callback function used to send 36 // SubscribeExecutionDataResponse to the client stream. 37 type sendSubscribeExecutionDataResponseFunc func(*executiondata.SubscribeExecutionDataResponse) error 38 39 var _ executiondata.ExecutionDataAPIServer = (*Handler)(nil) 40 41 func NewHandler(api state_stream.API, chain flow.Chain, config Config) *Handler { 42 h := &Handler{ 43 StreamingData: subscription.NewStreamingData(config.MaxGlobalStreams), 44 api: api, 45 chain: chain, 46 eventFilterConfig: config.EventFilterConfig, 47 defaultHeartbeatInterval: config.HeartbeatInterval, 48 } 49 return h 50 } 51 52 func (h *Handler) GetExecutionDataByBlockID(ctx context.Context, request *executiondata.GetExecutionDataByBlockIDRequest) (*executiondata.GetExecutionDataByBlockIDResponse, error) { 53 blockID, err := convert.BlockID(request.GetBlockId()) 54 if err != nil { 55 return nil, status.Errorf(codes.InvalidArgument, "could not convert block ID: %v", err) 56 } 57 58 execData, err := h.api.GetExecutionDataByBlockID(ctx, blockID) 59 if err != nil { 60 return nil, rpc.ConvertError(err, "could no get execution data", codes.Internal) 61 } 62 63 message, err := convert.BlockExecutionDataToMessage(execData) 64 if err != nil { 65 return nil, status.Errorf(codes.Internal, "could not convert execution data to entity: %v", err) 66 } 67 68 err = convert.BlockExecutionDataEventPayloadsToVersion(message, request.GetEventEncodingVersion()) 69 if err != nil { 70 return nil, status.Errorf(codes.Internal, "could not convert execution data event payloads to JSON: %v", err) 71 } 72 73 return &executiondata.GetExecutionDataByBlockIDResponse{BlockExecutionData: message}, nil 74 } 75 76 // SubscribeExecutionData is deprecated and will be removed in a future version. 77 // Use SubscribeExecutionDataFromStartBlockID, SubscribeExecutionDataFromStartBlockHeight or SubscribeExecutionDataFromLatest. 78 // 79 // SubscribeExecutionData handles subscription requests for execution data starting at the specified block ID or block height. 80 // The handler manages the subscription and sends the subscribed information to the client via the provided stream. 81 // 82 // Expected errors during normal operation: 83 // - codes.InvalidArgument - if request contains invalid startBlockID. 84 // - codes.ResourceExhausted - if the maximum number of streams is reached. 85 // - codes.Internal - if stream got unexpected response or could not send response. 86 func (h *Handler) SubscribeExecutionData(request *executiondata.SubscribeExecutionDataRequest, stream executiondata.ExecutionDataAPI_SubscribeExecutionDataServer) error { 87 // check if the maximum number of streams is reached 88 if h.StreamCount.Load() >= h.MaxStreams { 89 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 90 } 91 h.StreamCount.Add(1) 92 defer h.StreamCount.Add(-1) 93 94 startBlockID := flow.ZeroID 95 if request.GetStartBlockId() != nil { 96 blockID, err := convert.BlockID(request.GetStartBlockId()) 97 if err != nil { 98 return status.Errorf(codes.InvalidArgument, "could not convert start block ID: %v", err) 99 } 100 startBlockID = blockID 101 } 102 103 sub := h.api.SubscribeExecutionData(stream.Context(), startBlockID, request.GetStartBlockHeight()) 104 105 return subscription.HandleSubscription(sub, handleSubscribeExecutionData(stream.Send, request.GetEventEncodingVersion())) 106 } 107 108 // SubscribeExecutionDataFromStartBlockID handles subscription requests for 109 // execution data starting at the specified block ID. The handler manages the 110 // subscription and sends the subscribed information to the client via the 111 // provided stream. 112 // 113 // Expected errors during normal operation: 114 // - codes.InvalidArgument - if request contains invalid startBlockID. 115 // - codes.ResourceExhausted - if the maximum number of streams is reached. 116 // - codes.Internal - if stream got unexpected response or could not send response. 117 func (h *Handler) SubscribeExecutionDataFromStartBlockID(request *executiondata.SubscribeExecutionDataFromStartBlockIDRequest, stream executiondata.ExecutionDataAPI_SubscribeExecutionDataFromStartBlockIDServer) error { 118 // check if the maximum number of streams is reached 119 if h.StreamCount.Load() >= h.MaxStreams { 120 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 121 } 122 h.StreamCount.Add(1) 123 defer h.StreamCount.Add(-1) 124 125 startBlockID, err := convert.BlockID(request.GetStartBlockId()) 126 if err != nil { 127 return status.Errorf(codes.InvalidArgument, "could not convert start block ID: %v", err) 128 } 129 130 sub := h.api.SubscribeExecutionDataFromStartBlockID(stream.Context(), startBlockID) 131 132 return subscription.HandleSubscription(sub, handleSubscribeExecutionData(stream.Send, request.GetEventEncodingVersion())) 133 } 134 135 // SubscribeExecutionDataFromStartBlockHeight handles subscription requests for 136 // execution data starting at the specified block height. The handler manages the 137 // subscription and sends the subscribed information to the client via the 138 // provided stream. 139 // 140 // Expected errors during normal operation: 141 // - codes.ResourceExhausted - if the maximum number of streams is reached. 142 // - codes.Internal - if stream got unexpected response or could not send response. 143 func (h *Handler) SubscribeExecutionDataFromStartBlockHeight(request *executiondata.SubscribeExecutionDataFromStartBlockHeightRequest, stream executiondata.ExecutionDataAPI_SubscribeExecutionDataFromStartBlockHeightServer) error { 144 // check if the maximum number of streams is reached 145 if h.StreamCount.Load() >= h.MaxStreams { 146 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 147 } 148 h.StreamCount.Add(1) 149 defer h.StreamCount.Add(-1) 150 151 sub := h.api.SubscribeExecutionDataFromStartBlockHeight(stream.Context(), request.GetStartBlockHeight()) 152 153 return subscription.HandleSubscription(sub, handleSubscribeExecutionData(stream.Send, request.GetEventEncodingVersion())) 154 } 155 156 // SubscribeExecutionDataFromLatest handles subscription requests for 157 // execution data starting at the latest block. The handler manages the 158 // subscription and sends the subscribed information to the client via the 159 // provided stream. 160 // 161 // Expected errors during normal operation: 162 // - codes.ResourceExhausted - if the maximum number of streams is reached. 163 // - codes.Internal - if stream got unexpected response or could not send response. 164 func (h *Handler) SubscribeExecutionDataFromLatest(request *executiondata.SubscribeExecutionDataFromLatestRequest, stream executiondata.ExecutionDataAPI_SubscribeExecutionDataFromLatestServer) error { 165 // check if the maximum number of streams is reached 166 if h.StreamCount.Load() >= h.MaxStreams { 167 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 168 } 169 h.StreamCount.Add(1) 170 defer h.StreamCount.Add(-1) 171 172 sub := h.api.SubscribeExecutionDataFromLatest(stream.Context()) 173 174 return subscription.HandleSubscription(sub, handleSubscribeExecutionData(stream.Send, request.GetEventEncodingVersion())) 175 } 176 177 // SubscribeEvents is deprecated and will be removed in a future version. 178 // Use SubscribeEventsFromStartBlockID, SubscribeEventsFromStartHeight or SubscribeEventsFromLatest. 179 // 180 // SubscribeEvents handles subscription requests for events starting at the specified block ID or block height. 181 // The handler manages the subscription and sends the subscribed information to the client via the provided stream. 182 // 183 // Responses are returned for each block containing at least one event that matches the filter. Additionally, 184 // heartbeat responses (SubscribeEventsResponse with no events) are returned periodically to allow 185 // clients to track which blocks were searched. Clients can use this 186 // information to determine which block to start from when reconnecting. 187 // 188 // Expected errors during normal operation: 189 // - codes.InvalidArgument - if provided both startBlockID and startHeight, if invalid startBlockID is provided, if invalid event filter is provided. 190 // - codes.ResourceExhausted - if the maximum number of streams is reached. 191 // - codes.Internal - could not convert events to entity, if stream encountered an error, if stream got unexpected response or could not send response. 192 func (h *Handler) SubscribeEvents(request *executiondata.SubscribeEventsRequest, stream executiondata.ExecutionDataAPI_SubscribeEventsServer) error { 193 // check if the maximum number of streams is reached 194 if h.StreamCount.Load() >= h.MaxStreams { 195 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 196 } 197 h.StreamCount.Add(1) 198 defer h.StreamCount.Add(-1) 199 200 startBlockID := flow.ZeroID 201 if request.GetStartBlockId() != nil { 202 blockID, err := convert.BlockID(request.GetStartBlockId()) 203 if err != nil { 204 return status.Errorf(codes.InvalidArgument, "could not convert start block ID: %v", err) 205 } 206 startBlockID = blockID 207 } 208 209 filter, err := h.getEventFilter(request.GetFilter()) 210 if err != nil { 211 return err 212 } 213 214 sub := h.api.SubscribeEvents(stream.Context(), startBlockID, request.GetStartBlockHeight(), filter) 215 216 return subscription.HandleSubscription(sub, h.handleEventsResponse(stream.Send, request.HeartbeatInterval, request.GetEventEncodingVersion())) 217 } 218 219 // SubscribeEventsFromStartBlockID handles subscription requests for events starting at the specified block ID. 220 // The handler manages the subscription and sends the subscribed information to the client via the provided stream. 221 // 222 // Responses are returned for each block containing at least one event that matches the filter. Additionally, 223 // heartbeat responses (SubscribeEventsResponse with no events) are returned periodically to allow 224 // clients to track which blocks were searched. Clients can use this 225 // information to determine which block to start from when reconnecting. 226 // 227 // Expected errors during normal operation: 228 // - codes.InvalidArgument - if invalid startBlockID is provided, if invalid event filter is provided. 229 // - codes.ResourceExhausted - if the maximum number of streams is reached. 230 // - codes.Internal - could not convert events to entity, if stream encountered an error, if stream got unexpected response or could not send response. 231 func (h *Handler) SubscribeEventsFromStartBlockID(request *executiondata.SubscribeEventsFromStartBlockIDRequest, stream executiondata.ExecutionDataAPI_SubscribeEventsFromStartBlockIDServer) error { 232 // check if the maximum number of streams is reached 233 if h.StreamCount.Load() >= h.MaxStreams { 234 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 235 } 236 h.StreamCount.Add(1) 237 defer h.StreamCount.Add(-1) 238 239 startBlockID, err := convert.BlockID(request.GetStartBlockId()) 240 if err != nil { 241 return status.Errorf(codes.InvalidArgument, "could not convert start block ID: %v", err) 242 } 243 244 filter, err := h.getEventFilter(request.GetFilter()) 245 if err != nil { 246 return err 247 } 248 249 sub := h.api.SubscribeEventsFromStartBlockID(stream.Context(), startBlockID, filter) 250 251 return subscription.HandleSubscription(sub, h.handleEventsResponse(stream.Send, request.HeartbeatInterval, request.GetEventEncodingVersion())) 252 } 253 254 // SubscribeEventsFromStartHeight handles subscription requests for events starting at the specified block height. 255 // The handler manages the subscription and sends the subscribed information to the client via the provided stream. 256 // 257 // Responses are returned for each block containing at least one event that matches the filter. Additionally, 258 // heartbeat responses (SubscribeEventsResponse with no events) are returned periodically to allow 259 // clients to track which blocks were searched. Clients can use this 260 // information to determine which block to start from when reconnecting. 261 // 262 // Expected errors during normal operation: 263 // - codes.InvalidArgument - if invalid event filter is provided. 264 // - codes.ResourceExhausted - if the maximum number of streams is reached. 265 // - codes.Internal - could not convert events to entity, if stream encountered an error, if stream got unexpected response or could not send response. 266 func (h *Handler) SubscribeEventsFromStartHeight(request *executiondata.SubscribeEventsFromStartHeightRequest, stream executiondata.ExecutionDataAPI_SubscribeEventsFromStartHeightServer) error { 267 // check if the maximum number of streams is reached 268 if h.StreamCount.Load() >= h.MaxStreams { 269 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 270 } 271 h.StreamCount.Add(1) 272 defer h.StreamCount.Add(-1) 273 274 filter, err := h.getEventFilter(request.GetFilter()) 275 if err != nil { 276 return err 277 } 278 279 sub := h.api.SubscribeEventsFromStartHeight(stream.Context(), request.GetStartBlockHeight(), filter) 280 281 return subscription.HandleSubscription(sub, h.handleEventsResponse(stream.Send, request.HeartbeatInterval, request.GetEventEncodingVersion())) 282 } 283 284 // SubscribeEventsFromLatest handles subscription requests for events started from latest sealed block.. 285 // The handler manages the subscription and sends the subscribed information to the client via the provided stream. 286 // 287 // Responses are returned for each block containing at least one event that matches the filter. Additionally, 288 // heartbeat responses (SubscribeEventsResponse with no events) are returned periodically to allow 289 // clients to track which blocks were searched. Clients can use this 290 // information to determine which block to start from when reconnecting. 291 // 292 // Expected errors during normal operation: 293 // - codes.InvalidArgument - if invalid event filter is provided. 294 // - codes.ResourceExhausted - if the maximum number of streams is reached. 295 // - codes.Internal - could not convert events to entity, if stream encountered an error, if stream got unexpected response or could not send response. 296 func (h *Handler) SubscribeEventsFromLatest(request *executiondata.SubscribeEventsFromLatestRequest, stream executiondata.ExecutionDataAPI_SubscribeEventsFromLatestServer) error { 297 // check if the maximum number of streams is reached 298 if h.StreamCount.Load() >= h.MaxStreams { 299 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 300 } 301 h.StreamCount.Add(1) 302 defer h.StreamCount.Add(-1) 303 304 filter, err := h.getEventFilter(request.GetFilter()) 305 if err != nil { 306 return err 307 } 308 309 sub := h.api.SubscribeEventsFromLatest(stream.Context(), filter) 310 311 return subscription.HandleSubscription(sub, h.handleEventsResponse(stream.Send, request.HeartbeatInterval, request.GetEventEncodingVersion())) 312 } 313 314 // handleSubscribeExecutionData handles the subscription to execution data and sends it to the client via the provided stream. 315 // This function is designed to be used as a callback for execution data updates in a subscription. 316 // 317 // Parameters: 318 // - send: The function responsible for sending execution data in response to the client. 319 // 320 // Returns a function that can be used as a callback for execution data updates. 321 // 322 // Expected errors during normal operation: 323 // - codes.Internal - could not convert execution data to entity or could not convert execution data event payloads to JSON. 324 func handleSubscribeExecutionData(send sendSubscribeExecutionDataResponseFunc, eventEncodingVersion entities.EventEncodingVersion) func(response *ExecutionDataResponse) error { 325 return func(resp *ExecutionDataResponse) error { 326 execData, err := convert.BlockExecutionDataToMessage(resp.ExecutionData) 327 if err != nil { 328 return status.Errorf(codes.Internal, "could not convert execution data to entity: %v", err) 329 } 330 331 err = convert.BlockExecutionDataEventPayloadsToVersion(execData, eventEncodingVersion) 332 if err != nil { 333 return status.Errorf(codes.Internal, "could not convert execution data event payloads to JSON: %v", err) 334 } 335 336 err = send(&executiondata.SubscribeExecutionDataResponse{ 337 BlockHeight: resp.Height, 338 BlockExecutionData: execData, 339 BlockTimestamp: timestamppb.New(resp.BlockTimestamp), 340 }) 341 342 return err 343 } 344 } 345 346 // handleEventsResponse handles the event subscription and sends subscribed events to the client via the provided stream. 347 // This function is designed to be used as a callback for events updates in a subscription. 348 // It takes a EventsResponse, processes it, and sends the corresponding response to the client using the provided send function. 349 // 350 // Parameters: 351 // - send: The function responsible for sending events response to the client. 352 // 353 // Returns a function that can be used as a callback for events updates. 354 // 355 // Expected errors during normal operation: 356 // - codes.Internal - could not convert events to entity or the stream could not send a response. 357 func (h *Handler) handleEventsResponse(send sendSubscribeEventsResponseFunc, heartbeatInterval uint64, eventEncodingVersion entities.EventEncodingVersion) func(*EventsResponse) error { 358 if heartbeatInterval == 0 { 359 heartbeatInterval = h.defaultHeartbeatInterval 360 } 361 362 blocksSinceLastMessage := uint64(0) 363 messageIndex := counters.NewMonotonousCounter(0) 364 365 return func(resp *EventsResponse) error { 366 // check if there are any events in the response. if not, do not send a message unless the last 367 // response was more than HeartbeatInterval blocks ago 368 if len(resp.Events) == 0 { 369 blocksSinceLastMessage++ 370 if blocksSinceLastMessage < heartbeatInterval { 371 return nil 372 } 373 blocksSinceLastMessage = 0 374 } 375 376 // BlockExecutionData contains CCF encoded events, and the Access API returns JSON-CDC events. 377 // convert event payload formats. 378 // This is a temporary solution until the Access API supports specifying the encoding in the request 379 events, err := convert.EventsToMessagesWithEncodingConversion(resp.Events, entities.EventEncodingVersion_CCF_V0, eventEncodingVersion) 380 if err != nil { 381 return status.Errorf(codes.Internal, "could not convert events to entity: %v", err) 382 } 383 384 index := messageIndex.Increment() 385 386 err = send(&executiondata.SubscribeEventsResponse{ 387 BlockHeight: resp.Height, 388 BlockId: convert.IdentifierToMessage(resp.BlockID), 389 Events: events, 390 BlockTimestamp: timestamppb.New(resp.BlockTimestamp), 391 MessageIndex: index, 392 }) 393 if err != nil { 394 return rpc.ConvertError(err, "could not send response", codes.Internal) 395 } 396 397 return nil 398 } 399 } 400 401 // getEventFilter returns an event filter based on the provided event filter configuration. 402 // If the event filter is nil, it returns an empty filter. 403 // Otherwise, it initializes a new event filter using the provided filter parameters, 404 // including the event type, address, and contract. It then validates the filter configuration 405 // and returns the constructed event filter or an error if the filter configuration is invalid. 406 // The event filter is used for subscription to events. 407 // 408 // Parameters: 409 // - eventFilter: executiondata.EventFilter object containing filter parameters. 410 // 411 // Expected errors during normal operation: 412 // - codes.InvalidArgument - if the provided event filter is invalid. 413 func (h *Handler) getEventFilter(eventFilter *executiondata.EventFilter) (state_stream.EventFilter, error) { 414 if eventFilter == nil { 415 return state_stream.EventFilter{}, nil 416 } 417 filter, err := state_stream.NewEventFilter( 418 h.eventFilterConfig, 419 h.chain, 420 eventFilter.GetEventType(), 421 eventFilter.GetAddress(), 422 eventFilter.GetContract(), 423 ) 424 if err != nil { 425 return filter, status.Errorf(codes.InvalidArgument, "invalid event filter: %v", err) 426 } 427 return filter, nil 428 } 429 430 func (h *Handler) GetRegisterValues(_ context.Context, request *executiondata.GetRegisterValuesRequest) (*executiondata.GetRegisterValuesResponse, error) { 431 // Convert data 432 registerIDs, err := convert.MessagesToRegisterIDs(request.GetRegisterIds(), h.chain) 433 if err != nil { 434 return nil, status.Errorf(codes.InvalidArgument, "could not convert register IDs: %v", err) 435 } 436 437 // get payload from store 438 values, err := h.api.GetRegisterValues(registerIDs, request.GetBlockHeight()) 439 if err != nil { 440 return nil, rpc.ConvertError(err, "could not get register values", codes.Internal) 441 } 442 443 return &executiondata.GetRegisterValuesResponse{Values: values}, nil 444 } 445 446 // convertAccountsStatusesResultsToMessage converts account status responses to the message 447 func convertAccountsStatusesResultsToMessage( 448 eventVersion entities.EventEncodingVersion, 449 resp *AccountStatusesResponse, 450 ) ([]*executiondata.SubscribeAccountStatusesResponse_Result, error) { 451 var results []*executiondata.SubscribeAccountStatusesResponse_Result 452 for address, events := range resp.AccountEvents { 453 convertedEvent, err := convert.EventsToMessagesWithEncodingConversion(events, entities.EventEncodingVersion_CCF_V0, eventVersion) 454 if err != nil { 455 return nil, status.Errorf(codes.Internal, "could not convert events to entity: %v", err) 456 } 457 458 results = append(results, &executiondata.SubscribeAccountStatusesResponse_Result{ 459 Address: flow.HexToAddress(address).Bytes(), 460 Events: convertedEvent, 461 }) 462 } 463 return results, nil 464 } 465 466 // sendSubscribeAccountStatusesResponseFunc defines the function signature for sending account status responses 467 type sendSubscribeAccountStatusesResponseFunc func(*executiondata.SubscribeAccountStatusesResponse) error 468 469 // handleAccountStatusesResponse handles account status responses by converting them to the message and sending them to the subscriber. 470 func (h *Handler) handleAccountStatusesResponse( 471 heartbeatInterval uint64, 472 evenVersion entities.EventEncodingVersion, 473 send sendSubscribeAccountStatusesResponseFunc, 474 ) func(resp *AccountStatusesResponse) error { 475 if heartbeatInterval == 0 { 476 heartbeatInterval = h.defaultHeartbeatInterval 477 } 478 479 blocksSinceLastMessage := uint64(0) 480 messageIndex := counters.NewMonotonousCounter(0) 481 482 return func(resp *AccountStatusesResponse) error { 483 // check if there are any events in the response. if not, do not send a message unless the last 484 // response was more than HeartbeatInterval blocks ago 485 if len(resp.AccountEvents) == 0 { 486 blocksSinceLastMessage++ 487 if blocksSinceLastMessage < heartbeatInterval { 488 return nil 489 } 490 blocksSinceLastMessage = 0 491 } 492 493 results, err := convertAccountsStatusesResultsToMessage(evenVersion, resp) 494 if err != nil { 495 return err 496 } 497 498 index := messageIndex.Increment() 499 500 err = send(&executiondata.SubscribeAccountStatusesResponse{ 501 BlockId: convert.IdentifierToMessage(resp.BlockID), 502 BlockHeight: resp.Height, 503 Results: results, 504 MessageIndex: index, 505 }) 506 if err != nil { 507 return rpc.ConvertError(err, "could not send response", codes.Internal) 508 } 509 510 return nil 511 } 512 } 513 514 // SubscribeAccountStatusesFromStartBlockID streams account statuses for all blocks starting at the requested 515 // start block ID, up until the latest available block. Once the latest is 516 // reached, the stream will remain open and responses are sent for each new 517 // block as it becomes available. 518 func (h *Handler) SubscribeAccountStatusesFromStartBlockID( 519 request *executiondata.SubscribeAccountStatusesFromStartBlockIDRequest, 520 stream executiondata.ExecutionDataAPI_SubscribeAccountStatusesFromStartBlockIDServer, 521 ) error { 522 // check if the maximum number of streams is reached 523 if h.StreamCount.Load() >= h.MaxStreams { 524 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 525 } 526 527 h.StreamCount.Add(1) 528 defer h.StreamCount.Add(-1) 529 530 startBlockID, err := convert.BlockID(request.GetStartBlockId()) 531 if err != nil { 532 return status.Errorf(codes.InvalidArgument, "could not convert start block ID: %v", err) 533 } 534 535 statusFilter := request.GetFilter() 536 filter, err := state_stream.NewAccountStatusFilter(h.eventFilterConfig, h.chain, statusFilter.GetEventType(), statusFilter.GetAddress()) 537 if err != nil { 538 return status.Errorf(codes.InvalidArgument, "could not create account status filter: %v", err) 539 } 540 541 sub := h.api.SubscribeAccountStatusesFromStartBlockID(stream.Context(), startBlockID, filter) 542 543 return subscription.HandleSubscription(sub, h.handleAccountStatusesResponse(request.HeartbeatInterval, request.GetEventEncodingVersion(), stream.Send)) 544 } 545 546 // SubscribeAccountStatusesFromStartHeight streams account statuses for all blocks starting at the requested 547 // start block height, up until the latest available block. Once the latest is 548 // reached, the stream will remain open and responses are sent for each new 549 // block as it becomes available. 550 func (h *Handler) SubscribeAccountStatusesFromStartHeight( 551 request *executiondata.SubscribeAccountStatusesFromStartHeightRequest, 552 stream executiondata.ExecutionDataAPI_SubscribeAccountStatusesFromStartHeightServer, 553 ) error { 554 // check if the maximum number of streams is reached 555 if h.StreamCount.Load() >= h.MaxStreams { 556 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 557 } 558 559 h.StreamCount.Add(1) 560 defer h.StreamCount.Add(-1) 561 562 statusFilter := request.GetFilter() 563 filter, err := state_stream.NewAccountStatusFilter(h.eventFilterConfig, h.chain, statusFilter.GetEventType(), statusFilter.GetAddress()) 564 if err != nil { 565 return status.Errorf(codes.InvalidArgument, "could not create account status filter: %v", err) 566 } 567 568 sub := h.api.SubscribeAccountStatusesFromStartHeight(stream.Context(), request.GetStartBlockHeight(), filter) 569 570 return subscription.HandleSubscription(sub, h.handleAccountStatusesResponse(request.HeartbeatInterval, request.GetEventEncodingVersion(), stream.Send)) 571 } 572 573 // SubscribeAccountStatusesFromLatestBlock streams account statuses for all blocks starting 574 // at the last sealed block, up until the latest available block. Once the latest is 575 // reached, the stream will remain open and responses are sent for each new 576 // block as it becomes available. 577 func (h *Handler) SubscribeAccountStatusesFromLatestBlock( 578 request *executiondata.SubscribeAccountStatusesFromLatestBlockRequest, 579 stream executiondata.ExecutionDataAPI_SubscribeAccountStatusesFromLatestBlockServer, 580 ) error { 581 // check if the maximum number of streams is reached 582 if h.StreamCount.Load() >= h.MaxStreams { 583 return status.Errorf(codes.ResourceExhausted, "maximum number of streams reached") 584 } 585 586 h.StreamCount.Add(1) 587 defer h.StreamCount.Add(-1) 588 589 statusFilter := request.GetFilter() 590 filter, err := state_stream.NewAccountStatusFilter(h.eventFilterConfig, h.chain, statusFilter.GetEventType(), statusFilter.GetAddress()) 591 if err != nil { 592 return status.Errorf(codes.InvalidArgument, "could not create account status filter: %v", err) 593 } 594 595 sub := h.api.SubscribeAccountStatusesFromLatestBlock(stream.Context(), filter) 596 597 return subscription.HandleSubscription(sub, h.handleAccountStatusesResponse(request.HeartbeatInterval, request.GetEventEncodingVersion(), stream.Send)) 598 }