github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/state_stream/backend/event_retriever.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/onflow/flow-go/engine/access/index"
    11  	"github.com/onflow/flow-go/model/flow"
    12  	"github.com/onflow/flow-go/storage"
    13  	"github.com/onflow/flow-go/utils/logging"
    14  )
    15  
    16  // EventsResponse represents the response containing events for a specific block.
    17  type EventsResponse struct {
    18  	BlockID        flow.Identifier
    19  	Height         uint64
    20  	Events         flow.EventsList
    21  	BlockTimestamp time.Time
    22  }
    23  
    24  // EventsRetriever retrieves events by block height. It can be configured to retrieve events from
    25  // the events indexer(if available) or using a dedicated callback to query it from other sources.
    26  type EventsRetriever struct {
    27  	log              zerolog.Logger
    28  	headers          storage.Headers
    29  	getExecutionData GetExecutionDataFunc
    30  	eventsIndex      *index.EventsIndex
    31  	useEventsIndex   bool
    32  }
    33  
    34  // GetAllEventsResponse returns a function that retrieves the event response for a given block height.
    35  // Expected errors:
    36  // - codes.NotFound: If block header for the specified block height is not found.
    37  // - error: An error, if any, encountered during getting events from storage or execution data.
    38  func (b *EventsRetriever) GetAllEventsResponse(ctx context.Context, height uint64) (*EventsResponse, error) {
    39  	var response *EventsResponse
    40  	var err error
    41  	if b.useEventsIndex {
    42  		response, err = b.getEventsFromStorage(height)
    43  	} else {
    44  		response, err = b.getEventsFromExecutionData(ctx, height)
    45  	}
    46  
    47  	if err == nil {
    48  		header, err := b.headers.ByHeight(height)
    49  		if err != nil {
    50  			return nil, fmt.Errorf("could not get header for height %d: %w", height, err)
    51  		}
    52  		response.BlockTimestamp = header.Timestamp
    53  
    54  		if b.log.GetLevel() == zerolog.TraceLevel {
    55  			b.log.Trace().
    56  				Hex("block_id", logging.ID(response.BlockID)).
    57  				Uint64("height", height).
    58  				Int("events", len(response.Events)).
    59  				Msg("sending events")
    60  		}
    61  	}
    62  
    63  	return response, err
    64  }
    65  
    66  // getEventsFromExecutionData returns the events for a given height extract from the execution data.
    67  // Expected errors:
    68  // - error: An error indicating issues with getting execution data for block
    69  func (b *EventsRetriever) getEventsFromExecutionData(ctx context.Context, height uint64) (*EventsResponse, error) {
    70  	executionData, err := b.getExecutionData(ctx, height)
    71  	if err != nil {
    72  		return nil, fmt.Errorf("could not get execution data for block %d: %w", height, err)
    73  	}
    74  
    75  	var events flow.EventsList
    76  	for _, chunkExecutionData := range executionData.ChunkExecutionDatas {
    77  		events = append(events, chunkExecutionData.Events...)
    78  	}
    79  
    80  	return &EventsResponse{
    81  		BlockID: executionData.BlockID,
    82  		Height:  height,
    83  		Events:  events,
    84  	}, nil
    85  }
    86  
    87  // getEventsFromStorage returns the events for a given height from the index storage.
    88  // Expected errors:
    89  // - error: An error indicating any issues with the provided block height or
    90  // an error indicating issue with getting events for a block.
    91  func (b *EventsRetriever) getEventsFromStorage(height uint64) (*EventsResponse, error) {
    92  	blockID, err := b.headers.BlockIDByHeight(height)
    93  	if err != nil {
    94  		return nil, fmt.Errorf("could not get header for height %d: %w", height, err)
    95  	}
    96  
    97  	events, err := b.eventsIndex.ByBlockID(blockID, height)
    98  	if err != nil {
    99  		return nil, fmt.Errorf("could not get events for block %d: %w", height, err)
   100  	}
   101  
   102  	return &EventsResponse{
   103  		BlockID: blockID,
   104  		Height:  height,
   105  		Events:  events,
   106  	}, nil
   107  }