github.com/XinFinOrg/xdcchain@v1.1.0/cmd/puppeth/wizard_genesis.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum 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  // go-ethereum 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 go-ethereum. 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/rand"
    26  	"net/http"
    27  	"os"
    28  	"path/filepath"
    29  	"time"
    30  
    31  	"github.com/ethereum/go-ethereum/common"
    32  	"github.com/ethereum/go-ethereum/core"
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/ethereum/go-ethereum/params"
    35  
    36  	"context"
    37  	"math/big"
    38  
    39  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    40  	"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
    41  	blockSignerContract "github.com/ethereum/go-ethereum/contracts/blocksigner"
    42  	multiSignWalletContract "github.com/ethereum/go-ethereum/contracts/multisigwallet"
    43  	randomizeContract "github.com/ethereum/go-ethereum/contracts/randomize"
    44  	validatorContract "github.com/ethereum/go-ethereum/contracts/validator"
    45  	"github.com/ethereum/go-ethereum/crypto"
    46  	"github.com/ethereum/go-ethereum/rlp"
    47  )
    48  
    49  // makeGenesis creates a new genesis struct based on some user input.
    50  func (w *wizard) makeGenesis() {
    51  	// Construct a default genesis block
    52  	genesis := &core.Genesis{
    53  		Timestamp:  uint64(time.Now().Unix()),
    54  		GasLimit:   4700000,
    55  		Difficulty: big.NewInt(524288),
    56  		Alloc:      make(core.GenesisAlloc),
    57  		Config: &params.ChainConfig{
    58  			HomesteadBlock:      big.NewInt(1),
    59  			EIP150Block:         big.NewInt(2),
    60  			EIP155Block:         big.NewInt(3),
    61  			EIP158Block:         big.NewInt(3),
    62  			ByzantiumBlock:      big.NewInt(4),
    63  			ConstantinopleBlock: big.NewInt(5),
    64  		},
    65  	}
    66  	// Figure out which consensus engine to choose
    67  	fmt.Println()
    68  	fmt.Println("Which consensus engine to use? (default = XDPoS)")
    69  	fmt.Println(" 1. Ethash - proof-of-work")
    70  	fmt.Println(" 2. Clique - proof-of-authority")
    71  	fmt.Println(" 3. XDPoS - delegated-proof-of-stake")
    72  
    73  	choice := w.read()
    74  	switch {
    75  	case choice == "1":
    76  		// In case of ethash, we're pretty much done
    77  		genesis.Config.Ethash = new(params.EthashConfig)
    78  		genesis.ExtraData = make([]byte, 32)
    79  
    80  	case choice == "2":
    81  		// In the case of clique, configure the consensus parameters
    82  		genesis.Difficulty = big.NewInt(1)
    83  		genesis.Config.Clique = &params.CliqueConfig{
    84  			Period: 15,
    85  			Epoch:  30000,
    86  		}
    87  		fmt.Println()
    88  		fmt.Println("How many seconds should blocks take? (default = 15)")
    89  		genesis.Config.Clique.Period = uint64(w.readDefaultInt(15))
    90  
    91  		// We also need the initial list of signers
    92  		fmt.Println()
    93  		fmt.Println("Which accounts are allowed to seal? (mandatory at least one)")
    94  
    95  		var signers []common.Address
    96  		for {
    97  			if address := w.readAddress(); address != nil {
    98  				signers = append(signers, *address)
    99  				continue
   100  			}
   101  			if len(signers) > 0 {
   102  				break
   103  			}
   104  		}
   105  		// Sort the signers and embed into the extra-data section
   106  		for i := 0; i < len(signers); i++ {
   107  			for j := i + 1; j < len(signers); j++ {
   108  				if bytes.Compare(signers[i][:], signers[j][:]) > 0 {
   109  					signers[i], signers[j] = signers[j], signers[i]
   110  				}
   111  			}
   112  		}
   113  		genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
   114  		for i, signer := range signers {
   115  			copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
   116  		}
   117  
   118  	case choice == "" || choice == "3":
   119  		genesis.Difficulty = big.NewInt(1)
   120  		genesis.Config.XDPoS = &params.XDPoSConfig{
   121  			Period: 15,
   122  			Epoch:  30000,
   123  			Reward: 0,
   124  		}
   125  		fmt.Println()
   126  		fmt.Println("How many seconds should blocks take? (default = 2)")
   127  		genesis.Config.XDPoS.Period = uint64(w.readDefaultInt(2))
   128  
   129  		fmt.Println()
   130  		fmt.Println("How many XDC should be rewarded to masternode? (default = 5000)")
   131  		genesis.Config.XDPoS.Reward = uint64(w.readDefaultInt(5000))
   132  
   133  		fmt.Println()
   134  		fmt.Println("Who own the first masternodes? (mandatory)")
   135  		owner := *w.readAddress()
   136  
   137  		// We also need the initial list of signers
   138  		fmt.Println()
   139  		fmt.Println("Which accounts are allowed to seal (signers)? (mandatory at least one)")
   140  
   141  		var signers []common.Address
   142  		for {
   143  			if address := w.readAddress(); address != nil {
   144  				signers = append(signers, *address)
   145  				continue
   146  			}
   147  			if len(signers) > 0 {
   148  				break
   149  			}
   150  		}
   151  		// Sort the signers and embed into the extra-data section
   152  		for i := 0; i < len(signers); i++ {
   153  			for j := i + 1; j < len(signers); j++ {
   154  				if bytes.Compare(signers[i][:], signers[j][:]) > 0 {
   155  					signers[i], signers[j] = signers[j], signers[i]
   156  				}
   157  			}
   158  		}
   159  		validatorCap := new(big.Int)
   160  		validatorCap.SetString("10000000000000000000000000", 10)
   161  		var validatorCaps []*big.Int
   162  		genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
   163  		for i, signer := range signers {
   164  			validatorCaps = append(validatorCaps, validatorCap)
   165  			copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
   166  		}
   167  
   168  		fmt.Println()
   169  		fmt.Println("How many blocks per epoch? (default = 900)")
   170  		epochNumber := uint64(w.readDefaultInt(900))
   171  		genesis.Config.XDPoS.Epoch = epochNumber
   172  		genesis.Config.XDPoS.RewardCheckpoint = epochNumber
   173  
   174  		fmt.Println()
   175  		fmt.Println("How many blocks before checkpoint need to prepare new set of masternodes? (default = 450)")
   176  		genesis.Config.XDPoS.Gap = uint64(w.readDefaultInt(450))
   177  
   178  		fmt.Println()
   179  		fmt.Println("What is foundation wallet address? (default = xdc746249C61f5832C5eEd53172776b460491bDcd5C)")
   180  		genesis.Config.XDPoS.FoudationWalletAddr = w.readDefaultAddress(common.HexToAddress(common.FoudationAddr))
   181  
   182  		// Validator Smart Contract Code
   183  		pKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   184  		addr := crypto.PubkeyToAddress(pKey.PublicKey)
   185  		contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
   186  		transactOpts := bind.NewKeyedTransactor(pKey)
   187  
   188  		validatorAddress, _, err := validatorContract.DeployValidator(transactOpts, contractBackend, signers, validatorCaps, owner)
   189  		if err != nil {
   190  			fmt.Println("Can't deploy root registry")
   191  		}
   192  		contractBackend.Commit()
   193  
   194  		d := time.Now().Add(1000 * time.Millisecond)
   195  		ctx, cancel := context.WithDeadline(context.Background(), d)
   196  		defer cancel()
   197  		code, _ := contractBackend.CodeAt(ctx, validatorAddress, nil)
   198  		storage := make(map[common.Hash]common.Hash)
   199  		f := func(key, val common.Hash) bool {
   200  			decode := []byte{}
   201  			trim := bytes.TrimLeft(val.Bytes(), "\x00")
   202  			rlp.DecodeBytes(trim, &decode)
   203  			storage[key] = common.BytesToHash(decode)
   204  			log.Info("DecodeBytes", "value", val.String(), "decode", storage[key].String())
   205  			return true
   206  		}
   207  		contractBackend.ForEachStorageAt(ctx, validatorAddress, nil, f)
   208  		genesis.Alloc[common.HexToAddress(common.MasternodeVotingSMC)] = core.GenesisAccount{
   209  			Balance: validatorCap.Mul(validatorCap, big.NewInt(int64(len(validatorCaps)))),
   210  			Code:    code,
   211  			Storage: storage,
   212  		}
   213  
   214  		fmt.Println()
   215  		fmt.Println("Which accounts are allowed to confirm in Foudation MultiSignWallet?")
   216  		var owners []common.Address
   217  		for {
   218  			if address := w.readAddress(); address != nil {
   219  				owners = append(owners, *address)
   220  				continue
   221  			}
   222  			if len(owners) > 0 {
   223  				break
   224  			}
   225  		}
   226  		fmt.Println()
   227  		fmt.Println("How many require for confirm tx in Foudation MultiSignWallet? (default = 2)")
   228  		required := int64(w.readDefaultInt(2))
   229  
   230  		// MultiSigWallet.
   231  		multiSignWalletAddr, _, err := multiSignWalletContract.DeployMultiSigWallet(transactOpts, contractBackend, owners, big.NewInt(required))
   232  		if err != nil {
   233  			fmt.Println("Can't deploy MultiSignWallet SMC")
   234  		}
   235  		contractBackend.Commit()
   236  		code, _ = contractBackend.CodeAt(ctx, multiSignWalletAddr, nil)
   237  		storage = make(map[common.Hash]common.Hash)
   238  		contractBackend.ForEachStorageAt(ctx, multiSignWalletAddr, nil, f)
   239  		fBalance := big.NewInt(0) // 3 billion
   240  		fBalance.Add(fBalance, big.NewInt(0*1000*1000*1000))
   241  		fBalance.Mul(fBalance, big.NewInt(1000000000000000000))
   242  		genesis.Alloc[common.HexToAddress(common.FoudationAddr)] = core.GenesisAccount{
   243  			Balance: fBalance,
   244  			Code:    code,
   245  			Storage: storage,
   246  		}
   247  
   248  		// Block Signers Smart Contract
   249  		blockSignerAddress, _, err := blockSignerContract.DeployBlockSigner(transactOpts, contractBackend, big.NewInt(int64(epochNumber)))
   250  		if err != nil {
   251  			fmt.Println("Can't deploy root registry")
   252  		}
   253  		contractBackend.Commit()
   254  
   255  		code, _ = contractBackend.CodeAt(ctx, blockSignerAddress, nil)
   256  		storage = make(map[common.Hash]common.Hash)
   257  		contractBackend.ForEachStorageAt(ctx, blockSignerAddress, nil, f)
   258  		genesis.Alloc[common.HexToAddress(common.BlockSigners)] = core.GenesisAccount{
   259  			Balance: big.NewInt(0),
   260  			Code:    code,
   261  			Storage: storage,
   262  		}
   263  
   264  		// Randomize Smart Contract Code
   265  		randomizeAddress, _, err := randomizeContract.DeployRandomize(transactOpts, contractBackend)
   266  		if err != nil {
   267  			fmt.Println("Can't deploy root registry")
   268  		}
   269  		contractBackend.Commit()
   270  
   271  		code, _ = contractBackend.CodeAt(ctx, randomizeAddress, nil)
   272  		storage = make(map[common.Hash]common.Hash)
   273  		contractBackend.ForEachStorageAt(ctx, randomizeAddress, nil, f)
   274  		genesis.Alloc[common.HexToAddress(common.RandomizeSMC)] = core.GenesisAccount{
   275  			Balance: big.NewInt(0),
   276  			Code:    code,
   277  			Storage: storage,
   278  		}
   279  
   280  		fmt.Println()
   281  		fmt.Println("Which accounts are allowed to confirm in Team MultiSignWallet?")
   282  		var teams []common.Address
   283  		for {
   284  			if address := w.readAddress(); address != nil {
   285  				teams = append(teams, *address)
   286  				continue
   287  			}
   288  			if len(teams) > 0 {
   289  				break
   290  			}
   291  		}
   292  		fmt.Println()
   293  		fmt.Println("How many require for confirm tx in Team MultiSignWallet? (default = 2)")
   294  		required = int64(w.readDefaultInt(2))
   295  
   296  		// MultiSigWallet.
   297  		multiSignWalletTeamAddr, _, err := multiSignWalletContract.DeployMultiSigWallet(transactOpts, contractBackend, teams, big.NewInt(required))
   298  		if err != nil {
   299  			fmt.Println("Can't deploy MultiSignWallet SMC")
   300  		}
   301  		contractBackend.Commit()
   302  		code, _ = contractBackend.CodeAt(ctx, multiSignWalletTeamAddr, nil)
   303  		storage = make(map[common.Hash]common.Hash)
   304  		contractBackend.ForEachStorageAt(ctx, multiSignWalletTeamAddr, nil, f)
   305  		// Team balance.
   306  		balance := big.NewInt(0) // 20 billion
   307          balance.Add(balance, big.NewInt(30*1000*1000))
   308          balance.Mul(balance, big.NewInt(1000000000000000000))
   309          subBalance := big.NewInt(0) // i * 50k
   310          subBalance.Add(subBalance, big.NewInt(int64(len(signers))*10*1000*1000))
   311          subBalance.Mul(subBalance, big.NewInt(1000000000000000000))
   312          balance.Sub(balance, subBalance) // 12m - i * 50k
   313          genesis.Alloc[common.HexToAddress(common.TeamAddr)] = core.GenesisAccount{
   314  			Balance: balance,
   315  			Code:    code,
   316  			Storage: storage,
   317  		}
   318  
   319  		fmt.Println()
   320  		fmt.Println("What is swap wallet address for fund 37.47Billion XDC?")
   321  		swapAddr := *w.readAddress()
   322  		baseBalance := big.NewInt(0) // 14.5Billion
   323  		baseBalance.Add(baseBalance, big.NewInt(3747*1000*1000*10))
   324  		baseBalance.Mul(baseBalance, big.NewInt(1000000000000000000))
   325  		genesis.Alloc[swapAddr] = core.GenesisAccount{
   326  			Balance: baseBalance,
   327  		}
   328  
   329  	default:
   330  		log.Crit("Invalid consensus engine choice", "choice", choice)
   331  	}
   332  	// Consensus all set, just ask for initial funds and go
   333  	fmt.Println()
   334  	fmt.Println("Which accounts should be pre-funded? (advisable at least one)")
   335  	for {
   336  		// Read the address of the account to fund
   337  		if address := w.readAddress(); address != nil {
   338  			genesis.Alloc[*address] = core.GenesisAccount{
   339  				Balance: new(big.Int).Lsh(big.NewInt(1), 256-7), // 2^256 / 128 (allow many pre-funds without balance overflows)
   340  			}
   341  			continue
   342  		}
   343  		break
   344  	}
   345  	// Add a batch of precompile balances to avoid them getting deleted
   346  	for i := int64(0); i < 2; i++ {
   347  		genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(0)}
   348  	}
   349  	// Query the user for some custom extras
   350  	fmt.Println()
   351  	fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
   352  	genesis.Config.ChainID = new(big.Int).SetUint64(uint64(w.readDefaultInt(rand.Intn(65536))))
   353  
   354  	// All done, store the genesis and flush to disk
   355  	log.Info("Configured new genesis block")
   356  
   357  	w.conf.Genesis = genesis
   358  	w.conf.flush()
   359  }
   360  
   361  // importGenesis imports a Geth genesis spec into puppeth.
   362  func (w *wizard) importGenesis() {
   363  	// Request the genesis JSON spec URL from the user
   364  	fmt.Println()
   365  	fmt.Println("Where's the genesis file? (local file or http/https url)")
   366  	url := w.readURL()
   367  
   368  	// Convert the various allowed URLs to a reader stream
   369  	var reader io.Reader
   370  
   371  	switch url.Scheme {
   372  	case "http", "https":
   373  		// Remote web URL, retrieve it via an HTTP client
   374  		res, err := http.Get(url.String())
   375  		if err != nil {
   376  			log.Error("Failed to retrieve remote genesis", "err", err)
   377  			return
   378  		}
   379  		defer res.Body.Close()
   380  		reader = res.Body
   381  
   382  	case "":
   383  		// Schemaless URL, interpret as a local file
   384  		file, err := os.Open(url.String())
   385  		if err != nil {
   386  			log.Error("Failed to open local genesis", "err", err)
   387  			return
   388  		}
   389  		defer file.Close()
   390  		reader = file
   391  
   392  	default:
   393  		log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme)
   394  		return
   395  	}
   396  	// Parse the genesis file and inject it successful
   397  	var genesis core.Genesis
   398  	if err := json.NewDecoder(reader).Decode(&genesis); err != nil {
   399  		log.Error("Invalid genesis spec: %v", err)
   400  		return
   401  	}
   402  	log.Info("Imported genesis block")
   403  
   404  	w.conf.Genesis = &genesis
   405  	w.conf.flush()
   406  }
   407  
   408  // manageGenesis permits the modification of chain configuration parameters in
   409  // a genesis config and the export of the entire genesis spec.
   410  func (w *wizard) manageGenesis() {
   411  	// Figure out whether to modify or export the genesis
   412  	fmt.Println()
   413  	fmt.Println(" 1. Modify existing fork rules")
   414  	fmt.Println(" 2. Export genesis configurations")
   415  	fmt.Println(" 3. Remove genesis configuration")
   416  
   417  	choice := w.read()
   418  	switch choice {
   419  	case "1":
   420  		// Fork rule updating requested, iterate over each fork
   421  		fmt.Println()
   422  		fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock)
   423  		w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock)
   424  
   425  		fmt.Println()
   426  		fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
   427  		w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block)
   428  
   429  		fmt.Println()
   430  		fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
   431  		w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block)
   432  
   433  		fmt.Println()
   434  		fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
   435  		w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block)
   436  
   437  		fmt.Println()
   438  		fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock)
   439  		w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock)
   440  
   441  		fmt.Println()
   442  		fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
   443  		w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
   444  		if w.conf.Genesis.Config.PetersburgBlock == nil {
   445  			w.conf.Genesis.Config.PetersburgBlock = w.conf.Genesis.Config.ConstantinopleBlock
   446  		}
   447  		fmt.Println()
   448  		fmt.Printf("Which block should Constantinople-Fix (remove EIP-1283) come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock)
   449  		w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock)
   450  
   451  		out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", "  ")
   452  		fmt.Printf("Chain configuration updated:\n\n%s\n", out)
   453  
   454  		w.conf.flush()
   455  
   456  	case "2":
   457  		// Save whatever genesis configuration we currently have
   458  		fmt.Println()
   459  		fmt.Printf("Which folder to save the genesis specs into? (default = current)\n")
   460  		fmt.Printf("  Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network)
   461  
   462  		folder := w.readDefaultString(".")
   463  		if err := os.MkdirAll(folder, 0755); err != nil {
   464  			log.Error("Failed to create spec folder", "folder", folder, "err", err)
   465  			return
   466  		}
   467  		out, _ := json.MarshalIndent(w.conf.Genesis, "", "  ")
   468  
   469  		// Export the native genesis spec used by puppeth and Geth
   470  		gethJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network))
   471  		if err := ioutil.WriteFile((gethJson), out, 0644); err != nil {
   472  			log.Error("Failed to save genesis file", "err", err)
   473  			return
   474  		}
   475  		log.Info("Saved native genesis chain spec", "path", gethJson)
   476  
   477  		// Export the genesis spec used by Aleth (formerly C++ Ethereum)
   478  		if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil {
   479  			log.Error("Failed to create Aleth chain spec", "err", err)
   480  		} else {
   481  			saveGenesis(folder, w.network, "aleth", spec)
   482  		}
   483  		// Export the genesis spec used by Parity
   484  		if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil {
   485  			log.Error("Failed to create Parity chain spec", "err", err)
   486  		} else {
   487  			saveGenesis(folder, w.network, "parity", spec)
   488  		}
   489  		// Export the genesis spec used by Harmony (formerly EthereumJ
   490  		saveGenesis(folder, w.network, "harmony", w.conf.Genesis)
   491  
   492  	case "3":
   493  		// Make sure we don't have any services running
   494  		if len(w.conf.servers()) > 0 {
   495  			log.Error("Genesis reset requires all services and servers torn down")
   496  			return
   497  		}
   498  		log.Info("Genesis block destroyed")
   499  
   500  		w.conf.Genesis = nil
   501  		w.conf.flush()
   502  	default:
   503  		log.Error("That's not something I can do")
   504  		return
   505  	}
   506  }
   507  
   508  // saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file.
   509  func saveGenesis(folder, network, client string, spec interface{}) {
   510  	path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client))
   511  
   512  	out, _ := json.Marshal(spec)
   513  	if err := ioutil.WriteFile(path, out, 0644); err != nil {
   514  		log.Error("Failed to save genesis file", "client", client, "err", err)
   515  		return
   516  	}
   517  	log.Info("Saved genesis chain spec", "client", client, "path", path)
   518  }