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 }