github.com/klaytn/klaytn@v1.12.1/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 "github.com/urfave/cli/v2" 38 ) 39 40 var logger = log.NewModuleLogger(log.CMDUtilsNodeCMD) 41 42 var ( 43 InitCommand = &cli.Command{ 44 Action: 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.ChainDataDirFlag, 61 utils.RocksDBSecondaryFlag, 62 utils.RocksDBCacheSizeFlag, 63 utils.RocksDBDumpMallocStatFlag, 64 utils.RocksDBFilterPolicyFlag, 65 utils.RocksDBCompressionTypeFlag, 66 utils.RocksDBBottommostCompressionTypeFlag, 67 utils.RocksDBDisableMetricsFlag, 68 utils.RocksDBMaxOpenFilesFlag, 69 utils.RocksDBCacheIndexAndFilterFlag, 70 utils.OverwriteGenesisFlag, 71 utils.LivePruningFlag, 72 }, 73 Category: "BLOCKCHAIN COMMANDS", 74 Description: ` 75 The init command initializes a new genesis block and definition for the network. 76 This is a destructive action and changes the network in which you will be 77 participating. 78 79 It expects the genesis file as argument.`, 80 } 81 82 DumpGenesisCommand = &cli.Command{ 83 Action: dumpGenesis, 84 Name: "dumpgenesis", 85 Usage: "Dumps genesis block JSON configuration to stdout", 86 ArgsUsage: "", 87 Flags: []cli.Flag{ 88 utils.CypressFlag, 89 utils.BaobabFlag, 90 }, 91 Category: "BLOCKCHAIN COMMANDS", 92 Description: ` 93 The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`, 94 } 95 ) 96 97 // initGenesis will initialise the given JSON format genesis file and writes it as 98 // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. 99 func initGenesis(ctx *cli.Context) error { 100 // Make sure we have a valid genesis JSON 101 genesisPath := ctx.Args().First() 102 if len(genesisPath) == 0 { 103 logger.Crit("Must supply path to genesis JSON file") 104 } 105 file, err := os.Open(genesisPath) 106 if err != nil { 107 logger.Crit("Failed to read genesis file", "err", err) 108 } 109 defer file.Close() 110 111 genesis := new(blockchain.Genesis) 112 if err := json.NewDecoder(file).Decode(genesis); err != nil { 113 logger.Crit("Invalid genesis file", "err", err) 114 return err 115 } 116 if genesis.Config == nil { 117 logger.Crit("Genesis config is not set") 118 } 119 120 // Update undefined config with default values 121 genesis.Config.SetDefaultsForGenesis() 122 123 // Validate config values 124 if err := ValidateGenesisConfig(genesis); err != nil { 125 logger.Crit("Invalid genesis", "err", err) 126 } 127 128 // Set genesis.Governance and reward intervals 129 govSet := governance.GetGovernanceItemsFromChainConfig(genesis.Config) 130 govItemBytes, err := json.Marshal(govSet.Items()) 131 if err != nil { 132 logger.Crit("Failed to json marshaling governance data", "err", err) 133 } 134 if genesis.Governance, err = rlp.EncodeToBytes(govItemBytes); err != nil { 135 logger.Crit("Failed to encode initial settings. Check your genesis.json", "err", err) 136 } 137 params.SetStakingUpdateInterval(genesis.Config.Governance.Reward.StakingUpdateInterval) 138 params.SetProposerUpdateInterval(genesis.Config.Governance.Reward.ProposerUpdateInterval) 139 140 // Open an initialise both full and light databases 141 stack := MakeFullNode(ctx) 142 parallelDBWrite := !ctx.Bool(utils.NoParallelDBWriteFlag.Name) 143 singleDB := ctx.Bool(utils.SingleDBFlag.Name) 144 numStateTrieShards := ctx.Uint(utils.NumStateTrieShardsFlag.Name) 145 overwriteGenesis := ctx.Bool(utils.OverwriteGenesisFlag.Name) 146 livePruning := ctx.Bool(utils.LivePruningFlag.Name) 147 148 dbtype := database.DBType(ctx.String(utils.DbTypeFlag.Name)).ToValid() 149 if len(dbtype) == 0 { 150 logger.Crit("invalid dbtype", "dbtype", ctx.String(utils.DbTypeFlag.Name)) 151 } 152 153 var dynamoDBConfig *database.DynamoDBConfig 154 if dbtype == database.DynamoDB { 155 dynamoDBConfig = &database.DynamoDBConfig{ 156 TableName: ctx.String(utils.DynamoDBTableNameFlag.Name), 157 Region: ctx.String(utils.DynamoDBRegionFlag.Name), 158 IsProvisioned: ctx.Bool(utils.DynamoDBIsProvisionedFlag.Name), 159 ReadCapacityUnits: ctx.Int64(utils.DynamoDBReadCapacityFlag.Name), 160 WriteCapacityUnits: ctx.Int64(utils.DynamoDBWriteCapacityFlag.Name), 161 ReadOnly: ctx.Bool(utils.DynamoDBReadOnlyFlag.Name), 162 } 163 } 164 rocksDBConfig := database.GetDefaultRocksDBConfig() 165 if dbtype == database.RocksDB { 166 rocksDBConfig = &database.RocksDBConfig{ 167 Secondary: ctx.Bool(utils.RocksDBSecondaryFlag.Name), 168 DumpMallocStat: ctx.Bool(utils.RocksDBDumpMallocStatFlag.Name), 169 DisableMetrics: ctx.Bool(utils.RocksDBDisableMetricsFlag.Name), 170 CacheSize: ctx.Uint64(utils.RocksDBCacheSizeFlag.Name), 171 CompressionType: ctx.String(utils.RocksDBCompressionTypeFlag.Name), 172 BottommostCompressionType: ctx.String(utils.RocksDBBottommostCompressionTypeFlag.Name), 173 FilterPolicy: ctx.String(utils.RocksDBFilterPolicyFlag.Name), 174 MaxOpenFiles: ctx.Int(utils.RocksDBMaxOpenFilesFlag.Name), 175 CacheIndexAndFilter: ctx.Bool(utils.RocksDBCacheIndexAndFilterFlag.Name), 176 } 177 } 178 179 for _, name := range []string{"chaindata"} { // Removed "lightchaindata" since Klaytn doesn't use it 180 dbc := &database.DBConfig{ 181 Dir: name, DBType: dbtype, ParallelDBWrite: parallelDBWrite, 182 SingleDB: singleDB, NumStateTrieShards: numStateTrieShards, 183 LevelDBCacheSize: 0, OpenFilesLimit: 0, DynamoDBConfig: dynamoDBConfig, RocksDBConfig: rocksDBConfig, 184 } 185 chainDB := stack.OpenDatabase(dbc) 186 187 // Initialize DeriveSha implementation 188 blockchain.InitDeriveSha(genesis.Config) 189 190 _, hash, err := blockchain.SetupGenesisBlock(chainDB, genesis, params.UnusedNetworkId, false, overwriteGenesis) 191 if err != nil { 192 logger.Crit("Failed to write genesis block", "err", err) 193 } 194 195 // Write governance items to database 196 // If governance data already exist, it'll be skipped with an error log and will not return an error 197 gov := governance.NewMixedEngineNoInit(genesis.Config, chainDB) 198 if err := gov.WriteGovernance(0, govSet, governance.NewGovernanceSet()); err != nil { 199 logger.Crit("Failed to write governance items", "err", err) 200 } 201 202 // Write the live pruning flag to database 203 if livePruning { 204 logger.Info("Writing live pruning flag to database") 205 chainDB.WritePruningEnabled() 206 } 207 208 logger.Info("Successfully wrote genesis state", "database", name, "hash", hash.String()) 209 chainDB.Close() 210 } 211 return nil 212 } 213 214 func dumpGenesis(ctx *cli.Context) error { 215 genesis := MakeGenesis(ctx) 216 if genesis == nil { 217 genesis = blockchain.DefaultGenesisBlock() 218 } 219 if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil { 220 logger.Crit("could not encode genesis") 221 } 222 return nil 223 } 224 225 func MakeGenesis(ctx *cli.Context) *blockchain.Genesis { 226 var genesis *blockchain.Genesis 227 switch { 228 case ctx.Bool(utils.CypressFlag.Name): 229 genesis = blockchain.DefaultGenesisBlock() 230 case ctx.Bool(utils.BaobabFlag.Name): 231 genesis = blockchain.DefaultBaobabGenesisBlock() 232 } 233 return genesis 234 } 235 236 func ValidateGenesisConfig(g *blockchain.Genesis) error { 237 if g.Config.ChainID == nil { 238 return errors.New("chainID is not specified") 239 } 240 241 if g.Config.Clique == nil && g.Config.Istanbul == nil { 242 return errors.New("consensus engine should be configured") 243 } 244 245 if g.Config.Clique != nil && g.Config.Istanbul != nil { 246 return errors.New("only one consensus engine can be configured") 247 } 248 249 if g.Config.Governance == nil || g.Config.Governance.Reward == nil { 250 return errors.New("governance and reward policies should be configured") 251 } 252 253 if g.Config.Governance.Reward.ProposerUpdateInterval == 0 || g.Config.Governance.Reward. 254 StakingUpdateInterval == 0 { 255 return errors.New("proposerUpdateInterval and stakingUpdateInterval cannot be zero") 256 } 257 258 if g.Config.Istanbul != nil { 259 if err := governance.CheckGenesisValues(g.Config); err != nil { 260 return err 261 } 262 263 // TODO-Klaytn: Add validation logic for other GovernanceModes 264 // Check if governingNode is properly set 265 if strings.ToLower(g.Config.Governance.GovernanceMode) == "single" { 266 var found bool 267 268 istanbulExtra, err := types.ExtractIstanbulExtra(&types.Header{Extra: g.ExtraData}) 269 if err != nil { 270 return err 271 } 272 273 for _, v := range istanbulExtra.Validators { 274 if v == g.Config.Governance.GoverningNode { 275 found = true 276 break 277 } 278 } 279 if !found { 280 return errors.New("governingNode is not in the validator list") 281 } 282 } 283 } 284 return nil 285 }