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 }