Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Init can Pull existing Network #18

Merged
merged 9 commits into from
Jul 28, 2023
77 changes: 77 additions & 0 deletions cmd/seda-chaind/cmd/config_defaults.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package cmd

import (
"time"

tmcfg "github.com/cometbft/cometbft/config"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
)

// initTendermintConfig helps to override default Tendermint Config values.
// return tmcfg.DefaultConfig if no custom configuration is required for the application.
func initTendermintConfig() *tmcfg.Config {
cfg := tmcfg.DefaultConfig()

// Log Settings
cfg.LogFormat = "json"
// TODO how to tell if initing a node for testnet vs mainnet vs etc.
// cfg.LogLevel

// RPC Settings
cfg.RPC.ListenAddress = "tcp://0.0.0.0:26657"

// Consensus Settings
cfg.Consensus.TimeoutPropose = time.Duration(7.5 * float64(time.Second))
cfg.Consensus.TimeoutProposeDelta = time.Duration(0)
cfg.Consensus.TimeoutCommit = time.Duration(7.5 * float64(time.Second))

return cfg
}

// initAppConfig helps to override default appConfig template and configs.
// return "", nil if no custom configuration is required for the application.
func initAppConfig() (string, interface{}) {
// The following code snippet is just for reference.

type CustomAppConfig struct {
serverconfig.Config
}

// Optionally allow the chain developer to overwrite the SDK's default
// server config.
srvCfg := serverconfig.DefaultConfig()
// The SDK's default minimum gas price is set to "" (empty value) inside
// app.toml. If left empty by validators, the node will halt on startup.
// However, the chain developer can set a default app.toml value for their
// validators here.
//
// In summary:
// - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their
// own app.toml config,
// - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their
// own app.toml to override, or use this default value.
//
// In simapp, we set the min gas prices to 0.
srvCfg.MinGasPrices = "0seda"

// GRPC settings
srvCfg.GRPC.Enable = true
srvCfg.GRPC.Address = "0.0.0.0:9090"

// GRPC Web Settings
srvCfg.GRPCWeb.Enable = true
srvCfg.GRPCWeb.Address = "0.0.0.0:9091"
srvCfg.GRPCWeb.EnableUnsafeCORS = true

// API Settings
srvCfg.API.Enable = true
srvCfg.API.Address = "tcp://0.0.0.0:1317"
srvCfg.API.EnableUnsafeCORS = true

customAppConfig := CustomAppConfig{
Config: *srvCfg,
}
customAppTemplate := serverconfig.DefaultConfigTemplate

return customAppTemplate, customAppConfig
}
272 changes: 272 additions & 0 deletions cmd/seda-chaind/cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
package cmd

import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"seda-chain/cmd/seda-chaind/utils"

cfg "github.com/cometbft/cometbft/config"
"github.com/cometbft/cometbft/libs/cli"
"github.com/cometbft/cometbft/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/go-bip39"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

const (
// FlagOverwrite defines a flag to overwrite an existing genesis JSON file.
FlagOverwrite = "overwrite"
// FlagSeed defines a flag to initialize the private validator key from a specific seed.
FlagRecover = "recover"

// Default things
BondDenom = "seda"
ChainID = "sedachain"
)

type printInfo struct {
Moniker string `json:"moniker" yaml:"moniker"`
ChainID string `json:"chain_id" yaml:"chain_id"`
NodeID string `json:"node_id" yaml:"node_id"`
GenTxsDir string `json:"gentxs_dir" yaml:"gentxs_dir"`
AppMessage json.RawMessage `json:"app_message" yaml:"app_message"`
}

func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo {
return printInfo{
Moniker: moniker,
ChainID: chainID,
NodeID: nodeID,
GenTxsDir: genTxsDir,
AppMessage: appMessage,
}
}

func displayInfo(info printInfo) error {
out, err := json.MarshalIndent(info, "", " ")
if err != nil {
return err
}

_, err = fmt.Fprintf(os.Stderr, "%s\n", sdk.MustSortJSON(out))

return err
}

func validateOrGenerateMnemonic(recover bool, cmd *cobra.Command) (string, error) {
var mnemonic string
if recover {
inBuf := bufio.NewReader(cmd.InOrStdin())
value, err := input.GetString("Enter your bip39 mnemonic", inBuf)
if err != nil {
return "", err
}

mnemonic = value
if !bip39.IsMnemonicValid(mnemonic) {
return "", errors.New("invalid mnemonic")
}
}

return mnemonic, nil
}

// preserve old logic for if we want to create a new network
// though its slightly modified to set default settings.
func newNetworkCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "new [moniker]",
Short: "Initialize private validator, p2p, genesis, and application configuration files",
Long: `Initialize validators's and node's configuration files.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
cdc := clientCtx.Codec

serverCtx := server.GetServerContextFromCmd(cmd)
config := serverCtx.Config
config.SetRoot(clientCtx.HomeDir)

recover, _ := cmd.Flags().GetBool(FlagRecover)
mnemonic, err := validateOrGenerateMnemonic(recover, cmd)
if err != nil {
return err
}

// Get initial height
initHeight, _ := cmd.Flags().GetInt64(flags.FlagInitHeight)
if initHeight < 1 {
initHeight = 1
}

nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic)
if err != nil {
return err
}

config.Moniker = args[0]

overwrite, _ := cmd.Flags().GetBool(FlagOverwrite)
genFile := config.GenesisFile()
// use os.Stat to check if the file exists
_, err = os.Stat(genFile)
if !overwrite && !os.IsNotExist(err) {
return fmt.Errorf("genesis.json file already exists: %v", genFile)
}

sdk.DefaultBondDenom = BondDenom
appGenState := mbm.DefaultGenesis(cdc)

appState, err := json.MarshalIndent(appGenState, "", " ")
if err != nil {
return errors.Wrap(err, "Failed to marshal default genesis state")
}

genDoc := &types.GenesisDoc{}
if _, err := os.Stat(genFile); err != nil {
if !os.IsNotExist(err) {
return err
}
} else {
genDoc, err = types.GenesisDocFromFile(genFile)
if err != nil {
return errors.Wrap(err, "Failed to read genesis doc from file")
}
}

genDoc.ChainID = ChainID
genDoc.Validators = nil
genDoc.AppState = appState
genDoc.InitialHeight = initHeight

if err = genutil.ExportGenesisFile(genDoc, genFile); err != nil {
return errors.Wrap(err, "Failed to export genesis file")
}
toPrint := newPrintInfo(config.Moniker, ChainID, nodeID, "", appState)
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
return displayInfo(toPrint)

},
}

cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory")
cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().Bool(FlagRecover, false, "provide seed phrase to recover existing key instead of creating")
cmd.Flags().Int64(flags.FlagInitHeight, 1, "specify the initial block height at genesis")

return cmd
}

func joinNetwork(network, configDir, genesisFilePath, mnemonic string, config *cfg.Config) error {
err := utils.DownloadGitFiles(network, configDir)
if err != nil {
return errors.Wrapf(err, "failed to download network `%s` genesis files", network)
}

bytes, err := ioutil.ReadFile(genesisFilePath)
if err != nil {
return err
}

var genesisExistingState map[string]json.RawMessage
err = json.Unmarshal(bytes, &genesisExistingState)
if err != nil {
return err
}

genesisState, err := json.MarshalIndent(genesisExistingState, "", " ")
if err != nil {
return errors.Wrapf(err, "Failed to marshal network `%s` genesis state", network)
}

nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic)
if err != nil {
return err
}

toPrint := newPrintInfo(config.Moniker, ChainID, nodeID, "", genesisState)

return displayInfo(toPrint)
}

func existingNetworkComand(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "network [moniker]",
Short: "Grabs an existing network genesis configuration.",
Long: `Initialize validators's and node's configuration files from an existing configuration.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
serverCtx := server.GetServerContextFromCmd(cmd)
config := serverCtx.Config

recover, _ := cmd.Flags().GetBool(FlagRecover)
mnemonic, err := validateOrGenerateMnemonic(recover, cmd)
if err != nil {
return err
}

// get the value of the network flag
network, _ := cmd.Flags().GetString("network")
overwrite, _ := cmd.Flags().GetBool(FlagOverwrite)
configDir := filepath.Join(config.RootDir, "config")
genesisFilePath := filepath.Join(configDir, "genesis.json")
// use os.Stat to check if the file exists
_, err = os.Stat(genesisFilePath)
if !overwrite && !os.IsNotExist(err) {
return fmt.Errorf("genesis.json file already exists: %v", genesisFilePath)
}

// If we are overwriting the genesis make sure to remove gentx folder
// this is in case they are switching to a different network
if overwrite {
gentxDir := filepath.Join(configDir, "gentx")
err = os.RemoveAll(gentxDir)
if err != nil {
return err
}
}

// TODO should turn the insides here into a function for when we have more than one network
switch network {
case "devnet":
return joinNetwork(network, configDir, genesisFilePath, mnemonic, config)
default:
return fmt.Errorf("unsupported network type: %s", network)
}
},
}

cmd.Flags().Bool(FlagRecover, false, "provide seed phrase to recover existing key instead of creating")
cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().StringP("network", "n", "devnet", "Specify the type of network to initialize (e.g., 'mainnet', 'testnet', 'devnet')")

return cmd

}

// InitCmd returns a command that initializes all files needed for Tendermint
// and the respective application.
func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "init <new | newtwork> [moniker]",
Short: "Initialize private validator, p2p, genesis, and application configuration files",
Long: `Initialize validators's and node's configuration files.`,
Args: cobra.ExactArgs(1),
}

cmd.AddCommand(newNetworkCmd(mbm, defaultNodeHome))
cmd.AddCommand(existingNetworkComand(mbm, defaultNodeHome))

return cmd
}
Loading