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  }