github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/backend/backend_events.go (about)

     1  package backend
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/hashicorp/go-multierror"
    10  	execproto "github.com/onflow/flow/protobuf/go/flow/execution"
    11  	"github.com/rs/zerolog"
    12  	"google.golang.org/grpc/codes"
    13  	"google.golang.org/grpc/status"
    14  
    15  	"github.com/koko1123/flow-go-1/engine/common/rpc"
    16  	"github.com/koko1123/flow-go-1/engine/common/rpc/convert"
    17  	"github.com/koko1123/flow-go-1/model/flow"
    18  	"github.com/koko1123/flow-go-1/state/protocol"
    19  	"github.com/koko1123/flow-go-1/storage"
    20  )
    21  
    22  type backendEvents struct {
    23  	headers           storage.Headers
    24  	executionReceipts storage.ExecutionReceipts
    25  	state             protocol.State
    26  	connFactory       ConnectionFactory
    27  	log               zerolog.Logger
    28  	maxHeightRange    uint
    29  }
    30  
    31  // GetEventsForHeightRange retrieves events for all sealed blocks between the start block height and
    32  // the end block height (inclusive) that have the given type.
    33  func (b *backendEvents) GetEventsForHeightRange(
    34  	ctx context.Context,
    35  	eventType string,
    36  	startHeight, endHeight uint64,
    37  ) ([]flow.BlockEvents, error) {
    38  
    39  	if endHeight < startHeight {
    40  		return nil, status.Error(codes.InvalidArgument, "invalid start or end height")
    41  	}
    42  
    43  	rangeSize := endHeight - startHeight + 1 // range is inclusive on both ends
    44  	if rangeSize > uint64(b.maxHeightRange) {
    45  		return nil, status.Errorf(codes.InvalidArgument, "requested block range (%d) exceeded maximum (%d)", rangeSize, b.maxHeightRange)
    46  	}
    47  
    48  	// get the latest sealed block header
    49  	head, err := b.state.Sealed().Head()
    50  	if err != nil {
    51  		// sealed block must be in the store, so return an Internal code even if we got NotFound
    52  		return nil, status.Errorf(codes.Internal, "failed to get events: %v", err)
    53  	}
    54  
    55  	// start height should not be beyond the last sealed height
    56  	if head.Height < startHeight {
    57  		return nil, status.Errorf(codes.OutOfRange,
    58  			"start height %d is greater than the last sealed block height %d", startHeight, head.Height)
    59  	}
    60  
    61  	// limit max height to last sealed block in the chain
    62  	if head.Height < endHeight {
    63  		endHeight = head.Height
    64  	}
    65  
    66  	// find the block headers for all the blocks between min and max height (inclusive)
    67  	blockHeaders := make([]*flow.Header, 0)
    68  
    69  	for i := startHeight; i <= endHeight; i++ {
    70  		header, err := b.headers.ByHeight(i)
    71  		if err != nil {
    72  			return nil, rpc.ConvertStorageError(fmt.Errorf("failed to get events: %w", err))
    73  		}
    74  
    75  		blockHeaders = append(blockHeaders, header)
    76  	}
    77  
    78  	return b.getBlockEventsFromExecutionNode(ctx, blockHeaders, eventType)
    79  }
    80  
    81  // GetEventsForBlockIDs retrieves events for all the specified block IDs that have the given type
    82  func (b *backendEvents) GetEventsForBlockIDs(
    83  	ctx context.Context,
    84  	eventType string,
    85  	blockIDs []flow.Identifier,
    86  ) ([]flow.BlockEvents, error) {
    87  
    88  	if uint(len(blockIDs)) > b.maxHeightRange {
    89  		return nil, status.Errorf(codes.InvalidArgument, "requested block range (%d) exceeded maximum (%d)", len(blockIDs), b.maxHeightRange)
    90  	}
    91  
    92  	// find the block headers for all the block IDs
    93  	blockHeaders := make([]*flow.Header, 0)
    94  	for _, blockID := range blockIDs {
    95  		header, err := b.headers.ByBlockID(blockID)
    96  		if err != nil {
    97  			return nil, rpc.ConvertStorageError(fmt.Errorf("failed to get events: %w", err))
    98  		}
    99  
   100  		blockHeaders = append(blockHeaders, header)
   101  	}
   102  
   103  	// forward the request to the execution node
   104  	return b.getBlockEventsFromExecutionNode(ctx, blockHeaders, eventType)
   105  }
   106  
   107  func (b *backendEvents) getBlockEventsFromExecutionNode(
   108  	ctx context.Context,
   109  	blockHeaders []*flow.Header,
   110  	eventType string,
   111  ) ([]flow.BlockEvents, error) {
   112  
   113  	// create an execution API request for events at block ID
   114  	blockIDs := make([]flow.Identifier, len(blockHeaders))
   115  	for i := range blockIDs {
   116  		blockIDs[i] = blockHeaders[i].ID()
   117  	}
   118  
   119  	if len(blockIDs) == 0 {
   120  		return []flow.BlockEvents{}, nil
   121  	}
   122  
   123  	req := &execproto.GetEventsForBlockIDsRequest{
   124  		Type:     eventType,
   125  		BlockIds: convert.IdentifiersToMessages(blockIDs),
   126  	}
   127  
   128  	// choose the last block ID to find the list of execution nodes
   129  	lastBlockID := blockIDs[len(blockIDs)-1]
   130  
   131  	execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log)
   132  	if err != nil {
   133  		b.log.Error().Err(err).Msg("failed to retrieve events from execution node")
   134  		return nil, status.Errorf(codes.Internal, "failed to retrieve events from execution node: %v", err)
   135  	}
   136  
   137  	var resp *execproto.GetEventsForBlockIDsResponse
   138  	var successfulNode *flow.Identity
   139  	resp, successfulNode, err = b.getEventsFromAnyExeNode(ctx, execNodes, req)
   140  	if err != nil {
   141  		b.log.Error().Err(err).Msg("failed to retrieve events from execution nodes")
   142  		return nil, status.Errorf(codes.Internal, "failed to retrieve events from execution nodes %s: %v", execNodes, err)
   143  	}
   144  	b.log.Trace().
   145  		Str("execution_id", successfulNode.String()).
   146  		Str("last_block_id", lastBlockID.String()).
   147  		Msg("successfully got events")
   148  
   149  	// convert execution node api result to access node api result
   150  	results, err := verifyAndConvertToAccessEvents(resp.GetResults(), blockHeaders)
   151  	if err != nil {
   152  		return nil, status.Errorf(codes.Internal, "failed to verify retrieved events from execution node: %v", err)
   153  	}
   154  
   155  	return results, nil
   156  }
   157  
   158  // verifyAndConvertToAccessEvents converts execution node api result to access node api result, and verifies that the results contains
   159  // results from each block that was requested
   160  func verifyAndConvertToAccessEvents(execEvents []*execproto.GetEventsForBlockIDsResponse_Result, requestedBlockHeaders []*flow.Header) ([]flow.BlockEvents, error) {
   161  	if len(execEvents) != len(requestedBlockHeaders) {
   162  		return nil, errors.New("number of results does not match number of blocks requested")
   163  	}
   164  
   165  	requestedBlockHeaderSet := map[string]*flow.Header{}
   166  	for _, header := range requestedBlockHeaders {
   167  		requestedBlockHeaderSet[header.ID().String()] = header
   168  	}
   169  
   170  	results := make([]flow.BlockEvents, len(execEvents))
   171  
   172  	for i, result := range execEvents {
   173  		header, expected := requestedBlockHeaderSet[hex.EncodeToString(result.GetBlockId())]
   174  		if !expected {
   175  			return nil, fmt.Errorf("unexpected blockID from exe node %x", result.GetBlockId())
   176  		}
   177  		if result.GetBlockHeight() != header.Height {
   178  			return nil, fmt.Errorf("unexpected block height %d for block %x from exe node",
   179  				result.GetBlockHeight(),
   180  				result.GetBlockId())
   181  		}
   182  
   183  		results[i] = flow.BlockEvents{
   184  			BlockID:        header.ID(),
   185  			BlockHeight:    header.Height,
   186  			BlockTimestamp: header.Timestamp,
   187  			Events:         convert.MessagesToEvents(result.GetEvents()),
   188  		}
   189  	}
   190  
   191  	return results, nil
   192  }
   193  
   194  func (b *backendEvents) getEventsFromAnyExeNode(ctx context.Context,
   195  	execNodes flow.IdentityList,
   196  	req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, *flow.Identity, error) {
   197  	var errors *multierror.Error
   198  	// try to get events from one of the execution nodes
   199  	for _, execNode := range execNodes {
   200  		resp, err := b.tryGetEvents(ctx, execNode, req)
   201  		if err == nil {
   202  			return resp, execNode, nil
   203  		}
   204  		errors = multierror.Append(errors, err)
   205  	}
   206  	return nil, nil, errors.ErrorOrNil()
   207  }
   208  
   209  func (b *backendEvents) tryGetEvents(ctx context.Context,
   210  	execNode *flow.Identity,
   211  	req *execproto.GetEventsForBlockIDsRequest) (*execproto.GetEventsForBlockIDsResponse, error) {
   212  	execRPCClient, closer, err := b.connFactory.GetExecutionAPIClient(execNode.Address)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	defer closer.Close()
   217  
   218  	resp, err := execRPCClient.GetEventsForBlockIDs(ctx, req)
   219  	if err != nil {
   220  		if status.Code(err) == codes.Unavailable {
   221  			b.connFactory.InvalidateExecutionAPIClient(execNode.Address)
   222  		}
   223  		return nil, err
   224  	}
   225  	return resp, nil
   226  }