github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/admin/commands/storage/read_results.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/onflow/flow-go/admin"
     8  	"github.com/onflow/flow-go/admin/commands"
     9  	"github.com/onflow/flow-go/model/flow"
    10  	"github.com/onflow/flow-go/state/protocol"
    11  	"github.com/onflow/flow-go/storage"
    12  )
    13  
    14  var _ commands.AdminCommand = (*ReadResultsCommand)(nil)
    15  
    16  type readResultsRequestType int
    17  
    18  const (
    19  	readResultsRequestByID readResultsRequestType = iota
    20  	readResultsRequestByBlock
    21  )
    22  
    23  type readResultsRequest struct {
    24  	requestType       readResultsRequestType
    25  	value             interface{}
    26  	numResultsToQuery uint64
    27  }
    28  
    29  type ReadResultsCommand struct {
    30  	state   protocol.State
    31  	results storage.ExecutionResults
    32  }
    33  
    34  func (r *ReadResultsCommand) Handler(ctx context.Context, req *admin.CommandRequest) (interface{}, error) {
    35  	data := req.ValidatorData.(*readResultsRequest)
    36  	var results []*flow.ExecutionResult
    37  	var resultID flow.Identifier
    38  	n := data.numResultsToQuery
    39  
    40  	switch data.requestType {
    41  	case readResultsRequestByID:
    42  		resultID = data.value.(flow.Identifier)
    43  	case readResultsRequestByBlock:
    44  		if header, err := getBlockHeader(r.state, data.value.(*blocksRequest)); err != nil {
    45  			return nil, fmt.Errorf("failed to get block header: %w", err)
    46  		} else if result, err := r.results.ByBlockID(header.ID()); err != nil {
    47  			return nil, fmt.Errorf("failed to get result by block ID: %w", err)
    48  		} else {
    49  			results = append(results, result)
    50  			resultID = result.PreviousResultID
    51  			n -= 1
    52  		}
    53  	}
    54  
    55  	for i := uint64(0); i < n && resultID != flow.ZeroID; i++ {
    56  		result, err := r.results.ByID(resultID)
    57  		if err != nil {
    58  			return nil, fmt.Errorf("failed to get result by ID: %w", err)
    59  		}
    60  		results = append(results, result)
    61  		resultID = result.PreviousResultID
    62  	}
    63  
    64  	return commands.ConvertToInterfaceList(results)
    65  }
    66  
    67  // Validator validates the request.
    68  // Returns admin.InvalidAdminReqError for invalid/malformed requests.
    69  func (r *ReadResultsCommand) Validator(req *admin.CommandRequest) error {
    70  	input, ok := req.Data.(map[string]interface{})
    71  	if !ok {
    72  		return admin.NewInvalidAdminReqFormatError("expected map[string]any")
    73  	}
    74  
    75  	data := &readResultsRequest{}
    76  
    77  	if resultIn, ok := input["result"]; ok {
    78  		errInvalidResultValue := admin.NewInvalidAdminReqParameterError("result", "expected a result ID represented as a 64 character long hex string", resultIn)
    79  		result, ok := resultIn.(string)
    80  		if !ok {
    81  			return errInvalidResultValue
    82  		}
    83  		resultID, err := flow.HexStringToIdentifier(result)
    84  		if err != nil {
    85  			return errInvalidResultValue
    86  		}
    87  		data.requestType = readResultsRequestByID
    88  		data.value = resultID
    89  	} else if block, ok := input["block"]; ok {
    90  		br, err := parseBlocksRequest(block)
    91  		if err != nil {
    92  			return admin.NewInvalidAdminReqErrorf("invalid 'block' field: %w", err)
    93  		}
    94  		data.requestType = readResultsRequestByBlock
    95  		data.value = br
    96  	} else {
    97  		return admin.NewInvalidAdminReqErrorf("either \"block\" or \"result\" field is required")
    98  	}
    99  
   100  	if n, ok := input["n"]; ok {
   101  		if n, err := parseN(n); err != nil {
   102  			return admin.NewInvalidAdminReqErrorf("invalid 'n' field: %w", err)
   103  		} else {
   104  			data.numResultsToQuery = n
   105  		}
   106  	} else {
   107  		data.numResultsToQuery = 1
   108  	}
   109  
   110  	req.ValidatorData = data
   111  
   112  	return nil
   113  }
   114  
   115  func NewReadResultsCommand(state protocol.State, storage storage.ExecutionResults) commands.AdminCommand {
   116  	return &ReadResultsCommand{
   117  		state,
   118  		storage,
   119  	}
   120  }