github.com/onflow/flow-go@v0.33.17/admin/commands/common/read_protocol_state_blocks.go (about) 1 package common 2 3 import ( 4 "context" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "math" 9 "strings" 10 11 "github.com/onflow/flow-go/admin" 12 "github.com/onflow/flow-go/admin/commands" 13 storageCommands "github.com/onflow/flow-go/admin/commands/storage" 14 "github.com/onflow/flow-go/model/flow" 15 "github.com/onflow/flow-go/state/protocol" 16 "github.com/onflow/flow-go/storage" 17 ) 18 19 var _ commands.AdminCommand = (*ReadProtocolStateBlocksCommand)(nil) 20 21 type requestType int 22 23 const ( 24 ID requestType = iota 25 Height 26 Final 27 Sealed 28 ) 29 30 type requestData struct { 31 requestType requestType 32 blockID flow.Identifier 33 blockHeight uint64 34 numBlocksToQuery uint 35 } 36 37 type ReadProtocolStateBlocksCommand struct { 38 state protocol.State 39 blocks storage.Blocks 40 } 41 42 func (r *ReadProtocolStateBlocksCommand) getBlockByHeight(height uint64) (*flow.Block, error) { 43 header, err := r.state.AtHeight(height).Head() 44 if err != nil { 45 return nil, fmt.Errorf("could not get header by height: %v, %w", height, err) 46 } 47 48 block, err := r.getBlockByHeader(header) 49 if err != nil { 50 return nil, fmt.Errorf("could not get block by header: %w", err) 51 } 52 return block, nil 53 } 54 55 func (r *ReadProtocolStateBlocksCommand) getFinal() (*flow.Block, error) { 56 header, err := r.state.Final().Head() 57 if err != nil { 58 return nil, fmt.Errorf("could not get finalized, %w", err) 59 } 60 61 block, err := r.getBlockByHeader(header) 62 if err != nil { 63 return nil, fmt.Errorf("could not get block by header: %w", err) 64 } 65 return block, nil 66 } 67 68 func (r *ReadProtocolStateBlocksCommand) getSealed() (*flow.Block, error) { 69 header, err := r.state.Sealed().Head() 70 if err != nil { 71 return nil, fmt.Errorf("could not get sealed block, %w", err) 72 } 73 74 block, err := r.getBlockByHeader(header) 75 if err != nil { 76 return nil, fmt.Errorf("could not get block by header: %w", err) 77 } 78 return block, nil 79 } 80 81 func (r *ReadProtocolStateBlocksCommand) getBlockByID(blockID flow.Identifier) (*flow.Block, error) { 82 header, err := r.state.AtBlockID(blockID).Head() 83 if err != nil { 84 return nil, fmt.Errorf("could not get header by blockID: %v, %w", blockID, err) 85 } 86 87 block, err := r.getBlockByHeader(header) 88 if err != nil { 89 return nil, fmt.Errorf("could not get block by header: %w", err) 90 } 91 return block, nil 92 } 93 94 func (r *ReadProtocolStateBlocksCommand) getBlockByHeader(header *flow.Header) (*flow.Block, error) { 95 blockID := header.ID() 96 block, err := r.blocks.ByID(blockID) 97 if err != nil { 98 return nil, fmt.Errorf("could not get block by ID %v: %w", blockID, err) 99 } 100 return block, nil 101 } 102 103 func (r *ReadProtocolStateBlocksCommand) Handler(_ context.Context, req *admin.CommandRequest) (interface{}, error) { 104 data := req.ValidatorData.(*requestData) 105 var result []*flow.Block 106 var block *flow.Block 107 var err error 108 109 switch data.requestType { 110 case ID: 111 block, err = r.getBlockByID(data.blockID) 112 case Height: 113 block, err = r.getBlockByHeight(data.blockHeight) 114 case Final: 115 block, err = r.getFinal() 116 case Sealed: 117 block, err = r.getSealed() 118 } 119 120 if err != nil { 121 return nil, err 122 } 123 124 result = append(result, block) 125 firstHeight := int64(block.Header.Height) 126 127 for height := firstHeight - 1; height >= 0 && height > firstHeight-int64(data.numBlocksToQuery); height-- { 128 block, err = r.getBlockByHeight(uint64(height)) 129 if err != nil { 130 return nil, err 131 } 132 result = append(result, block) 133 } 134 135 var resultList []interface{} 136 bytes, err := json.Marshal(result) 137 if err != nil { 138 return nil, err 139 } 140 err = json.Unmarshal(bytes, &resultList) 141 142 return resultList, err 143 } 144 145 // Validator validates the request. 146 // Returns admin.InvalidAdminReqError for invalid/malformed requests. 147 func (r *ReadProtocolStateBlocksCommand) Validator(req *admin.CommandRequest) error { 148 input, ok := req.Data.(map[string]interface{}) 149 if !ok { 150 return admin.NewInvalidAdminReqFormatError("expected map[string]any") 151 } 152 153 inBlock, ok := input["block"] 154 if !ok { 155 return admin.NewInvalidAdminReqErrorf("missing 'block' field") 156 } 157 158 data := &requestData{} 159 160 switch block := inBlock.(type) { 161 case string: 162 block = strings.ToLower(strings.TrimSpace(block)) 163 if block == storageCommands.FINAL { 164 data.requestType = Final 165 } else if block == storageCommands.SEALED { 166 data.requestType = Sealed 167 } else if len(block) == 2*flow.IdentifierLen { 168 b, err := hex.DecodeString(block) 169 if err != nil { 170 return admin.NewInvalidAdminReqParameterError("block", "block ID must be 64-char hex string", inBlock) 171 } 172 data.requestType = ID 173 data.blockID = flow.HashToID(b) 174 } else { 175 return admin.NewInvalidAdminReqParameterError("block", "must be 'final', 'sealed', or block ID hex", inBlock) 176 } 177 case float64: 178 if block < 0 || math.Trunc(block) != block { 179 return admin.NewInvalidAdminReqParameterError("block", "block height must be >=0 and integral", inBlock) 180 } 181 data.requestType = Height 182 data.blockHeight = uint64(block) 183 default: 184 return admin.NewInvalidAdminReqParameterError("block", "must be string or number", inBlock) 185 } 186 187 if inN, ok := input["n"]; ok { 188 n, ok := inN.(float64) 189 if !ok { 190 return admin.NewInvalidAdminReqParameterError("n", "must be number", inN) 191 } 192 if math.Trunc(n) != n { 193 return admin.NewInvalidAdminReqParameterError("n", "must be integral", inN) 194 } 195 if n < 1 { 196 return admin.NewInvalidAdminReqParameterError("n", "must be >=1", inN) 197 } 198 data.numBlocksToQuery = uint(n) 199 } else { 200 data.numBlocksToQuery = 1 201 } 202 203 req.ValidatorData = data 204 205 return nil 206 207 } 208 209 func NewReadProtocolStateBlocksCommand(state protocol.State, storage storage.Blocks) commands.AdminCommand { 210 return &ReadProtocolStateBlocksCommand{ 211 state, 212 storage, 213 } 214 }