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: &params.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 = &params.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 = &params.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  }