github.com/onflow/flow-go@v0.33.17/admin/commands/storage/read_protocol_snapshot.go (about) 1 package storage 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/rs/zerolog" 8 9 "github.com/onflow/flow-go/admin" 10 "github.com/onflow/flow-go/admin/commands" 11 "github.com/onflow/flow-go/cmd/util/common" 12 "github.com/onflow/flow-go/state/protocol" 13 "github.com/onflow/flow-go/state/protocol/inmem" 14 "github.com/onflow/flow-go/storage" 15 "github.com/onflow/flow-go/utils/logging" 16 ) 17 18 var _ commands.AdminCommand = (*ProtocolSnapshotCommand)(nil) 19 20 type protocolSnapshotData struct { 21 blocksToSkip uint 22 } 23 24 // ProtocolSnapshotCommand is a command that generates a protocol snapshot for a checkpoint (usually latest checkpoint) 25 // This command is only available for execution node 26 type ProtocolSnapshotCommand struct { 27 logger zerolog.Logger 28 state protocol.State 29 headers storage.Headers 30 seals storage.Seals 31 checkpointDir string // the directory where the checkpoint is stored 32 } 33 34 func NewProtocolSnapshotCommand( 35 logger zerolog.Logger, 36 state protocol.State, 37 headers storage.Headers, 38 seals storage.Seals, 39 checkpointDir string, 40 ) *ProtocolSnapshotCommand { 41 return &ProtocolSnapshotCommand{ 42 logger: logger, 43 state: state, 44 headers: headers, 45 seals: seals, 46 checkpointDir: checkpointDir, 47 } 48 } 49 50 func (s *ProtocolSnapshotCommand) Handler(_ context.Context, req *admin.CommandRequest) (interface{}, error) { 51 validated, ok := req.ValidatorData.(*protocolSnapshotData) 52 if !ok { 53 return nil, fmt.Errorf("fail to parse validator data") 54 } 55 56 blocksToSkip := validated.blocksToSkip 57 58 s.logger.Info().Uint("blocksToSkip", blocksToSkip).Msgf("admintool: generating protocol snapshot") 59 60 snapshot, sealedHeight, commit, checkpointFile, err := common.GenerateProtocolSnapshotForCheckpoint( 61 s.logger, s.state, s.headers, s.seals, s.checkpointDir, blocksToSkip) 62 if err != nil { 63 return nil, fmt.Errorf("could not generate protocol snapshot for checkpoint, checkpointDir %v: %w", 64 s.checkpointDir, err) 65 } 66 67 header, err := snapshot.Head() 68 if err != nil { 69 return nil, fmt.Errorf("could not get header from snapshot: %w", err) 70 } 71 72 serializable, err := inmem.FromSnapshot(snapshot) 73 if err != nil { 74 return nil, fmt.Errorf("could not convert snapshot to serializable: %w", err) 75 } 76 77 s.logger.Info(). 78 Uint64("finalized_height", header.Height). // finalized height 79 Hex("finalized_block_id", logging.Entity(header)). 80 Uint64("sealed_height", sealedHeight). 81 Hex("sealed_commit", commit[:]). // not the commit for the finalized height, but for the sealed height 82 Str("checkpoint_file", checkpointFile). 83 Uint("blocks_to_skip", blocksToSkip). 84 Msgf("admintool: protocol snapshot generated successfully") 85 86 return commands.ConvertToMap(protocolSnapshotResponse{ 87 Snapshot: serializable.Encodable(), 88 Checkpoint: checkpointFile, 89 }) 90 } 91 92 type protocolSnapshotResponse struct { 93 Snapshot inmem.EncodableSnapshot `json:"snapshot"` 94 Checkpoint string `json:"checkpoint"` 95 } 96 97 func (s *ProtocolSnapshotCommand) Validator(req *admin.CommandRequest) error { 98 // blocksToSkip is the number of blocks to skip when iterating the sealed heights to find the state commitment 99 // in the checkpoint file. 100 // default is 0 101 validated := &protocolSnapshotData{ 102 blocksToSkip: uint(0), 103 } 104 105 input, ok := req.Data.(map[string]interface{}) 106 if ok { 107 data, ok := input["blocks-to-skip"] 108 109 if ok { 110 n, ok := data.(float64) 111 if !ok { 112 return fmt.Errorf("could not parse blocks-to-skip: %v", data) 113 } 114 validated.blocksToSkip = uint(n) 115 } 116 } 117 118 req.ValidatorData = validated 119 120 return nil 121 }