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