github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/cmd/puppeth/wizard_genesis.go (about) 1 // Copyright 2019 The ebakus/go-ebakus Authors 2 // This file is part of ebakus/go-ebakus. 3 // 4 // ebakus/go-ebakus is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // ebakus/go-ebakus is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with ebakus/go-ebakus. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "math/big" 26 "math/rand" 27 "net/http" 28 "os" 29 "path/filepath" 30 "time" 31 32 "github.com/ebakus/go-ebakus/common" 33 "github.com/ebakus/go-ebakus/core" 34 "github.com/ebakus/go-ebakus/log" 35 "github.com/ebakus/go-ebakus/params" 36 ) 37 38 // makeGenesis creates a new genesis struct based on some user input. 39 func (w *wizard) makeGenesis() { 40 // Construct a default genesis block 41 genesis := &core.Genesis{ 42 Timestamp: uint64(time.Now().Unix()), 43 GasLimit: 4700000, 44 Difficulty: big.NewInt(524288), 45 Alloc: make(core.GenesisAlloc), 46 Config: ¶ms.ChainConfig{ 47 HomesteadBlock: big.NewInt(0), 48 EIP150Block: big.NewInt(0), 49 EIP155Block: big.NewInt(0), 50 EIP158Block: big.NewInt(0), 51 ByzantiumBlock: big.NewInt(0), 52 ConstantinopleBlock: big.NewInt(0), 53 PetersburgBlock: big.NewInt(0), 54 IstanbulBlock: big.NewInt(0), 55 }, 56 } 57 // Figure out which consensus engine to choose 58 fmt.Println() 59 fmt.Println("Which consensus engine to use? (default = clique)") 60 fmt.Println(" 1. Ethash - proof-of-work") 61 fmt.Println(" 2. Clique - proof-of-authority") 62 fmt.Println(" 3. DPOS - Delegated Proof-of-stake") 63 64 choice := w.read() 65 switch { 66 case choice == "1": 67 // In case of ethash, we're pretty much done 68 genesis.Config.Ethash = new(params.EthashConfig) 69 genesis.ExtraData = make([]byte, 32) 70 71 case choice == "" || choice == "2": 72 // In the case of clique, configure the consensus parameters 73 genesis.Difficulty = big.NewInt(1) 74 genesis.Config.Clique = ¶ms.CliqueConfig{ 75 Period: 15, 76 Epoch: 30000, 77 } 78 fmt.Println() 79 fmt.Println("How many seconds should blocks take? (default = 15)") 80 genesis.Config.Clique.Period = uint64(w.readDefaultInt(15)) 81 82 // We also need the initial list of signers 83 fmt.Println() 84 fmt.Println("Which accounts are allowed to seal? (mandatory at least one)") 85 86 var signers []common.Address 87 for { 88 if address := w.readAddress(); address != nil { 89 signers = append(signers, *address) 90 continue 91 } 92 if len(signers) > 0 { 93 break 94 } 95 } 96 // Sort the signers and embed into the extra-data section 97 for i := 0; i < len(signers); i++ { 98 for j := i + 1; j < len(signers); j++ { 99 if bytes.Compare(signers[i][:], signers[j][:]) > 0 { 100 signers[i], signers[j] = signers[j], signers[i] 101 } 102 } 103 } 104 genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) 105 for i, signer := range signers { 106 copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) 107 } 108 109 case choice == "3": 110 genesis.Difficulty = big.NewInt(1) 111 genesis.Config.DPOS = ¶ms.DPOSConfig{ 112 Period: 2, 113 } 114 115 fmt.Println() 116 fmt.Println("How many seconds should blocks take? (default = 5)") 117 genesis.Config.DPOS.Period = uint64(w.readDefaultInt(5)) 118 119 default: 120 log.Crit("Invalid consensus engine choice", "choice", choice) 121 } 122 // Consensus all set, just ask for initial funds and go 123 fmt.Println() 124 fmt.Println("Which accounts should be pre-funded? (advisable at least one)") 125 for { 126 // Read the address of the account to fund 127 if address := w.readAddress(); address != nil { 128 genesis.Alloc[*address] = core.GenesisAccount{ 129 Balance: new(big.Int).Lsh(big.NewInt(1), 256-7), // 2^256 / 128 (allow many pre-funds without balance overflows) 130 } 131 continue 132 } 133 break 134 } 135 fmt.Println() 136 fmt.Println("Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)") 137 if w.readDefaultYesNo(true) { 138 // Add a batch of precompile balances to avoid them getting deleted 139 for i := int64(0); i < 256; i++ { 140 genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)} 141 } 142 } 143 // Query the user for some custom extras 144 fmt.Println() 145 fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)") 146 genesis.Config.ChainID = new(big.Int).SetUint64(uint64(w.readDefaultInt(rand.Intn(65536)))) 147 148 // All done, store the genesis and flush to disk 149 log.Info("Configured new genesis block") 150 151 w.conf.Genesis = genesis 152 w.conf.flush() 153 } 154 155 // importGenesis imports a Ebakus genesis spec into puppeth. 156 func (w *wizard) importGenesis() { 157 // Request the genesis JSON spec URL from the user 158 fmt.Println() 159 fmt.Println("Where's the genesis file? (local file or http/https url)") 160 url := w.readURL() 161 162 // Convert the various allowed URLs to a reader stream 163 var reader io.Reader 164 165 switch url.Scheme { 166 case "http", "https": 167 // Remote web URL, retrieve it via an HTTP client 168 res, err := http.Get(url.String()) 169 if err != nil { 170 log.Error("Failed to retrieve remote genesis", "err", err) 171 return 172 } 173 defer res.Body.Close() 174 reader = res.Body 175 176 case "": 177 // Schemaless URL, interpret as a local file 178 file, err := os.Open(url.String()) 179 if err != nil { 180 log.Error("Failed to open local genesis", "err", err) 181 return 182 } 183 defer file.Close() 184 reader = file 185 186 default: 187 log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme) 188 return 189 } 190 // Parse the genesis file and inject it successful 191 var genesis core.Genesis 192 if err := json.NewDecoder(reader).Decode(&genesis); err != nil { 193 log.Error("Invalid genesis spec: %v", err) 194 return 195 } 196 log.Info("Imported genesis block") 197 198 w.conf.Genesis = &genesis 199 w.conf.flush() 200 } 201 202 // manageGenesis permits the modification of chain configuration parameters in 203 // a genesis config and the export of the entire genesis spec. 204 func (w *wizard) manageGenesis() { 205 // Figure out whether to modify or export the genesis 206 fmt.Println() 207 fmt.Println(" 1. Modify existing configurations") 208 fmt.Println(" 2. Export genesis configurations") 209 fmt.Println(" 3. Remove genesis configuration") 210 211 choice := w.read() 212 switch choice { 213 case "1": 214 // Fork rule updating requested, iterate over each fork 215 fmt.Println() 216 fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock) 217 w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock) 218 219 fmt.Println() 220 fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block) 221 w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block) 222 223 fmt.Println() 224 fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block) 225 w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block) 226 227 fmt.Println() 228 fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block) 229 w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block) 230 231 fmt.Println() 232 fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock) 233 w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock) 234 235 fmt.Println() 236 fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock) 237 w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock) 238 if w.conf.Genesis.Config.PetersburgBlock == nil { 239 w.conf.Genesis.Config.PetersburgBlock = w.conf.Genesis.Config.ConstantinopleBlock 240 } 241 fmt.Println() 242 fmt.Printf("Which block should Petersburg come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock) 243 w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock) 244 245 fmt.Println() 246 fmt.Printf("Which block should Istanbul come into effect? (default = %v)\n", w.conf.Genesis.Config.IstanbulBlock) 247 w.conf.Genesis.Config.IstanbulBlock = w.readDefaultBigInt(w.conf.Genesis.Config.IstanbulBlock) 248 249 out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ") 250 fmt.Printf("Chain configuration updated:\n\n%s\n", out) 251 252 w.conf.flush() 253 254 case "2": 255 // Save whatever genesis configuration we currently have 256 fmt.Println() 257 fmt.Printf("Which folder to save the genesis specs into? (default = current)\n") 258 fmt.Printf(" Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network) 259 260 folder := w.readDefaultString(".") 261 if err := os.MkdirAll(folder, 0755); err != nil { 262 log.Error("Failed to create spec folder", "folder", folder, "err", err) 263 return 264 } 265 out, _ := json.MarshalIndent(w.conf.Genesis, "", " ") 266 267 // Export the native genesis spec used by puppeth and Ebakus 268 ebakusJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network)) 269 if err := ioutil.WriteFile((ebakusJson), out, 0644); err != nil { 270 log.Error("Failed to save genesis file", "err", err) 271 return 272 } 273 log.Info("Saved native genesis chain spec", "path", ebakusJson) 274 275 // Export the genesis spec used by Aleth (formerly C++ Ethereum) 276 if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil { 277 log.Error("Failed to create Aleth chain spec", "err", err) 278 } else { 279 saveGenesis(folder, w.network, "aleth", spec) 280 } 281 // Export the genesis spec used by Parity 282 if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil { 283 log.Error("Failed to create Parity chain spec", "err", err) 284 } else { 285 saveGenesis(folder, w.network, "parity", spec) 286 } 287 // Export the genesis spec used by Harmony (formerly EthereumJ) 288 saveGenesis(folder, w.network, "harmony", w.conf.Genesis) 289 290 case "3": 291 // Make sure we don't have any services running 292 if len(w.conf.servers()) > 0 { 293 log.Error("Genesis reset requires all services and servers torn down") 294 return 295 } 296 log.Info("Genesis block destroyed") 297 298 w.conf.Genesis = nil 299 w.conf.flush() 300 default: 301 log.Error("That's not something I can do") 302 return 303 } 304 } 305 306 // saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file. 307 func saveGenesis(folder, network, client string, spec interface{}) { 308 path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client)) 309 310 out, _ := json.MarshalIndent(spec, "", " ") 311 if err := ioutil.WriteFile(path, out, 0644); err != nil { 312 log.Error("Failed to save genesis file", "client", client, "err", err) 313 return 314 } 315 log.Info("Saved genesis chain spec", "client", client, "path", path) 316 }