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  }