github.com/onflow/flow-go@v0.33.17/engine/access/rpc/backend/backend_events.go (about) 1 package backend 2 3 import ( 4 "context" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "sort" 9 "time" 10 11 "github.com/onflow/flow/protobuf/go/flow/entities" 12 13 execproto "github.com/onflow/flow/protobuf/go/flow/execution" 14 "github.com/rs/zerolog" 15 "google.golang.org/grpc/codes" 16 "google.golang.org/grpc/status" 17 18 "github.com/onflow/flow-go/engine/access/rpc/connection" 19 "github.com/onflow/flow-go/engine/common/rpc" 20 "github.com/onflow/flow-go/engine/common/rpc/convert" 21 "github.com/onflow/flow-go/model/events" 22 "github.com/onflow/flow-go/model/flow" 23 "github.com/onflow/flow-go/module/irrecoverable" 24 "github.com/onflow/flow-go/module/state_synchronization/indexer" 25 "github.com/onflow/flow-go/state/protocol" 26 "github.com/onflow/flow-go/storage" 27 ) 28 29 type backendEvents struct { 30 headers storage.Headers 31 executionReceipts storage.ExecutionReceipts 32 state protocol.State 33 chain flow.Chain 34 connFactory connection.ConnectionFactory 35 log zerolog.Logger 36 maxHeightRange uint 37 nodeCommunicator Communicator 38 queryMode IndexQueryMode 39 eventsIndex *EventsIndex 40 } 41 42 // blockMetadata is used to capture information about requested blocks to avoid repeated blockID 43 // calculations and passing around full block headers. 44 type blockMetadata struct { 45 ID flow.Identifier 46 Height uint64 47 Timestamp time.Time 48 } 49 50 // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and 51 // the end block height (inclusive) that have the given type. 52 func (b *backendEvents) GetEventsForHeightRange( 53 ctx context.Context, 54 eventType string, 55 startHeight, endHeight uint64, 56 requiredEventEncodingVersion entities.EventEncodingVersion, 57 ) ([]flow.BlockEvents, error) { 58 59 if endHeight < startHeight { 60 return nil, status.Error(codes.InvalidArgument, "start height must not be larger than end height") 61 } 62 63 rangeSize := endHeight - startHeight + 1 // range is inclusive on both ends 64 if rangeSize > uint64(b.maxHeightRange) { 65 return nil, status.Errorf(codes.InvalidArgument, 66 "requested block range (%d) exceeded maximum (%d)", rangeSize, b.maxHeightRange) 67 } 68 69 // get the latest sealed block header 70 sealed, err := b.state.Sealed().Head() 71 if err != nil { 72 // sealed block must be in the store, so throw an exception for any error 73 err := irrecoverable.NewExceptionf("failed to lookup sealed header: %w", err) 74 irrecoverable.Throw(ctx, err) 75 return nil, err 76 } 77 78 // start height should not be beyond the last sealed height 79 if startHeight > sealed.Height { 80 return nil, status.Errorf(codes.OutOfRange, 81 "start height %d is greater than the last sealed block height %d", startHeight, sealed.Height) 82 } 83 84 // limit max height to last sealed block in the chain 85 // 86 // Note: this causes unintuitive behavior for clients making requests through a proxy that 87 // fronts multiple nodes. With that setup, clients may receive responses for a smaller range 88 // than requested because the node serving the request has a slightly delayed view of the chain. 89 // 90 // An alternative option is to return an error here, but that's likely to cause more pain for 91 // these clients since the requests would intermittently fail. it's recommended instead to 92 // check the block height of the last message in the response. this will be the last block 93 // height searched, and can be used to determine the start height for the next range. 94 if endHeight > sealed.Height { 95 endHeight = sealed.Height 96 } 97 98 // find the block headers for all the blocks between min and max height (inclusive) 99 blockHeaders := make([]blockMetadata, 0, endHeight-startHeight+1) 100 101 for i := startHeight; i <= endHeight; i++ { 102 // this looks inefficient, but is actually what's done under the covers by `headers.ByHeight` 103 // and avoids calculating header.ID() for each block. 104 blockID, err := b.headers.BlockIDByHeight(i) 105 if err != nil { 106 return nil, rpc.ConvertStorageError(fmt.Errorf("failed to get blockID for %d: %w", i, err)) 107 } 108 header, err := b.headers.ByBlockID(blockID) 109 if err != nil { 110 return nil, rpc.ConvertStorageError(fmt.Errorf("failed to get block header for %d: %w", i, err)) 111 } 112 113 blockHeaders = append(blockHeaders, blockMetadata{ 114 ID: blockID, 115 Height: header.Height, 116 Timestamp: header.Timestamp, 117 }) 118 } 119 120 return b.getBlockEvents(ctx, blockHeaders, eventType, requiredEventEncodingVersion) 121 } 122 123 // GetEventsForBlockIDs retrieves events for all the specified block IDs that have the given type 124 func (b *backendEvents) GetEventsForBlockIDs( 125 ctx context.Context, 126 eventType string, 127 blockIDs []flow.Identifier, 128 requiredEventEncodingVersion entities.EventEncodingVersion, 129 ) ([]flow.BlockEvents, error) { 130 131 if uint(len(blockIDs)) > b.maxHeightRange { 132 return nil, status.Errorf(codes.InvalidArgument, "requested block range (%d) exceeded maximum (%d)", len(blockIDs), b.maxHeightRange) 133 } 134 135 // find the block headers for all the block IDs 136 blockHeaders := make([]blockMetadata, 0, len(blockIDs)) 137 for _, blockID := range blockIDs { 138 header, err := b.headers.ByBlockID(blockID) 139 if err != nil { 140 return nil, rpc.ConvertStorageError(fmt.Errorf("failed to get block header for %s: %w", blockID, err)) 141 } 142 143 blockHeaders = append(blockHeaders, blockMetadata{ 144 ID: blockID, 145 Height: header.Height, 146 Timestamp: header.Timestamp, 147 }) 148 } 149 150 return b.getBlockEvents(ctx, blockHeaders, eventType, requiredEventEncodingVersion) 151 } 152 153 // getBlockEvents retrieves events for all the specified blocks that have the given type 154 // It gets all events available in storage, and requests the rest from an execution node. 155 func (b *backendEvents) getBlockEvents( 156 ctx context.Context, 157 blockInfos []blockMetadata, 158 eventType string, 159 requiredEventEncodingVersion entities.EventEncodingVersion, 160 ) ([]flow.BlockEvents, error) { 161 target := flow.EventType(eventType) 162 163 if _, err := events.ValidateEvent(target, b.chain); err != nil { 164 return nil, status.Errorf(codes.InvalidArgument, "invalid event type: %v", err) 165 } 166 167 switch b.queryMode { 168 case IndexQueryModeExecutionNodesOnly: 169 return b.getBlockEventsFromExecutionNode(ctx, blockInfos, eventType, requiredEventEncodingVersion) 170 171 case IndexQueryModeLocalOnly: 172 localResponse, missingBlocks, err := b.getBlockEventsFromStorage(ctx, blockInfos, target, requiredEventEncodingVersion) 173 if err != nil { 174 return nil, err 175 } 176 // all blocks should be available. 177 if len(missingBlocks) > 0 { 178 return nil, status.Errorf(codes.NotFound, "events not found in local storage for %d blocks", len(missingBlocks)) 179 } 180 return localResponse, nil 181 182 case IndexQueryModeFailover: 183 localResponse, missingBlocks, err := b.getBlockEventsFromStorage(ctx, blockInfos, target, requiredEventEncodingVersion) 184 if err != nil { 185 // if there was an error, request all blocks from execution nodes 186 missingBlocks = blockInfos 187 b.log.Debug().Err(err).Msg("failed to get events from local storage") 188 } 189 190 if len(missingBlocks) == 0 { 191 return localResponse, nil 192 } 193 194 b.log.Debug(). 195 Int("missing_blocks", len(missingBlocks)). 196 Msg("querying execution nodes for events from missing blocks") 197 198 enResponse, err := b.getBlockEventsFromExecutionNode(ctx, missingBlocks, eventType, requiredEventEncodingVersion) 199 if err != nil { 200 return nil, err 201 } 202 203 // sort ascending by block height 204 // this is needed because some blocks may be retrieved from storage and others from execution nodes. 205 // most likely, the earlier blocks will all be found in local storage, but that's not guaranteed, 206 // especially for nodes started after a spork, or once pruning is enabled. 207 // Note: this may not match the order of the original request for clients using GetEventsForBlockIDs 208 // that provide out of order block IDs 209 response := append(localResponse, enResponse...) 210 sort.Slice(response, func(i, j int) bool { 211 return response[i].BlockHeight < response[j].BlockHeight 212 }) 213 return response, nil 214 215 default: 216 return nil, status.Errorf(codes.Internal, "unknown event query mode: %v", b.queryMode) 217 } 218 } 219 220 // getBlockEventsFromStorage retrieves events for all the specified blocks that have the given type 221 // from the local storage 222 func (b *backendEvents) getBlockEventsFromStorage( 223 ctx context.Context, 224 blockInfos []blockMetadata, 225 eventType flow.EventType, 226 requiredEventEncodingVersion entities.EventEncodingVersion, 227 ) ([]flow.BlockEvents, []blockMetadata, error) { 228 missing := make([]blockMetadata, 0) 229 resp := make([]flow.BlockEvents, 0) 230 231 for _, blockInfo := range blockInfos { 232 if ctx.Err() != nil { 233 return nil, nil, rpc.ConvertError(ctx.Err(), "failed to get events from storage", codes.Canceled) 234 } 235 236 events, err := b.eventsIndex.ByBlockID(blockInfo.ID, blockInfo.Height) 237 if err != nil { 238 if errors.Is(err, storage.ErrNotFound) || 239 errors.Is(err, storage.ErrHeightNotIndexed) || 240 errors.Is(err, indexer.ErrIndexNotInitialized) { 241 missing = append(missing, blockInfo) 242 continue 243 } 244 err = fmt.Errorf("failed to get events for block %s: %w", blockInfo.ID, err) 245 return nil, nil, rpc.ConvertError(err, "failed to get events from storage", codes.Internal) 246 } 247 248 filteredEvents := make([]flow.Event, 0) 249 for _, e := range events { 250 if e.Type != eventType { 251 continue 252 } 253 254 // events are encoded in CCF format in storage. convert to JSON-CDC if requested 255 if requiredEventEncodingVersion == entities.EventEncodingVersion_JSON_CDC_V0 { 256 payload, err := convert.CcfPayloadToJsonPayload(e.Payload) 257 if err != nil { 258 err = fmt.Errorf("failed to convert event payload for block %s: %w", blockInfo.ID, err) 259 return nil, nil, rpc.ConvertError(err, "failed to convert event payload", codes.Internal) 260 } 261 e.Payload = payload 262 } 263 264 filteredEvents = append(filteredEvents, e) 265 } 266 267 resp = append(resp, flow.BlockEvents{ 268 BlockID: blockInfo.ID, 269 BlockHeight: blockInfo.Height, 270 BlockTimestamp: blockInfo.Timestamp, 271 Events: filteredEvents, 272 }) 273 } 274 275 return resp, missing, nil 276 } 277 278 // getBlockEventsFromExecutionNode retrieves events for all the specified blocks that have the given type 279 // from an execution node 280 func (b *backendEvents) getBlockEventsFromExecutionNode( 281 ctx context.Context, 282 blockInfos []blockMetadata, 283 eventType string, 284 requiredEventEncodingVersion entities.EventEncodingVersion, 285 ) ([]flow.BlockEvents, error) { 286 287 // create an execution API request for events at block ID 288 blockIDs := make([]flow.Identifier, len(blockInfos)) 289 for i := range blockInfos { 290 blockIDs[i] = blockInfos[i].ID 291 } 292 293 if len(blockIDs) == 0 { 294 return []flow.BlockEvents{}, nil 295 } 296 297 req := &execproto.GetEventsForBlockIDsRequest{ 298 Type: eventType, 299 BlockIds: convert.IdentifiersToMessages(blockIDs), 300 } 301 302 // choose the last block ID to find the list of execution nodes 303 lastBlockID := blockIDs[len(blockIDs)-1] 304 305 execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) 306 if err != nil { 307 return nil, rpc.ConvertError(err, "failed to retrieve events from execution node", codes.Internal) 308 } 309 310 var resp *execproto.GetEventsForBlockIDsResponse 311 var successfulNode *flow.Identity 312 resp, successfulNode, err = b.getEventsFromAnyExeNode(ctx, execNodes, req) 313 if err != nil { 314 return nil, rpc.ConvertError(err, "failed to retrieve events from execution nodes", codes.Internal) 315 } 316 b.log.Trace(). 317 Str("execution_id", successfulNode.String()). 318 Str("last_block_id", lastBlockID.String()). 319 Msg("successfully got events") 320 321 // convert execution node api result to access node api result 322 results, err := verifyAndConvertToAccessEvents( 323 resp.GetResults(), 324 blockInfos, 325 resp.GetEventEncodingVersion(), 326 requiredEventEncodingVersion, 327 ) 328 if err != nil { 329 return nil, status.Errorf(codes.Internal, "failed to verify retrieved events from execution node: %v", err) 330 } 331 332 return results, nil 333 } 334 335 // verifyAndConvertToAccessEvents converts execution node api result to access node api result, 336 // and verifies that the results contains results from each block that was requested 337 func verifyAndConvertToAccessEvents( 338 execEvents []*execproto.GetEventsForBlockIDsResponse_Result, 339 requestedBlockInfos []blockMetadata, 340 from entities.EventEncodingVersion, 341 to entities.EventEncodingVersion, 342 ) ([]flow.BlockEvents, error) { 343 if len(execEvents) != len(requestedBlockInfos) { 344 return nil, errors.New("number of results does not match number of blocks requested") 345 } 346 347 requestedBlockInfoSet := map[string]blockMetadata{} 348 for _, header := range requestedBlockInfos { 349 requestedBlockInfoSet[header.ID.String()] = header 350 } 351 352 results := make([]flow.BlockEvents, len(execEvents)) 353 354 for i, result := range execEvents { 355 blockInfo, expected := requestedBlockInfoSet[hex.EncodeToString(result.GetBlockId())] 356 if !expected { 357 return nil, fmt.Errorf("unexpected blockID from exe node %x", result.GetBlockId()) 358 } 359 if result.GetBlockHeight() != blockInfo.Height { 360 return nil, fmt.Errorf("unexpected block height %d for block %x from exe node", 361 result.GetBlockHeight(), 362 result.GetBlockId()) 363 } 364 365 events, err := convert.MessagesToEventsWithEncodingConversion(result.GetEvents(), from, to) 366 if err != nil { 367 return nil, fmt.Errorf("failed to unmarshal events in event %d with encoding version %s: %w", 368 i, to.String(), err) 369 } 370 371 results[i] = flow.BlockEvents{ 372 BlockID: blockInfo.ID, 373 BlockHeight: blockInfo.Height, 374 BlockTimestamp: blockInfo.Timestamp, 375 Events: events, 376 } 377 } 378 379 return results, nil 380 } 381 382 // getEventsFromAnyExeNode retrieves the given events from any EN in `execNodes`. 383 // We attempt querying each EN in sequence. If any EN returns a valid response, then errors from 384 // other ENs are logged and swallowed. If all ENs fail to return a valid response, then an 385 // error aggregating all failures is returned. 386 func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context, 387 execNodes flow.IdentityList, 388 req *execproto.GetEventsForBlockIDsRequest, 389 ) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) { 390 var resp *execproto.GetEventsForBlockIDsResponse 391 var execNode *flow.Identity 392 errToReturn := b.nodeCommunicator.CallAvailableNode( 393 execNodes, 394 func(node *flow.Identity) error { 395 var err error 396 start := time.Now() 397 resp, err = b.tryGetEvents(ctx, node, req) 398 duration := time.Since(start) 399 400 logger := b.log.With(). 401 Str("execution_node", node.String()). 402 Str("event", req.GetType()). 403 Int("blocks", len(req.BlockIds)). 404 Int64("rtt_ms", duration.Milliseconds()). 405 Logger() 406 407 if err == nil { 408 // return if any execution node replied successfully 409 logger.Debug().Msg("Successfully got events") 410 execNode = node 411 return nil 412 } 413 414 logger.Err(err).Msg("failed to execute GetEvents") 415 return err 416 }, 417 nil, 418 ) 419 420 return resp, execNode, errToReturn 421 } 422 423 func (b *backendEvents) tryGetEvents(ctx context.Context, 424 execNode *flow.Identity, 425 req *execproto.GetEventsForBlockIDsRequest, 426 ) (*execproto.GetEventsForBlockIDsResponse, error) { 427 execRPCClient, closer, err := b.connFactory.GetExecutionAPIClient(execNode.Address) 428 if err != nil { 429 return nil, err 430 } 431 defer closer.Close() 432 433 return execRPCClient.GetEventsForBlockIDs(ctx, req) 434 }