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 }