code.vegaprotocol.io/vega@v0.79.0/cmd/vega/commands/genesis/new_validator.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  	"errors"
    21  	"fmt"
    22  
    23  	"code.vegaprotocol.io/vega/core/nodewallets"
    24  	vgtm "code.vegaprotocol.io/vega/core/tendermint"
    25  	"code.vegaprotocol.io/vega/core/validators"
    26  	vgjson "code.vegaprotocol.io/vega/libs/json"
    27  	"code.vegaprotocol.io/vega/logging"
    28  	"code.vegaprotocol.io/vega/paths"
    29  
    30  	tmjson "github.com/cometbft/cometbft/libs/json"
    31  	tmtypes "github.com/cometbft/cometbft/types"
    32  	"github.com/jessevdk/go-flags"
    33  )
    34  
    35  var ErrAppendAndReplaceAreMutuallyExclusive = errors.New("--append and --replace and mutually exclusive")
    36  
    37  type newValidatorCmd struct {
    38  	Config nodewallets.Config
    39  
    40  	TmHome        string `description:"The home path of tendermint"                                                    long:"tm-home"    short:"t"`
    41  	Country       string `description:"The country from which the validator operates"                                  long:"country"    required:"true"`
    42  	InfoURL       string `description:"The URL from which people can get to know the validator"                        long:"info-url"   required:"true"`
    43  	Name          string `description:"The name of the validator node"                                                 long:"name"       required:"true"`
    44  	AvatarURL     string `description:"An URL to an avatar for the validator"                                          long:"avatar-url"`
    45  	ShouldAppend  bool   `description:"Append the generated validator to the existing validators in the genesis file"  long:"append"`
    46  	ShouldReplace bool   `description:"Replace the existing validators by the generated validator in the genesis file" long:"replace"`
    47  }
    48  
    49  func (opts *newValidatorCmd) Execute(_ []string) error {
    50  	if err := opts.Validate(); err != nil {
    51  		return err
    52  	}
    53  
    54  	log := logging.NewLoggerFromConfig(logging.NewDefaultConfig())
    55  	defer log.AtExit()
    56  
    57  	pass, err := genesisCmd.PassphraseFile.Get("node wallet", false)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	vegaPaths := paths.New(genesisCmd.VegaHome)
    63  
    64  	if _, err := flags.NewParser(opts, flags.Default|flags.IgnoreUnknown).Parse(); err != nil {
    65  		return err
    66  	}
    67  
    68  	vegaKey, ethAddress, walletID, err := loadNodeWalletPubKey(opts.Config, vegaPaths, pass)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	tmConfig := vgtm.NewConfig(opts.TmHome)
    74  
    75  	pubKey, err := tmConfig.PublicValidatorKey()
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	validatorDataDoc := tmtypes.GenesisValidator{
    81  		Address: pubKey.Address(),
    82  		PubKey:  pubKey,
    83  		Power:   10,
    84  		Name:    opts.Name,
    85  	}
    86  
    87  	b64TmPubKey := base64.StdEncoding.EncodeToString(pubKey.Bytes())
    88  	validatorDataState := validators.ValidatorData{
    89  		ID:              walletID,
    90  		VegaPubKey:      vegaKey.value,
    91  		VegaPubKeyIndex: vegaKey.index,
    92  		TmPubKey:        b64TmPubKey,
    93  		EthereumAddress: ethAddress,
    94  		Country:         opts.Country,
    95  		InfoURL:         opts.InfoURL,
    96  		Name:            opts.Name,
    97  		AvatarURL:       opts.AvatarURL,
    98  	}
    99  
   100  	if !opts.ShouldAppend && !opts.ShouldReplace {
   101  		marshalledGenesisDoc, err := tmjson.MarshalIndent(validatorDataDoc, "", "  ")
   102  		if err != nil {
   103  			return err
   104  		}
   105  		fmt.Println("Info to add in genesis file under `validators` key")
   106  		fmt.Println(string(marshalledGenesisDoc))
   107  
   108  		fmt.Println("Info to add in genesis file under `app_state.validators` key")
   109  		return vgjson.PrettyPrint(map[string]validators.ValidatorData{
   110  			b64TmPubKey: validatorDataState,
   111  		})
   112  	}
   113  
   114  	genesisDoc, genesisState, err := tmConfig.Genesis()
   115  	if err != nil {
   116  		return fmt.Errorf("couldn't get genesis file: %w", err)
   117  	}
   118  
   119  	if opts.ShouldAppend {
   120  		if _, ok := genesisState.Validators[b64TmPubKey]; ok {
   121  			return fmt.Errorf("validator with tendermint key \"%s\" is already registered under app_state.validators key", b64TmPubKey)
   122  		}
   123  		for _, validator := range genesisDoc.Validators {
   124  			if validator.PubKey.Equals(pubKey) {
   125  				return fmt.Errorf("validator with tendermint key \"%s\" is already registered under validators key", b64TmPubKey)
   126  			}
   127  		}
   128  		genesisDoc.Validators = append(genesisDoc.Validators, validatorDataDoc)
   129  		genesisState.Validators[b64TmPubKey] = validatorDataState
   130  	} else if opts.ShouldReplace {
   131  		genesisDoc.Validators = []tmtypes.GenesisValidator{validatorDataDoc}
   132  		genesisState.Validators = map[string]validators.ValidatorData{
   133  			b64TmPubKey: validatorDataState,
   134  		}
   135  	}
   136  
   137  	if err = vgtm.AddAppStateToGenesis(genesisDoc, genesisState); err != nil {
   138  		return fmt.Errorf("couldn't add app_state to genesis: %w", err)
   139  	}
   140  
   141  	if err := tmConfig.SaveGenesis(genesisDoc); err != nil {
   142  		return fmt.Errorf("couldn't save genesis: %w", err)
   143  	}
   144  
   145  	prettifiedDoc, err := vgtm.Prettify(genesisDoc)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	fmt.Println(prettifiedDoc)
   150  	return err
   151  }
   152  
   153  func (opts *newValidatorCmd) Validate() error {
   154  	if opts.ShouldAppend && opts.ShouldReplace {
   155  		return ErrAppendAndReplaceAreMutuallyExclusive
   156  	}
   157  	return nil
   158  }