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  }