diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3c294210..b198a1b3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: hooks: - id: conventional-pre-commit stages: [commit-msg] - args: [feat, fix, ci, chore, test, docs] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] + args: [feat, fix, ci, chore, test, docs, refactor] # optional: list of Conventional Commits types to allow e.g. [feat, fix, ci, chore, test] - repo: local hooks: - id: goimports-reviser diff --git a/cmd/config/config.go b/cmd/config/config.go index 6c596eee..6929e6fc 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -4,7 +4,6 @@ import ( "github.com/spf13/cobra" "github.com/dymensionxyz/roller/cmd/config/export" - configInit "github.com/dymensionxyz/roller/cmd/config/init" "github.com/dymensionxyz/roller/cmd/config/set" "github.com/dymensionxyz/roller/cmd/config/show" ) @@ -14,7 +13,7 @@ func Cmd() *cobra.Command { Use: "config", Short: "Commands for setting up and managing rollapp configuration files.", } - cmd.AddCommand(configInit.InitCmd()) + cmd.AddCommand(show.Cmd()) cmd.AddCommand(set.Cmd()) cmd.AddCommand(export.Cmd()) diff --git a/cmd/config/init/flags.go b/cmd/config/init/flags.go index c876b0b7..048f6705 100644 --- a/cmd/config/init/flags.go +++ b/cmd/config/init/flags.go @@ -1,25 +1,24 @@ package initconfig import ( - "crypto/rand" "fmt" - "math/big" - "regexp" + "path/filepath" "strings" - "github.com/dymensionxyz/roller/version" + "github.com/spf13/cobra" "github.com/dymensionxyz/roller/cmd/consts" "github.com/dymensionxyz/roller/cmd/utils" "github.com/dymensionxyz/roller/config" - "github.com/spf13/cobra" + global_utils "github.com/dymensionxyz/roller/utils" + "github.com/dymensionxyz/roller/version" ) const ( defaultTokenSupply = "1000000000" ) -func addFlags(cmd *cobra.Command) error { +func AddFlags(cmd *cobra.Command) error { cmd.Flags(). StringP(FlagNames.HubID, "", consts.FroopylandHubName, fmt.Sprintf("The ID of the Dymension hub. %s", getAvailableHubsMessage())) cmd.Flags(). @@ -28,7 +27,7 @@ func addFlags(cmd *cobra.Command) error { StringP(FlagNames.VMType, "", string(config.EVM_ROLLAPP), "The rollapp type [evm, sdk]. Defaults to evm") cmd.Flags(). StringP(FlagNames.TokenSupply, "", defaultTokenSupply, "The total token supply of the RollApp") - cmd.Flags().BoolP(FlagNames.Interactive, "i", false, "Run roller in interactive mode") + // cmd.Flags().BoolP(FlagNames.Interactive, "i", false, "Run roller in interactive mode") cmd.Flags().BoolP(FlagNames.NoOutput, "", false, "Run init without any output") cmd.Flags().UintP(FlagNames.Decimals, "", 18, "The precision level of the RollApp's token defined by the number of decimal places. "+ @@ -47,36 +46,49 @@ func addFlags(cmd *cobra.Command) error { func GetInitConfig(initCmd *cobra.Command, args []string) (*config.RollappConfig, error) { home := initCmd.Flag(utils.FlagNames.Home).Value.String() - interactive, _ := initCmd.Flags().GetBool(FlagNames.Interactive) + rollerConfigFilePath := filepath.Join(home, "roller.toml") + // interactive, _ := initCmd.Flags().GetBool(FlagNames.Interactive) + raType, err := global_utils.GetKeyFromTomlFile(rollerConfigFilePath, "execution") + if err != nil { + return nil, err + } + raID, err := global_utils.GetKeyFromTomlFile(rollerConfigFilePath, "rollapp_id") + if err != nil { + return nil, err + } + raBaseDenom, err := global_utils.GetKeyFromTomlFile(rollerConfigFilePath, "base_denom") + if err != nil { + return nil, err + } + da, err := global_utils.GetKeyFromTomlFile(rollerConfigFilePath, "da") + if err != nil { + return nil, err + } + + fmt.Println(home, raType, raID, raBaseDenom, da) // load initial config if exists var cfg config.RollappConfig // load from flags cfg.Home = home - cfg.RollappBinary = initCmd.Flag(FlagNames.RollappBinary).Value.String() - cfg.VMType = config.VMType(initCmd.Flag(FlagNames.VMType).Value.String()) - cfg.TokenSupply = initCmd.Flag(FlagNames.TokenSupply).Value.String() - decimals, _ := initCmd.Flags().GetUint(FlagNames.Decimals) - cfg.Decimals = decimals - cfg.DA = config.DAType(strings.ToLower(initCmd.Flag(FlagNames.DAType).Value.String())) + + // TODO: support wasm, make the bainry name generic, like 'rollappd' + // for both RollApp types + cfg.RollappBinary = consts.Executables.RollappEVM + cfg.VMType = config.VMType(raType) + // token supply is provided in the pre-created genesis + // cfg.TokenSupply = initCmd.Flag(FlagNames.TokenSupply).Value.String() + // decimals, _ := initCmd.Flags().GetUint(FlagNames.Decimals) + cfg.Decimals = 18 + cfg.DA = config.DAType(strings.ToLower(da)) + hubID := initCmd.Flag(FlagNames.HubID).Value.String() if hub, ok := consts.Hubs[hubID]; ok { cfg.HubData = hub } cfg.RollerVersion = version.TrimVersionStr(version.BuildVersion) - - if len(args) > 0 { - cfg.RollappID = args[0] - } - if len(args) > 1 { - cfg.Denom = "a" + args[1] - } - - if interactive { - if err := RunInteractiveMode(&cfg); err != nil { - return nil, err - } - } + cfg.RollappID = raID + cfg.Denom = raBaseDenom return formatBaseCfg(cfg, initCmd) } @@ -86,41 +98,37 @@ func formatBaseCfg( initCmd *cobra.Command, ) (*config.RollappConfig, error) { setDecimals(initCmd, &cfg) - formattedRollappId, err := generateRollappId(cfg) - if err != nil { - return nil, err - } - cfg.RollappID = formattedRollappId return &cfg, nil } -func generateRollappId(rlpCfg config.RollappConfig) (string, error) { - for { - RandEthId, err := generateRandEthId() - if err != nil { - return "", err - } - if rlpCfg.HubData.ID == consts.LocalHubID { - return fmt.Sprintf("%s_%s-1", rlpCfg.RollappID, RandEthId), nil - } - isUnique, err := isEthIdentifierUnique(RandEthId, rlpCfg) - if err != nil { - return "", err - } - if isUnique { - return fmt.Sprintf("%s_%s-1", rlpCfg.RollappID, RandEthId), nil - } - } -} - -func generateRandEthId() (string, error) { - max := big.NewInt(9000000) - n, err := rand.Int(rand.Reader, max) - if err != nil { - return "", err - } - return fmt.Sprintf("%d", n), nil -} +// there's no need for a custom chain id as the rollapp id is set via the genesis-creator +// func generateRollappId(rlpCfg config.RollappConfig) (string, error) { +// for { +// RandEthId, err := generateRandEthId() +// if err != nil { +// return "", err +// } +// if rlpCfg.HubData.ID == consts.LocalHubID { +// return fmt.Sprintf("%s_%s-1", rlpCfg.RollappID, RandEthId), nil +// } +// isUnique, err := isEthIdentifierUnique(RandEthId, rlpCfg) +// if err != nil { +// return "", err +// } +// if isUnique { +// return fmt.Sprintf("%s_%s-1", rlpCfg.RollappID, RandEthId), nil +// } +// } +// } + +// func generateRandEthId() (string, error) { +// max := big.NewInt(9000000) +// n, err := rand.Int(rand.Reader, max) +// if err != nil { +// return "", err +// } +// return fmt.Sprintf("%d", n), nil +// } func setDecimals(initCmd *cobra.Command, cfg *config.RollappConfig) { decimals, _ := initCmd.Flags().GetUint(FlagNames.Decimals) @@ -140,7 +148,7 @@ func getAvailableHubsMessage() string { ) } -func isLowercaseAlphabetical(s string) bool { - match, _ := regexp.MatchString("^[a-z]+$", s) - return match -} +// func isLowercaseAlphabetical(s string) bool { +// match, _ := regexp.MatchString("^[a-z]+$", s) +// return match +// } diff --git a/cmd/config/init/init.go b/cmd/config/init/init.go deleted file mode 100644 index 9885af11..00000000 --- a/cmd/config/init/init.go +++ /dev/null @@ -1,179 +0,0 @@ -package initconfig - -import ( - "fmt" - "os" - - "github.com/dymensionxyz/roller/relayer" - global_utils "github.com/dymensionxyz/roller/utils" - - "github.com/dymensionxyz/roller/cmd/consts" - "github.com/dymensionxyz/roller/cmd/utils" - "github.com/dymensionxyz/roller/config" - datalayer "github.com/dymensionxyz/roller/data_layer" - "github.com/spf13/cobra" -) - -func InitCmd() *cobra.Command { - initCmd := &cobra.Command{ - Use: "init | --interactive", - Short: "Initialize a RollApp configuration on your local machine.", - Long: "Initialize a RollApp configuration on your local machine\n" + requiredFlagsUsage(), - Example: `init mars btc`, - PreRunE: func(cmd *cobra.Command, args []string) error { - interactive, _ := cmd.Flags().GetBool(FlagNames.Interactive) - if interactive { - return nil - } - - if len(args) < 2 { - return fmt.Errorf("invalid number of arguments. Expected 2, got %d", len(args)) - } - - if !isLowercaseAlphabetical(args[0]) { - return fmt.Errorf("invalid rollapp id %s. %s", args[0], validRollappIDMsg) - } - - // TODO: parse the config here instead of GetInitConfig in Run command - // cmd.SetContextValue("mydata", data) - - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - utils.PrettifyErrorIfExists(runInit(cmd, args)) - }, - } - - if err := addFlags(initCmd); err != nil { - panic(err) - } - - return initCmd -} - -const validRollappIDMsg = "A valid RollApp ID should contain only lowercase alphabetical characters, for example, 'mars', 'venus', 'earth', etc." - -func requiredFlagsUsage() string { - return fmt.Sprintf(` -%s - -A valid denom should consist of 3-6 English alphabet letters, for example, 'btc', 'eth', 'pepe', etc.`, validRollappIDMsg) -} - -func runInit(cmd *cobra.Command, args []string) error { - noOutput, err := cmd.Flags().GetBool(FlagNames.NoOutput) - if err != nil { - return err - } - initConfigPtr, err := GetInitConfig(cmd, args) - if err != nil { - return err - } - initConfig := *initConfigPtr - outputHandler := NewOutputHandler(noOutput) - defer outputHandler.StopSpinner() - utils.RunOnInterrupt(outputHandler.StopSpinner) - outputHandler.StartSpinner(consts.SpinnerMsgs.UniqueIdVerification) - err = initConfig.Validate() - if err != nil { - return err - } - isRootExist, err := global_utils.DirNotEmpty(initConfig.Home) - if err != nil { - return err - } - if isRootExist { - outputHandler.StopSpinner() - shouldOverwrite, err := outputHandler.PromptOverwriteConfig(initConfig.Home) - if err != nil { - return err - } - if shouldOverwrite { - err = os.RemoveAll(initConfig.Home) - if err != nil { - return err - } - } else { - os.Exit(0) - } - } - // nolint:gofumpt - err = os.MkdirAll(initConfig.Home, 0755) - if err != nil { - return err - } - // TODO: create all dirs here - outputHandler.StartSpinner(" Initializing RollApp configuration files...") - /* ---------------------------- Initialize relayer --------------------------- */ - rollappPrefix, err := utils.GetAddressPrefix(initConfig.RollappBinary) - utils.PrettifyErrorIfExists(err) - err = initializeRelayerConfig(relayer.ChainConfig{ - ID: initConfig.RollappID, - RPC: consts.DefaultRollappRPC, - Denom: initConfig.Denom, - AddressPrefix: rollappPrefix, - GasPrices: "0", - }, relayer.ChainConfig{ - ID: initConfig.HubData.ID, - RPC: initConfig.HubData.ARCHIVE_RPC_URL, - Denom: consts.Denoms.Hub, - AddressPrefix: consts.AddressPrefixes.Hub, - GasPrices: initConfig.HubData.GAS_PRICE, - }, initConfig) - if err != nil { - return err - } - - /* ------------------------------ Generate keys ----------------------------- */ - addresses, err := generateKeys(initConfig) - if err != nil { - return err - } - - /* ------------------------ Initialize DA light node ------------------------ */ - damanager := datalayer.NewDAManager(initConfig.DA, initConfig.Home) - err = damanager.InitializeLightNodeConfig() - if err != nil { - return err - } - daAddress, err := damanager.GetDAAccountAddress() - if err != nil { - return err - } - - if daAddress != "" { - addresses = append(addresses, utils.AddressData{ - Name: damanager.GetKeyName(), - Addr: daAddress, - }) - } - - /* --------------------------- Initialize Rollapp -------------------------- */ - err = initializeRollappConfig(initConfig) - if err != nil { - return err - } - - err = initializeRollappGenesis(initConfig) - if err != nil { - return err - } - - err = config.WriteConfigToTOML(initConfig) - if err != nil { - return err - } - - /* ------------------------------ Initialize Local Hub ---------------------------- */ - hub := cmd.Flag(FlagNames.HubID).Value.String() - if hub == consts.LocalHubName { - err := initLocalHub(initConfig) - utils.PrettifyErrorIfExists(err) - } - - /* ------------------------------ Print output ------------------------------ */ - outputHandler.StopSpinner() - outputHandler.printInitOutput(initConfig, addresses, initConfig.RollappID) - - return nil -} diff --git a/cmd/config/init/interactive.go b/cmd/config/init/interactive.go deleted file mode 100644 index 959d95bf..00000000 --- a/cmd/config/init/interactive.go +++ /dev/null @@ -1,144 +0,0 @@ -package initconfig - -import ( - "fmt" - "os" - "strings" - - "github.com/dymensionxyz/roller/cmd/consts" - "github.com/dymensionxyz/roller/config" - "github.com/manifoldco/promptui" -) - -func RunInteractiveMode(cfg *config.RollappConfig) error { - promptNetwork := promptui.Select{ - Label: "Select your network", - Items: []string{"froopyland", "devnet", "local"}, - CursorPos: func() int { - switch cfg.HubData.ID { - case consts.Hubs[consts.FroopylandHubName].ID: - return 0 - case consts.Hubs[consts.StagingHubName].ID: - return 1 - case consts.Hubs[consts.LocalHubName].ID: - return 2 - default: - return 0 - } - }(), - } - _, mode, err := promptNetwork.Run() - if err != nil { - return err - } - cfg.HubData = consts.Hubs[mode] - - promptExecutionEnv := promptui.Select{ - Label: "Choose your rollapp execution environment", - Items: []string{"EVM rollapp", "custom EVM rollapp", "custom non-EVM rollapp"}, - CursorPos: func() int { - if cfg.RollappBinary == consts.Executables.RollappEVM { - return 0 - } else if cfg.VMType == config.EVM_ROLLAPP { - return 1 - } else { - return 2 - } - }(), - } - _, env, err := promptExecutionEnv.Run() - if err != nil { - return err - } - - if env == "custom non-EVM rollapp" { - cfg.VMType = config.SDK_ROLLAPP - } else { - cfg.VMType = config.EVM_ROLLAPP - } - - // if custom binary, get the binary path - if env != "EVM rollapp" { - promptBinaryPath := promptui.Prompt{ - Label: "Set your runtime binary", - Default: cfg.RollappBinary, - AllowEdit: true, - Validate: func(s string) error { - _, err := os.Stat(s) - return err - }, - } - path, err := promptBinaryPath.Run() - if err != nil { - return err - } - cfg.RollappBinary = path - } - promptChainID := promptui.Prompt{ - Label: "Enter your RollApp ID", - Default: strings.Split(cfg.RollappID, "_")[0], - AllowEdit: true, - } - for { - rollappID, err := promptChainID.Run() - if err != nil { - return err - } - if !isLowercaseAlphabetical(rollappID) { - fmt.Printf("invalid rollapp id %s. %s\n", rollappID, validRollappIDMsg) - continue - } - cfg.RollappID = rollappID - break - } - - promptDenom := promptui.Prompt{ - Label: "Specify your RollApp denom", - Default: strings.TrimPrefix(cfg.Denom, "a"), - AllowEdit: true, - Validate: func(s string) error { - if !config.IsValidTokenSymbol(s) { - return fmt.Errorf("invalid token symbol") - } - return nil - }, - } - denom, err := promptDenom.Run() - if err != nil { - return err - } - cfg.Denom = "a" + denom - - promptTokenSupply := promptui.Prompt{ - Label: "How many " + denom + " tokens do you wish to mint for Genesis?", - Default: cfg.TokenSupply, - Validate: config.VerifyTokenSupply, - } - supply, err := promptTokenSupply.Run() - if err != nil { - return err - } - cfg.TokenSupply = supply - - availableDAs := []config.DAType{config.Celestia, config.Avail, config.Local} - promptDAType := promptui.Select{ - Label: "Choose your data layer", - Items: availableDAs, - CursorPos: func() int { - switch cfg.DA { - case config.Celestia: - return 0 - case config.Avail: - return 1 - case config.Local: - return 2 - default: - return 0 - } - }(), - } - _, da, _ := promptDAType.Run() - cfg.DA = config.DAType(strings.ToLower(da)) - - return nil -} diff --git a/cmd/config/init/keys.go b/cmd/config/init/keys.go index 68e6017a..1a39f325 100644 --- a/cmd/config/init/keys.go +++ b/cmd/config/init/keys.go @@ -1,8 +1,8 @@ package initconfig import ( + "fmt" "os/exec" - "path" "path/filepath" "github.com/dymensionxyz/roller/cmd/consts" @@ -10,16 +10,18 @@ import ( "github.com/dymensionxyz/roller/config" ) -func generateKeys(rollappConfig config.RollappConfig) ([]utils.AddressData, error) { +func GenerateKeys(rollappConfig config.RollappConfig) ([]utils.AddressData, error) { sequencerAddresses, err := generateSequencersKeys(rollappConfig) if err != nil { + fmt.Println("failed to generate sequencerAddresses") return nil, err } - relayerAddresses, err := generateRelayerKeys(rollappConfig) - if err != nil { - return nil, err - } - return append(sequencerAddresses, relayerAddresses...), nil + // relayerAddresses, err := generateRelayerKeys(rollappConfig) + // if err != nil { + // return nil, err + // } + fmt.Println(sequencerAddresses) + return sequencerAddresses, nil } func generateSequencersKeys(initConfig config.RollappConfig) ([]utils.AddressData, error) { @@ -58,22 +60,22 @@ func getSequencerKeysConfig(rollappConfig config.RollappConfig) []utils.KeyConfi } } -func getRelayerKeysConfig(rollappConfig config.RollappConfig) map[string]utils.KeyConfig { - return map[string]utils.KeyConfig{ - consts.KeysIds.RollappRelayer: { - Dir: path.Join(rollappConfig.Home, consts.ConfigDirName.Relayer), - ID: consts.KeysIds.RollappRelayer, - ChainBinary: rollappConfig.RollappBinary, - Type: rollappConfig.VMType, - }, - consts.KeysIds.HubRelayer: { - Dir: path.Join(rollappConfig.Home, consts.ConfigDirName.Relayer), - ID: consts.KeysIds.HubRelayer, - ChainBinary: consts.Executables.Dymension, - Type: config.SDK_ROLLAPP, - }, - } -} +// func getRelayerKeysConfig(rollappConfig config.RollappConfig) map[string]utils.KeyConfig { +// return map[string]utils.KeyConfig{ +// consts.KeysIds.RollappRelayer: { +// Dir: path.Join(rollappConfig.Home, consts.ConfigDirName.Relayer), +// ID: consts.KeysIds.RollappRelayer, +// ChainBinary: rollappConfig.RollappBinary, +// Type: rollappConfig.VMType, +// }, +// consts.KeysIds.HubRelayer: { +// Dir: path.Join(rollappConfig.Home, consts.ConfigDirName.Relayer), +// ID: consts.KeysIds.HubRelayer, +// ChainBinary: consts.Executables.Dymension, +// Type: config.SDK_ROLLAPP, +// }, +// } +// } func createAddressBinary(keyConfig utils.KeyConfig, home string) (string, error) { args := []string{ @@ -89,55 +91,55 @@ func createAddressBinary(keyConfig utils.KeyConfig, home string) (string, error) return utils.ParseAddressFromOutput(out) } -func generateRelayerKeys(rollappConfig config.RollappConfig) ([]utils.AddressData, error) { - relayerAddresses := make([]utils.AddressData, 0) - keys := getRelayerKeysConfig(rollappConfig) - createRollappKeyCmd := getAddRlyKeyCmd( - keys[consts.KeysIds.RollappRelayer], - rollappConfig.RollappID, - ) - createHubKeyCmd := getAddRlyKeyCmd(keys[consts.KeysIds.HubRelayer], rollappConfig.HubData.ID) - out, err := utils.ExecBashCommandWithStdout(createRollappKeyCmd) - if err != nil { - return nil, err - } - relayerRollappAddress, err := utils.ParseAddressFromOutput(out) - if err != nil { - return nil, err - } - relayerAddresses = append(relayerAddresses, utils.AddressData{ - Addr: relayerRollappAddress, - Name: consts.KeysIds.RollappRelayer, - }) - out, err = utils.ExecBashCommandWithStdout(createHubKeyCmd) - if err != nil { - return nil, err - } - relayerHubAddress, err := utils.ParseAddressFromOutput(out) - if err != nil { - return nil, err - } - relayerAddresses = append(relayerAddresses, utils.AddressData{ - Addr: relayerHubAddress, - Name: consts.KeysIds.HubRelayer, - }) - return relayerAddresses, err -} +// func generateRelayerKeys(rollappConfig config.RollappConfig) ([]utils.AddressData, error) { +// relayerAddresses := make([]utils.AddressData, 0) +// keys := getRelayerKeysConfig(rollappConfig) +// createRollappKeyCmd := getAddRlyKeyCmd( +// keys[consts.KeysIds.RollappRelayer], +// rollappConfig.RollappID, +// ) +// createHubKeyCmd := getAddRlyKeyCmd(keys[consts.KeysIds.HubRelayer], rollappConfig.HubData.ID) +// out, err := utils.ExecBashCommandWithStdout(createRollappKeyCmd) +// if err != nil { +// return nil, err +// } +// relayerRollappAddress, err := utils.ParseAddressFromOutput(out) +// if err != nil { +// return nil, err +// } +// relayerAddresses = append(relayerAddresses, utils.AddressData{ +// Addr: relayerRollappAddress, +// Name: consts.KeysIds.RollappRelayer, +// }) +// out, err = utils.ExecBashCommandWithStdout(createHubKeyCmd) +// if err != nil { +// return nil, err +// } +// relayerHubAddress, err := utils.ParseAddressFromOutput(out) +// if err != nil { +// return nil, err +// } +// relayerAddresses = append(relayerAddresses, utils.AddressData{ +// Addr: relayerHubAddress, +// Name: consts.KeysIds.HubRelayer, +// }) +// return relayerAddresses, err +// } -func getAddRlyKeyCmd(keyConfig utils.KeyConfig, chainID string) *exec.Cmd { - coinType := "118" - if keyConfig.Type == config.EVM_ROLLAPP { - coinType = "60" - } - return exec.Command( - consts.Executables.Relayer, - "keys", - "add", - chainID, - keyConfig.ID, - "--home", - keyConfig.Dir, - "--coin-type", - coinType, - ) -} +// func getAddRlyKeyCmd(keyConfig utils.KeyConfig, chainID string) *exec.Cmd { +// coinType := "118" +// if keyConfig.Type == config.EVM_ROLLAPP { +// coinType = "60" +// } +// return exec.Command( +// consts.Executables.Relayer, +// "keys", +// "add", +// chainID, +// keyConfig.ID, +// "--home", +// keyConfig.Dir, +// "--coin-type", +// coinType, +// ) +// } diff --git a/cmd/config/init/output.go b/cmd/config/init/output.go index eca27319..5a1753a6 100644 --- a/cmd/config/init/output.go +++ b/cmd/config/init/output.go @@ -17,7 +17,7 @@ func NewOutputHandler(noOutput bool) *OutputHandler { } } -func (o *OutputHandler) printInitOutput( +func (o *OutputHandler) PrintInitOutput( rollappConfig config.RollappConfig, addresses []utils.AddressData, rollappId string, @@ -32,6 +32,7 @@ func (o *OutputHandler) printInitOutput( fmt.Println(FormatTokenSupplyLine(rollappConfig)) fmt.Println() utils.PrintAddressesWithTitle(formatAddresses(rollappConfig, addresses)) + fmt.Printf("\nšŸ”” Please fund these addresses to register and run the rollapp.\n") } diff --git a/cmd/config/init/rollapp.go b/cmd/config/init/rollapp.go index 4703791f..246ad455 100644 --- a/cmd/config/init/rollapp.go +++ b/cmd/config/init/rollapp.go @@ -1,6 +1,7 @@ package initconfig import ( + "fmt" "os/exec" "path/filepath" @@ -9,8 +10,10 @@ import ( "github.com/dymensionxyz/roller/config" ) -func initializeRollappConfig(initConfig config.RollappConfig) error { +func InitializeRollappConfig(initConfig config.RollappConfig) error { home := filepath.Join(initConfig.Home, consts.ConfigDirName.Rollapp) + fmt.Println(initConfig.RollappID) + initRollappCmd := exec.Command( initConfig.RollappBinary, "init", diff --git a/cmd/rollapp/init/main.go b/cmd/rollapp/init/main.go new file mode 100644 index 00000000..d6e21ce8 --- /dev/null +++ b/cmd/rollapp/init/main.go @@ -0,0 +1,247 @@ +package initrollapp + +import ( + "bufio" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + + initconfig "github.com/dymensionxyz/roller/cmd/config/init" + "github.com/dymensionxyz/roller/cmd/consts" + "github.com/dymensionxyz/roller/cmd/utils" + datalayer "github.com/dymensionxyz/roller/data_layer" + global_utils "github.com/dymensionxyz/roller/utils" + "github.com/dymensionxyz/roller/utils/archives" +) + +var Cmd = &cobra.Command{ + Use: "init", + Short: "Inititlize RollApp locally", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + err := initconfig.AddFlags(cmd) + if err != nil { + fmt.Println("failed to add flags") + return + } + reader := bufio.NewReader(os.Stdin) + + fmt.Println("Do you already have rollapp config? (y/n)") + resp, err := reader.ReadString('\n') + if err != nil { + fmt.Println(err) + return + } + + resp = strings.TrimSpace(resp) + resp = strings.ToLower(resp) + + if resp == "n" || resp == "no" { + fmt.Println( + `To generate a RollApp configuration file go to +or run 'rollapp config' to expose the UI on localhost:11133. +after configuration files are generated, rerun the 'init' command`, + ) + return + } + + if resp == "y" || resp == "yes" { + fmt.Println( + "provide a path to the configuration archive file downloaded from ", + ) + fp, err := reader.ReadString('\n') + if err != nil { + return + } + + fp = strings.TrimSpace(fp) + if fp == "" { + fmt.Println("no path was provided") + return + } + + archivePath, err := expandHomePath(fp) + if err != nil { + return + } + + if _, err := os.Stat(archivePath); os.IsNotExist(err) { + fmt.Printf("the file %s does not exist. \n", fp) + return + } + + err = archives.ExtractZip(archivePath) + if err != nil { + fmt.Println("failed to extract: ", err) + } + + err = runInit(cmd, args) + if err != nil { + fmt.Printf("failed to initialize the RollApp: %v\n", err) + return + } + + return + } + + fmt.Println("invalid or no input") + }, +} + +func expandHomePath(path string) (string, error) { + if path[:2] == "~/" { + usr, err := user.Current() + if err != nil { + return "", err + } + path = filepath.Join(usr.HomeDir, path[2:]) + } + return path, nil +} + +func runInit(cmd *cobra.Command, args []string) error { + fmt.Println("running init") + // noOutput, err := cmd.Flags().GetBool(initconfig.FlagNames.NoOutput) + // if err != nil { + // utils.PrettifyErrorIfExists(err) + // return err + // } + + initConfigPtr, err := initconfig.GetInitConfig(cmd, args) + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + + initConfig := *initConfigPtr + + outputHandler := initconfig.NewOutputHandler(false) + defer outputHandler.StopSpinner() + + utils.RunOnInterrupt(outputHandler.StopSpinner) + outputHandler.StartSpinner(consts.SpinnerMsgs.UniqueIdVerification) + err = initConfig.Validate() + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + + isRootExist, err := global_utils.DirNotEmpty(initConfig.Home) + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + + if isRootExist { + outputHandler.StopSpinner() + shouldOverwrite, err := outputHandler.PromptOverwriteConfig(initConfig.Home) + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + if shouldOverwrite { + err = os.RemoveAll(initConfig.Home) + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + } else { + os.Exit(0) + } + } + + // nolint:gofumpt + err = os.MkdirAll(initConfig.Home, 0755) + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + + // TODO: create all dirs here + outputHandler.StartSpinner(" Initializing RollApp configuration files...") + /* ---------------------------- Initialize relayer --------------------------- */ + // 20240607 relayer will be handled using a separate, relayer command + // rollerConfigFilePath := filepath.Join(utils.GetRollerRootDir(), "roller.toml") + // rollappPrefix, err := global_utils.GetKeyFromTomlFile(rollerConfigFilePath, "bech32_prefix") + // utils.PrettifyErrorIfExists(err) + + // err = initializeRelayerConfig(relayer.ChainConfig{ + // ID: initConfig.RollappID, + // RPC: consts.DefaultRollappRPC, + // Denom: initConfig.Denom, + // AddressPrefix: rollappPrefix, + // GasPrices: "0", + // }, relayer.ChainConfig{ + // ID: initConfig.HubData.ID, + // RPC: initConfig.HubData.ARCHIVE_RPC_URL, + // Denom: consts.Denoms.Hub, + // AddressPrefix: consts.AddressPrefixes.Hub, + // GasPrices: initConfig.HubData.GAS_PRICE, + // }, initConfig) + // if err != nil { + // return err + // } + + /* ------------------------------ Generate keys ----------------------------- */ + addresses, err := initconfig.GenerateKeys(initConfig) + if err != nil { + utils.PrettifyErrorIfExists(err) + return err + } + + /* ------------------------ Initialize DA light node ------------------------ */ + damanager := datalayer.NewDAManager(initConfig.DA, initConfig.Home) + err = damanager.InitializeLightNodeConfig() + if err != nil { + return err + } + + daAddress, err := damanager.GetDAAccountAddress() + if err != nil { + return err + } + + if daAddress != "" { + addresses = append(addresses, utils.AddressData{ + Name: damanager.GetKeyName(), + Addr: daAddress, + }) + } + + /* --------------------------- Initialize Rollapp -------------------------- */ + err = initconfig.InitializeRollappConfig(initConfig) + if err != nil { + return err + } + + // 20240607 genesis is generated using the genesis-creator + // err = initializeRollappGenesis(initConfig) + // if err != nil { + // return err + // } + + // TODO: review, roller config is generated using genesis-creator + // some of the config values should be moved there + // err = config.WriteConfigToTOML(initConfig) + // if err != nil { + // return err + // } + + /* ------------------------------ Initialize Local Hub ---------------------------- */ + // TODO: local hub is out of scope, implement as the last step + // hub := cmd.Flag(FlagNames.HubID).Value.String() + // if hub == consts.LocalHubName { + // err := initLocalHub(initConfig) + // utils.PrettifyErrorIfExists(err) + // } + + /* ------------------------------ Print output ------------------------------ */ + outputHandler.StopSpinner() + outputHandler.PrintInitOutput(initConfig, addresses, initConfig.RollappID) + + return nil +} diff --git a/cmd/rollapp/rollapp.go b/cmd/rollapp/rollapp.go new file mode 100644 index 00000000..3d08ae56 --- /dev/null +++ b/cmd/rollapp/rollapp.go @@ -0,0 +1,18 @@ +package rollapp + +import ( + "github.com/spf13/cobra" + + initrollapp "github.com/dymensionxyz/roller/cmd/rollapp/init" +) + +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "rollapp [command]", + Short: "Commands to initialize and run a RollApp", + } + + cmd.AddCommand(initrollapp.Cmd) + + return cmd +} diff --git a/cmd/root.go b/cmd/root.go index 9ffa6e1c..b1041509 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,18 +3,20 @@ package cmd import ( "os" + "github.com/spf13/cobra" + "github.com/dymensionxyz/roller/cmd/config" da_light_client "github.com/dymensionxyz/roller/cmd/da-light-client" "github.com/dymensionxyz/roller/cmd/keys" "github.com/dymensionxyz/roller/cmd/migrate" "github.com/dymensionxyz/roller/cmd/relayer" + "github.com/dymensionxyz/roller/cmd/rollapp" "github.com/dymensionxyz/roller/cmd/run" "github.com/dymensionxyz/roller/cmd/sequencer" "github.com/dymensionxyz/roller/cmd/services" "github.com/dymensionxyz/roller/cmd/tx" "github.com/dymensionxyz/roller/cmd/utils" "github.com/dymensionxyz/roller/cmd/version" - "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ @@ -44,6 +46,7 @@ func init() { rootCmd.AddCommand(migrate.Cmd()) rootCmd.AddCommand(tx.Cmd()) rootCmd.AddCommand(test()) + rootCmd.AddCommand(rollapp.Cmd()) utils.AddGlobalFlags(rootCmd) } diff --git a/test/config/init/init_test.go b/test/config/init/init_test.go deleted file mode 100644 index a5c8c7af..00000000 --- a/test/config/init/init_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package initconfig_test - -import ( - "fmt" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - - initconfig "github.com/dymensionxyz/roller/cmd/config/init" - "github.com/dymensionxyz/roller/cmd/utils" - "github.com/dymensionxyz/roller/config" - "github.com/dymensionxyz/roller/test/config/init/testutils" -) - -func TestInitCmd(t *testing.T) { - tokenSupply := "10000000" - testCases := []struct { - name string - goldenDirPath string - excludedDirs []string - optionalFlags []string - }{ - { - name: "Roller config init with default values", - goldenDirPath: "./goldens/init_without_flags", - excludedDirs: []string{"gentx"}, - optionalFlags: []string{ - "--" + initconfig.FlagNames.HubID, "local", - }, - }, - { - name: "Roller config init with custom flags", - goldenDirPath: "./goldens/init_with_flags", - excludedDirs: []string{"gentx"}, - optionalFlags: []string{ - "--" + initconfig.FlagNames.TokenSupply, tokenSupply, - "--" + initconfig.FlagNames.HubID, "local", - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert := assert.New(t) - tempDir, err := os.MkdirTemp(os.TempDir(), "test") - tempDir = filepath.Join(tempDir, ".roller") - fmt.Println(tempDir, tc.name) - assert.NoError(err) - defer func() { - err := os.RemoveAll(tempDir) - assert.NoError(err) - }() - initCmd := initconfig.InitCmd() - utils.AddGlobalFlags(initCmd) - denom := "dym" - rollappID := "mars" - initCmd.SetArgs(append([]string{ - rollappID, - denom, - "--" + utils.FlagNames.Home, tempDir, - }, tc.optionalFlags...)) - assert.NoError(initCmd.Execute()) - rlpCfg, err := config.LoadConfigFromTOML(tempDir) - assert.NoError(err) - assert.NoError(os.Remove(filepath.Join(tempDir, config.RollerConfigFileName))) - assert.NoError(testutils.VerifyRollappKeys(tempDir)) - assert.NoError( - testutils.VerifyRelayerKeys(tempDir, rlpCfg.RollappID, rlpCfg.HubData.ID), - ) - assert.NoError(testutils.VerifyCelestiaLightNodeKeys(tempDir)) - assert.NoError(testutils.SanitizeConfigDir(tempDir, &rlpCfg)) - assert.NoError(testutils.VerifyRlyConfig(rlpCfg, tc.goldenDirPath)) - }) - } -} diff --git a/utils/archives/archives.go b/utils/archives/archives.go new file mode 100644 index 00000000..448c6a6b --- /dev/null +++ b/utils/archives/archives.go @@ -0,0 +1,156 @@ +package archives + +import ( + "archive/tar" + "archive/zip" + "fmt" + "io" + "os" + "path/filepath" + "slices" + + "github.com/dymensionxyz/roller/cmd/consts" + "github.com/dymensionxyz/roller/cmd/utils" +) + +// ExtractZip function extracts the zip file created by the genesis-generator +// into a temporary directory and passes the tar archive container within +// the zip archive to ExtractTar for processing +func ExtractZip(zipFile string) error { + tmpDir, err := os.MkdirTemp("", "genesis_zip_files") + if err != nil { + return err + } + // nolint errcheck + defer os.RemoveAll(tmpDir) + + zipReader, err := zip.OpenReader(zipFile) + if err != nil { + return err + } + // nolint errcheck + defer zipReader.Close() + + var tarFilePath string + + // Iterate through the files in the ZIP archive + for _, f := range zipReader.File { + if filepath.Ext(f.Name) == ".tar" { + // nolint gosec + tarFilePath = filepath.Join(tmpDir, f.Name) + if err := extractFileFromZip(f, tarFilePath); err != nil { + return fmt.Errorf("failed to extract .tar file %s: %w", tarFilePath, err) + } + } + } + + if tarFilePath == "" { + return fmt.Errorf("no .tar file found in the zip archive") + } + + // Process the extracted .tar file + if err := ExtractTar(tarFilePath, tmpDir); err != nil { + return fmt.Errorf("failed to extract .tar file %s: %w", tarFilePath, err) + } + + return nil +} + +// ExtractTar function extracts the tar archive created by the genesis-generator +// and moves the files into the correct location +func ExtractTar(tarFile, outputDir string) error { + supportedFiles := []string{"roller.toml", "genesis.json"} + + // Open the .tar file + file, err := os.Open(tarFile) + if err != nil { + return fmt.Errorf("failed to open tar file: %w", err) + } + // nolint errcheck + defer file.Close() + + // Create a tar reader + tarReader := tar.NewReader(file) + + // Iterate through the files in the tar archive + for { + header, err := tarReader.Next() + if err == io.EOF { + break // End of archive + } + if err != nil { + return fmt.Errorf("ExtractTar: Next() failed: %w", err) + } + + if !slices.Contains(supportedFiles, header.Name) { + continue + } + + switch header.Name { + case "roller.toml": + // Create directory + filePath := filepath.Join(utils.GetRollerRootDir(), "roller.toml") + err := createFileFromArchive(filePath, tarReader) + if err != nil { + return err + } + case "genesis.json": + filePath := filepath.Join( + utils.GetRollerRootDir(), + consts.ConfigDirName.Rollapp, + "config", + "genesis.json", + ) + err := createFileFromArchive(filePath, tarReader) + if err != nil { + return err + } + } + } + + return nil +} + +func createFileFromArchive(outputPath string, tarReader *tar.Reader) error { + if err := os.MkdirAll(filepath.Dir(outputPath), 0o755); err != nil { + return fmt.Errorf("ExtractTar: MkdirAll() failed: %w", err) + } + + outFile, err := os.Create(outputPath) + if err != nil { + return fmt.Errorf("ExtractTar: Create() failed: %w", err) + } + + if _, err := io.Copy(outFile, tarReader); err != nil { + // nolint errcheck + outFile.Close() + return fmt.Errorf("ExtractTar: Copy() failed: %w", err) + } + // nolint errcheck + outFile.Close() + return nil +} + +// extractFileFromZip extracts a file from a zip archive to the specified path +func extractFileFromZip(f *zip.File, outputPath string) error { + rc, err := f.Open() + if err != nil { + return fmt.Errorf("failed to open file in zip: %w", err) + } + // nolint errcheck + defer rc.Close() + + outFile, err := os.Create(outputPath) + if err != nil { + return fmt.Errorf("failed to create output file: %w", err) + } + // nolint errcheck + defer outFile.Close() + + // nolint gosec + if _, err := io.Copy(outFile, rc); err != nil { + return fmt.Errorf("failed to copy file contents: %w", err) + } + + return nil +}