github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/admin/commands/storage/read_seals.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 = (*ReadSealsCommand)(nil)
    15  
    16  type readSealsRequestType int
    17  
    18  const (
    19  	readSealsRequestByID readSealsRequestType = iota
    20  	readSealsRequestByBlock
    21  )
    22  
    23  type readSealsRequest struct {
    24  	requestType      readSealsRequestType
    25  	value            interface{}
    26  	numBlocksToQuery uint64
    27  }
    28  
    29  type sealInfo struct {
    30  	BlockID     flow.Identifier
    31  	BlockHeight uint64
    32  	SealID      flow.Identifier
    33  }
    34  
    35  type blockSeals struct {
    36  	BlockID     flow.Identifier
    37  	BlockHeight uint64
    38  	Seals       []*flow.Seal
    39  	LastSeal    *sealInfo
    40  }
    41  
    42  type ReadSealsCommand struct {
    43  	state protocol.State
    44  	seals storage.Seals
    45  	index storage.Index
    46  }
    47  
    48  func (r *ReadSealsCommand) Handler(ctx context.Context, req *admin.CommandRequest) (interface{}, error) {
    49  	data := req.ValidatorData.(*readSealsRequest)
    50  
    51  	if data.requestType == readSealsRequestByID {
    52  		if seal, err := r.seals.ByID(data.value.(flow.Identifier)); err != nil {
    53  			return nil, fmt.Errorf("failed to get seal by ID: %w", err)
    54  		} else {
    55  			return commands.ConvertToMap(seal)
    56  		}
    57  	}
    58  
    59  	var result []*blockSeals
    60  	var br *blocksRequest = data.value.(*blocksRequest)
    61  
    62  	for i := uint64(0); i < data.numBlocksToQuery; i++ {
    63  		header, err := getBlockHeader(r.state, br)
    64  		if err != nil {
    65  			return nil, fmt.Errorf("failed to get block header: %w", err)
    66  		}
    67  		blockID := header.ID()
    68  
    69  		index, err := r.index.ByBlockID(blockID)
    70  		if err != nil {
    71  			return nil, fmt.Errorf("failed to retrieve index for block %#v: %w", blockID, err)
    72  		}
    73  		lastSeal, err := r.seals.HighestInFork(blockID)
    74  		if err != nil {
    75  			return nil, fmt.Errorf("failed to retrieve last seal for block %#v: %w", blockID, err)
    76  		}
    77  		lastSealed, err := r.state.AtBlockID(lastSeal.BlockID).Head()
    78  		if err != nil {
    79  			return nil, fmt.Errorf("failed to retrieve header for last sealed block at block %#v: %w", blockID, err)
    80  		}
    81  		bs := &blockSeals{
    82  			BlockID:     blockID,
    83  			BlockHeight: header.Height,
    84  			LastSeal: &sealInfo{
    85  				BlockID:     lastSeal.BlockID,
    86  				BlockHeight: lastSealed.Height,
    87  				SealID:      lastSeal.ID(),
    88  			},
    89  		}
    90  
    91  		seals := make(map[uint64]*flow.Seal)
    92  		for _, sealID := range index.SealIDs {
    93  			seal, err := r.seals.ByID(sealID)
    94  			if err != nil {
    95  				return nil, fmt.Errorf("failed to retrieve seal with ID %#v: %w", sealID, err)
    96  			}
    97  			h, err := r.state.AtBlockID(seal.BlockID).Head()
    98  			if err != nil {
    99  				return nil, fmt.Errorf("failed to retrieve header for sealed block %#v: %w", seal.BlockID, err)
   100  			}
   101  			seals[h.Height] = seal
   102  		}
   103  
   104  		bs.Seals = make([]*flow.Seal, len(index.SealIDs))
   105  		for i := uint64(0); i < uint64(len(seals)); i++ {
   106  			bs.Seals[i] = seals[lastSealed.Height-i]
   107  		}
   108  
   109  		result = append(result, bs)
   110  
   111  		if header.Height == 0 {
   112  			break
   113  		}
   114  
   115  		br = &blocksRequest{
   116  			blocksRequestByID,
   117  			header.ParentID,
   118  		}
   119  	}
   120  
   121  	return commands.ConvertToInterfaceList(result)
   122  }
   123  
   124  // Validator validates the request.
   125  // Returns admin.InvalidAdminReqError for invalid/malformed requests.
   126  func (r *ReadSealsCommand) Validator(req *admin.CommandRequest) error {
   127  	input, ok := req.Data.(map[string]interface{})
   128  	if !ok {
   129  		return admin.NewInvalidAdminReqFormatError("expected map[string]any")
   130  	}
   131  
   132  	data := &readSealsRequest{}
   133  
   134  	if sealIn, ok := input["seal"]; ok {
   135  		errInvalidResultValue := admin.NewInvalidAdminReqParameterError("seal", "expected a seal ID represented as a 64 character long hex string, but got", sealIn)
   136  		seal, ok := sealIn.(string)
   137  		if !ok {
   138  			return errInvalidResultValue
   139  		}
   140  		sealID, err := flow.HexStringToIdentifier(seal)
   141  		if err != nil {
   142  			return errInvalidResultValue
   143  		}
   144  		data.requestType = readSealsRequestByID
   145  		data.value = sealID
   146  	} else if block, ok := input["block"]; ok {
   147  		br, err := parseBlocksRequest(block)
   148  		if err != nil {
   149  			return admin.NewInvalidAdminReqErrorf("invalid 'block' field: %w", err)
   150  		}
   151  		data.requestType = readSealsRequestByBlock
   152  		data.value = br
   153  		if n, ok := input["n"]; ok {
   154  			if n, err := parseN(n); err != nil {
   155  				return admin.NewInvalidAdminReqErrorf("invalid 'n' field: %w", err)
   156  			} else {
   157  				data.numBlocksToQuery = n
   158  			}
   159  		} else {
   160  			data.numBlocksToQuery = 1
   161  		}
   162  	} else {
   163  		return admin.NewInvalidAdminReqErrorf("either \"block\" or \"seal\" field is required")
   164  	}
   165  
   166  	req.ValidatorData = data
   167  
   168  	return nil
   169  }
   170  
   171  func NewReadSealsCommand(state protocol.State, storage storage.Seals, index storage.Index) commands.AdminCommand {
   172  	return &ReadSealsCommand{
   173  		state,
   174  		storage,
   175  		index,
   176  	}
   177  }