code.vegaprotocol.io/vega@v0.79.0/cmd/vega/commands/genesis/load_checkpoint.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package genesis
    17  
    18  import (
    19  	"encoding/base64"
    20  	"encoding/hex"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	"code.vegaprotocol.io/vega/core/genesis"
    29  	vgtm "code.vegaprotocol.io/vega/core/tendermint"
    30  	"code.vegaprotocol.io/vega/core/types"
    31  	vgfs "code.vegaprotocol.io/vega/libs/fs"
    32  	"code.vegaprotocol.io/vega/logging"
    33  
    34  	tmjson "github.com/cometbft/cometbft/libs/json"
    35  	tmtypes "github.com/cometbft/cometbft/types"
    36  	"github.com/jessevdk/go-flags"
    37  )
    38  
    39  type loadCheckpointCmd struct {
    40  	DryRun         bool   `description:"Display the genesis file without writing it" long:"dry-run"`
    41  	TmHome         string `description:"The home path of tendermint"                 long:"tm-home"         short:"t"`
    42  	GenesisFile    string `description:"A genesis file to be updated"                long:"genesis-file"    short:"g"`
    43  	CheckpointPath string `description:"The path to the checkpoint file to load"     long:"checkpoint-path" required:"true"`
    44  }
    45  
    46  func (opts *loadCheckpointCmd) Execute(_ []string) error {
    47  	log := logging.NewLoggerFromConfig(
    48  		logging.NewDefaultConfig(),
    49  	)
    50  	defer log.AtExit()
    51  
    52  	if _, err := flags.NewParser(opts, flags.Default|flags.IgnoreUnknown).Parse(); err != nil {
    53  		return err
    54  	}
    55  
    56  	var (
    57  		genesisDoc *tmtypes.GenesisDoc
    58  		err        error
    59  		tmConfig   *vgtm.Config
    60  	)
    61  
    62  	if len(opts.GenesisFile) > 0 {
    63  		genesisDoc, _, err = readGenesisFile(opts.GenesisFile)
    64  	} else {
    65  		tmConfig = vgtm.NewConfig(opts.TmHome)
    66  		genesisDoc, _, err = tmConfig.Genesis()
    67  	}
    68  	if err != nil {
    69  		return fmt.Errorf("couldn't get genesis file: %w", err)
    70  	}
    71  
    72  	appState := genesis.State{}
    73  	err = json.Unmarshal(genesisDoc.AppState, &appState)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	f, err := os.Open(opts.CheckpointPath)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	splits := strings.Split(filepath.Base(f.Name()), "-")
    84  	if len(splits) != 3 {
    85  		return fmt.Errorf("invalid checkpoint file name: `%v`", f.Name())
    86  	}
    87  
    88  	expectHash := strings.TrimSuffix(splits[2], ".cp")
    89  	f.Close()
    90  
    91  	buf, err := ioutil.ReadFile(opts.CheckpointPath)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	cpt := &types.CheckpointState{}
    97  	if err := cpt.SetState(buf); err != nil {
    98  		return fmt.Errorf("invalid restore checkpoint command: %w", err)
    99  	}
   100  
   101  	if hex.EncodeToString(cpt.Hash) != expectHash {
   102  		return fmt.Errorf("invalid hash, file name have hash `%s` but content hash is `%s`", expectHash, hex.EncodeToString(cpt.Hash))
   103  	}
   104  
   105  	appState.Checkpoint.CheckpointHash = expectHash
   106  	appState.Checkpoint.CheckpointState = base64.StdEncoding.EncodeToString(buf)
   107  
   108  	if err := vgtm.AddAppStateToGenesis(genesisDoc, &appState); err != nil {
   109  		return fmt.Errorf("couldn't add app_state to genesis: %w", err)
   110  	}
   111  
   112  	if !opts.DryRun {
   113  		if len(opts.GenesisFile) > 0 {
   114  			if err := genesisDoc.SaveAs(opts.GenesisFile); err != nil {
   115  				return fmt.Errorf("couldn't save the genesis file: %w", err)
   116  			}
   117  		} else {
   118  			if err := tmConfig.SaveGenesis(genesisDoc); err != nil {
   119  				return fmt.Errorf("couldn't save genesis: %w", err)
   120  			}
   121  		}
   122  	}
   123  
   124  	prettifiedDoc, err := vgtm.Prettify(genesisDoc)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	fmt.Println(prettifiedDoc)
   129  	return nil
   130  }
   131  
   132  func readGenesisFile(path string) (*tmtypes.GenesisDoc, *genesis.State, error) {
   133  	data, err := vgfs.ReadFile(path)
   134  	if err != nil {
   135  		return nil, nil, fmt.Errorf("couldn't read genesis file: %w", err)
   136  	}
   137  
   138  	doc := &tmtypes.GenesisDoc{}
   139  	err = tmjson.Unmarshal(data, doc)
   140  	if err != nil {
   141  		return nil, nil, fmt.Errorf("couldn't unmarshal the genesis document: %w", err)
   142  	}
   143  
   144  	state := &genesis.State{}
   145  
   146  	if len(doc.AppState) != 0 {
   147  		if err := json.Unmarshal(doc.AppState, state); err != nil {
   148  			return nil, nil, fmt.Errorf("couldn't unmarshal genesis state: %w", err)
   149  		}
   150  	}
   151  
   152  	return doc, state, nil
   153  }