github.com/klaytn/klaytn@v1.10.2/cmd/utils/nodecmd/chaincmd.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of go-ethereum.
     4  //
     5  // go-ethereum is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // go-ethereum is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from cmd/geth/chaincmd.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package nodecmd
    22  
    23  import (
    24  	"encoding/json"
    25  	"errors"
    26  	"os"
    27  	"strings"
    28  
    29  	"github.com/klaytn/klaytn/blockchain"
    30  	"github.com/klaytn/klaytn/blockchain/types"
    31  	"github.com/klaytn/klaytn/cmd/utils"
    32  	"github.com/klaytn/klaytn/governance"
    33  	"github.com/klaytn/klaytn/log"
    34  	"github.com/klaytn/klaytn/params"
    35  	"github.com/klaytn/klaytn/rlp"
    36  	"github.com/klaytn/klaytn/storage/database"
    37  	"gopkg.in/urfave/cli.v1"
    38  )
    39  
    40  var logger = log.NewModuleLogger(log.CMDUtilsNodeCMD)
    41  
    42  var (
    43  	InitCommand = cli.Command{
    44  		Action:    utils.MigrateFlags(initGenesis),
    45  		Name:      "init",
    46  		Usage:     "Bootstrap and initialize a new genesis block",
    47  		ArgsUsage: "<genesisPath>",
    48  		Flags: []cli.Flag{
    49  			utils.DbTypeFlag,
    50  			utils.SingleDBFlag,
    51  			utils.NumStateTrieShardsFlag,
    52  			utils.DynamoDBTableNameFlag,
    53  			utils.DynamoDBRegionFlag,
    54  			utils.DynamoDBIsProvisionedFlag,
    55  			utils.DynamoDBReadCapacityFlag,
    56  			utils.DynamoDBWriteCapacityFlag,
    57  			utils.DynamoDBReadOnlyFlag,
    58  			utils.LevelDBCompressionTypeFlag,
    59  			utils.DataDirFlag,
    60  			utils.OverwriteGenesisFlag,
    61  		},
    62  		Category: "BLOCKCHAIN COMMANDS",
    63  		Description: `
    64  The init command initializes a new genesis block and definition for the network.
    65  This is a destructive action and changes the network in which you will be
    66  participating.
    67  
    68  It expects the genesis file as argument.`,
    69  	}
    70  
    71  	DumpGenesisCommand = cli.Command{
    72  		Action:    utils.MigrateFlags(dumpGenesis),
    73  		Name:      "dumpgenesis",
    74  		Usage:     "Dumps genesis block JSON configuration to stdout",
    75  		ArgsUsage: "",
    76  		Flags: []cli.Flag{
    77  			utils.CypressFlag,
    78  			utils.BaobabFlag,
    79  		},
    80  		Category: "BLOCKCHAIN COMMANDS",
    81  		Description: `
    82  The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`,
    83  	}
    84  )
    85  
    86  // initGenesis will initialise the given JSON format genesis file and writes it as
    87  // the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
    88  func initGenesis(ctx *cli.Context) error {
    89  	// Make sure we have a valid genesis JSON
    90  	genesisPath := ctx.Args().First()
    91  	if len(genesisPath) == 0 {
    92  		logger.Crit("Must supply path to genesis JSON file")
    93  	}
    94  	file, err := os.Open(genesisPath)
    95  	if err != nil {
    96  		logger.Crit("Failed to read genesis file", "err", err)
    97  	}
    98  	defer file.Close()
    99  
   100  	genesis := new(blockchain.Genesis)
   101  	if err := json.NewDecoder(file).Decode(genesis); err != nil {
   102  		logger.Crit("Invalid genesis file", "err", err)
   103  		return err
   104  	}
   105  	if genesis.Config == nil {
   106  		logger.Crit("Genesis config is not set")
   107  	}
   108  
   109  	// Update undefined config with default values
   110  	genesis.Config.SetDefaultsForGenesis()
   111  
   112  	// Validate config values
   113  	if err := ValidateGenesisConfig(genesis); err != nil {
   114  		logger.Crit("Invalid genesis", "err", err)
   115  	}
   116  
   117  	// Set genesis.Governance and reward intervals
   118  	govSet := governance.GetGovernanceItemsFromChainConfig(genesis.Config)
   119  	govItemBytes, err := json.Marshal(govSet.Items())
   120  	if err != nil {
   121  		logger.Crit("Failed to json marshaling governance data", "err", err)
   122  	}
   123  	if genesis.Governance, err = rlp.EncodeToBytes(govItemBytes); err != nil {
   124  		logger.Crit("Failed to encode initial settings. Check your genesis.json", "err", err)
   125  	}
   126  	params.SetStakingUpdateInterval(genesis.Config.Governance.Reward.StakingUpdateInterval)
   127  	params.SetProposerUpdateInterval(genesis.Config.Governance.Reward.ProposerUpdateInterval)
   128  
   129  	// Open an initialise both full and light databases
   130  	stack := MakeFullNode(ctx)
   131  	parallelDBWrite := !ctx.GlobalIsSet(utils.NoParallelDBWriteFlag.Name)
   132  	singleDB := ctx.GlobalIsSet(utils.SingleDBFlag.Name)
   133  	numStateTrieShards := ctx.GlobalUint(utils.NumStateTrieShardsFlag.Name)
   134  	overwriteGenesis := ctx.GlobalBool(utils.OverwriteGenesisFlag.Name)
   135  
   136  	dbtype := database.DBType(ctx.GlobalString(utils.DbTypeFlag.Name)).ToValid()
   137  	if len(dbtype) == 0 {
   138  		logger.Crit("invalid dbtype", "dbtype", ctx.GlobalString(utils.DbTypeFlag.Name))
   139  	}
   140  
   141  	var dynamoDBConfig *database.DynamoDBConfig
   142  	if dbtype == database.DynamoDB {
   143  		dynamoDBConfig = &database.DynamoDBConfig{
   144  			TableName:          ctx.GlobalString(utils.DynamoDBTableNameFlag.Name),
   145  			Region:             ctx.GlobalString(utils.DynamoDBRegionFlag.Name),
   146  			IsProvisioned:      ctx.GlobalBool(utils.DynamoDBIsProvisionedFlag.Name),
   147  			ReadCapacityUnits:  ctx.GlobalInt64(utils.DynamoDBReadCapacityFlag.Name),
   148  			WriteCapacityUnits: ctx.GlobalInt64(utils.DynamoDBWriteCapacityFlag.Name),
   149  			ReadOnly:           ctx.GlobalBool(utils.DynamoDBReadOnlyFlag.Name),
   150  		}
   151  	}
   152  
   153  	for _, name := range []string{"chaindata"} { // Removed "lightchaindata" since Klaytn doesn't use it
   154  		dbc := &database.DBConfig{
   155  			Dir: name, DBType: dbtype, ParallelDBWrite: parallelDBWrite,
   156  			SingleDB: singleDB, NumStateTrieShards: numStateTrieShards,
   157  			LevelDBCacheSize: 0, OpenFilesLimit: 0, DynamoDBConfig: dynamoDBConfig,
   158  		}
   159  		chainDB := stack.OpenDatabase(dbc)
   160  
   161  		// Initialize DeriveSha implementation
   162  		blockchain.InitDeriveSha(genesis.Config)
   163  
   164  		_, hash, err := blockchain.SetupGenesisBlock(chainDB, genesis, params.UnusedNetworkId, false, overwriteGenesis)
   165  		if err != nil {
   166  			logger.Crit("Failed to write genesis block", "err", err)
   167  		}
   168  
   169  		// Write governance items to database
   170  		// If governance data already exist, it'll be skipped with an error log and will not return an error
   171  		gov := governance.NewMixedEngineNoInit(genesis.Config, chainDB)
   172  		if err := gov.WriteGovernance(0, govSet, governance.NewGovernanceSet()); err != nil {
   173  			logger.Crit("Failed to write governance items", "err", err)
   174  		}
   175  
   176  		logger.Info("Successfully wrote genesis state", "database", name, "hash", hash.String())
   177  		chainDB.Close()
   178  	}
   179  	return nil
   180  }
   181  
   182  func dumpGenesis(ctx *cli.Context) error {
   183  	genesis := MakeGenesis(ctx)
   184  	if genesis == nil {
   185  		genesis = blockchain.DefaultGenesisBlock()
   186  	}
   187  	if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil {
   188  		logger.Crit("could not encode genesis")
   189  	}
   190  	return nil
   191  }
   192  
   193  func MakeGenesis(ctx *cli.Context) *blockchain.Genesis {
   194  	var genesis *blockchain.Genesis
   195  	switch {
   196  	case ctx.GlobalBool(utils.CypressFlag.Name):
   197  		genesis = blockchain.DefaultGenesisBlock()
   198  	case ctx.GlobalBool(utils.BaobabFlag.Name):
   199  		genesis = blockchain.DefaultBaobabGenesisBlock()
   200  	}
   201  	return genesis
   202  }
   203  
   204  func ValidateGenesisConfig(g *blockchain.Genesis) error {
   205  	if g.Config.ChainID == nil {
   206  		return errors.New("chainID is not specified")
   207  	}
   208  
   209  	if g.Config.Clique == nil && g.Config.Istanbul == nil {
   210  		return errors.New("consensus engine should be configured")
   211  	}
   212  
   213  	if g.Config.Clique != nil && g.Config.Istanbul != nil {
   214  		return errors.New("only one consensus engine can be configured")
   215  	}
   216  
   217  	if g.Config.Governance == nil || g.Config.Governance.Reward == nil {
   218  		return errors.New("governance and reward policies should be configured")
   219  	}
   220  
   221  	if g.Config.Governance.Reward.ProposerUpdateInterval == 0 || g.Config.Governance.Reward.
   222  		StakingUpdateInterval == 0 {
   223  		return errors.New("proposerUpdateInterval and stakingUpdateInterval cannot be zero")
   224  	}
   225  
   226  	if g.Config.Istanbul != nil {
   227  		if err := governance.CheckGenesisValues(g.Config); err != nil {
   228  			return err
   229  		}
   230  
   231  		// TODO-Klaytn: Add validation logic for other GovernanceModes
   232  		// Check if governingNode is properly set
   233  		if strings.ToLower(g.Config.Governance.GovernanceMode) == "single" {
   234  			var found bool
   235  
   236  			istanbulExtra, err := types.ExtractIstanbulExtra(&types.Header{Extra: g.ExtraData})
   237  			if err != nil {
   238  				return err
   239  			}
   240  
   241  			for _, v := range istanbulExtra.Validators {
   242  				if v == g.Config.Governance.GoverningNode {
   243  					found = true
   244  					break
   245  				}
   246  			}
   247  			if !found {
   248  				return errors.New("governingNode is not in the validator list")
   249  			}
   250  		}
   251  	}
   252  	return nil
   253  }