Skip to content

Commit

Permalink
feat: add interactive cli for rollapp init (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsitrin authored Jul 2, 2023
1 parent de39ccb commit a6f207a
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 82 deletions.
10 changes: 6 additions & 4 deletions cmd/config/init/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,25 @@ var FlagNames = struct {
TokenSupply string
RollappBinary string
HubID string
Interactive string
}{
TokenSupply: "token-supply",
RollappBinary: "rollapp-binary",
HubID: "hub",
Interactive: "interactive",
}

const (
StagingHubID = "internal-devnet"
StagingHubID = "devnet"
LocalHubID = "local"
)

// TODO(#112): The avaialble hub networks should be read from YAML file
var Hubs = map[string]utils.HubData{
StagingHubID: {
API_URL: "https://rest-hub-devnet.dymension.xyz",
ID: "devnet_666-1",
RPC_URL: "https://rpc-hub-devnet.dymension.xyz:443",
API_URL: "https://dymension.devnet.api.silknodes.io:443",
ID: "devnet_304-1",
RPC_URL: "https://dymension.devnet.rpc.silknodes.io:443",
},
LocalHubID: {
API_URL: "http://localhost:1318",
Expand Down
84 changes: 21 additions & 63 deletions cmd/config/init/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package initconfig

import (
"fmt"
"regexp"

"math/big"

"github.com/dymensionxyz/roller/cmd/consts"
"github.com/dymensionxyz/roller/cmd/utils"
Expand All @@ -17,84 +14,45 @@ const (

func addFlags(cmd *cobra.Command) {
cmd.Flags().StringP(FlagNames.HubID, "", StagingHubID, fmt.Sprintf("The ID of the Dymension hub. %s", getAvailableHubsMessage()))
cmd.Flags().StringP(FlagNames.RollappBinary, "", "", "The rollapp binary. Should be passed only if you built a custom rollapp")
cmd.Flags().StringP(FlagNames.RollappBinary, "", consts.Executables.RollappEVM, "The rollapp binary. Should be passed only if you built a custom rollapp")
cmd.Flags().StringP(FlagNames.TokenSupply, "", defaultTokenSupply, "The total token supply of the RollApp")
}

func getRollappBinaryPath(cmd *cobra.Command) string {
rollappBinaryPath := cmd.Flag(FlagNames.RollappBinary).Value.String()
if rollappBinaryPath == "" {
return consts.Executables.RollappEVM
}
return rollappBinaryPath
cmd.Flags().BoolP(FlagNames.Interactive, "", false, "Run roller in interactive mode")
}

func getTokenSupply(cmd *cobra.Command) string {
return cmd.Flag(FlagNames.TokenSupply).Value.String()
}

func GetInitConfig(initCmd *cobra.Command, args []string) (utils.RollappConfig, error) {
cfg := utils.RollappConfig{}
cfg.Home = initCmd.Flag(utils.FlagNames.Home).Value.String()
cfg.RollappBinary = initCmd.Flag(FlagNames.RollappBinary).Value.String()

interactive, _ := initCmd.Flags().GetBool(FlagNames.Interactive)
if interactive {
RunInteractiveMode(&cfg)
return cfg, nil
}

rollappId := args[0]
denom := args[1]
home := initCmd.Flag(utils.FlagNames.Home).Value.String()
rollappBinaryPath := getRollappBinaryPath(initCmd)

hubID := initCmd.Flag(FlagNames.HubID).Value.String()
tokenSupply := getTokenSupply(initCmd)
return utils.RollappConfig{
Home: home,
RollappID: rollappId,
RollappBinary: rollappBinaryPath,
Denom: denom,
HubData: Hubs[hubID],
TokenSupply: tokenSupply,
}, nil

cfg.RollappID = rollappId
cfg.Denom = denom
cfg.HubData = Hubs[hubID]
cfg.TokenSupply = tokenSupply

return cfg, nil
}
func getValidRollappIdMessage() string {
return "A valid RollApp ID should follow the format 'rollapp-name_EIP155-revision', where 'rollapp-name' is made up of" +
return "A valid RollApp ID should follow the format 'name_EIP155-revision', where 'name' is made up of" +
" lowercase English letters, 'EIP155-revision' is a 1 to 5 digit number representing the EIP155 rollapp ID, and '" +
"revision' is a 1 to 5 digit number representing the revision. For example: 'mars_9721-1'"
}

func getAvailableHubsMessage() string {
return fmt.Sprintf("Acceptable values are '%s' or '%s'", StagingHubID, LocalHubID)
}

func validateRollAppID(id string) bool {
pattern := `^[a-z]+_[0-9]{1,5}-[0-9]{1,5}$`
r, _ := regexp.Compile(pattern)
return r.MatchString(id)
}

func verifyHubID(cmd *cobra.Command) error {
hubID, err := cmd.Flags().GetString(FlagNames.HubID)
if err != nil {
return err
}
if _, ok := Hubs[hubID]; !ok {
return fmt.Errorf("invalid hub ID: %s. %s", hubID, getAvailableHubsMessage())
}
return nil
}

func verifyTokenSupply(cmd *cobra.Command) error {
tokenSupplyStr, err := cmd.Flags().GetString(FlagNames.TokenSupply)
if err != nil {
return err
}

tokenSupply := new(big.Int)
_, ok := tokenSupply.SetString(tokenSupplyStr, 10)
if !ok {
return fmt.Errorf("invalid token supply: %s. Must be a valid integer", tokenSupplyStr)
}

ten := big.NewInt(10)
remainder := new(big.Int)
remainder.Mod(tokenSupply, ten)

if remainder.Cmp(big.NewInt(0)) != 0 {
return fmt.Errorf("invalid token supply: %s. Must be divisible by 10", tokenSupplyStr)
}

return nil
}
31 changes: 21 additions & 10 deletions cmd/config/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"

"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/dymensionxyz/roller/cmd/consts"
"github.com/dymensionxyz/roller/cmd/utils"
"github.com/spf13/cobra"
Expand All @@ -14,23 +15,34 @@ func InitCmd() *cobra.Command {
Use: "init <chain-id> <denom>",
Short: "Initialize a RollApp configuration on your local machine.",
PreRunE: func(cmd *cobra.Command, args []string) error {
err := verifyHubID(cmd)
if err != nil {
return err
interactive, _ := cmd.Flags().GetBool(FlagNames.Interactive)
if interactive {
return nil
}
err = verifyTokenSupply(cmd)
if err != nil {
return err

if len(args) == 0 {
fmt.Println("No arguments provided. Running in interactive mode.")
cmd.Flags().Set(FlagNames.Interactive, "true")
return nil
}
rollappID := args[0]
if !validateRollAppID(rollappID) {
return fmt.Errorf("invalid RollApp ID '%s'. %s", rollappID, getValidRollappIdMessage())

if len(args) < 2 {
return fmt.Errorf("invalid number of arguments. Expected 2, got %d", len(args))
}

//TODO: parse the config here instead of GetInitConfig in Run command
// cmd.SetContextValue("mydata", data)

return nil
},
Run: func(cmd *cobra.Command, args []string) {
initConfig, err := GetInitConfig(cmd, args)
utils.PrettifyErrorIfExists(err)

err = initConfig.Validate()
err = errors.Wrap(err, getValidRollappIdMessage())
utils.PrettifyErrorIfExists(err)

utils.PrettifyErrorIfExists(VerifyUniqueRollappID(initConfig.RollappID, initConfig))
isRootExist, err := dirNotEmpty(initConfig.Home)
utils.PrettifyErrorIfExists(err)
Expand Down Expand Up @@ -81,7 +93,6 @@ func InitCmd() *cobra.Command {
/* ------------------------------ Print output ------------------------------ */
printInitOutput(addresses, initConfig.RollappID)
},
Args: cobra.ExactArgs(2),
}

addFlags(initCmd)
Expand Down
60 changes: 60 additions & 0 deletions cmd/config/init/interactive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package initconfig

import (
"fmt"

"github.com/dymensionxyz/roller/cmd/utils"
"github.com/manifoldco/promptui"
)

// TODO: return error output
func RunInteractiveMode(config *utils.RollappConfig) {
promptNetwork := promptui.Select{
Label: "Select your network",
Items: []string{"local", "devnet"},
CursorPos: 1,
}
_, mode, _ := promptNetwork.Run()
config.HubData = Hubs[mode]

promptChainID := promptui.Prompt{
Label: "Enter your RollApp ID",
Default: "myrollapp_1234-1",
AllowEdit: true,
Validate: utils.ValidateRollAppID,
}
chainID, _ := promptChainID.Run()
config.RollappID = chainID

promptDenom := promptui.Prompt{
Label: "Specify your RollApp denom",
Default: "RAX",
// TODO: add validation for denomination
}
denom, _ := promptDenom.Run()
config.Denom = denom

promptTokenSupply := promptui.Prompt{
Label: "How many " + denom + " tokens do you wish to mint for Genesis?",
Default: "1000000000",
Validate: utils.VerifyTokenSupply,
}
supply, _ := promptTokenSupply.Run()
config.TokenSupply = supply

promptDAType := promptui.Select{
Label: "Choose your data layer",
Items: []string{"Celestia", "Avail"},
}
_, _, _ = promptDAType.Run()
fmt.Println("Only Celestia supported for now")

promptBinaryPath := promptui.Prompt{
Label: "Set your runtime binary",
Default: config.RollappBinary,
AllowEdit: true,
//TODO: add validate for binary path
}
path, _ := promptBinaryPath.Run()
config.RollappBinary = path
}
4 changes: 3 additions & 1 deletion cmd/config/init/unique_rollapp_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package initconfig

import (
"fmt"
"github.com/dymensionxyz/roller/cmd/utils"
"net/http"

"github.com/dymensionxyz/roller/cmd/utils"
)

// TODO(#150): roller should use RPC for queries instead of REST
func IsRollappIDUnique(rollappID string, initConfig utils.RollappConfig) (bool, error) {
url := initConfig.HubData.API_URL + "/dymensionxyz/dymension/rollapp/rollapp/" + rollappID

Expand Down
18 changes: 15 additions & 3 deletions cmd/sequencer/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import (
// TODO: Test sequencing on 35-C and update the price
var OneDaySequencePrice = big.NewInt(1)

var (
RollappBinary string
RollappDirPath string
LogPath string
)

func StartCmd() *cobra.Command {
runCmd := &cobra.Command{
Use: "start",
Expand All @@ -24,6 +30,11 @@ func StartCmd() *cobra.Command {
home := cmd.Flag(utils.FlagNames.Home).Value.String()
rollappConfig, err := utils.LoadConfigFromTOML(home)
utils.PrettifyErrorIfExists(err)

LogPath = filepath.Join(rollappConfig.Home, consts.ConfigDirName.Rollapp, "rollapp.log")
RollappBinary = rollappConfig.RollappBinary
RollappDirPath = filepath.Join(rollappConfig.Home, consts.ConfigDirName.Rollapp)

sequencerInsufficientAddrs, err := utils.GetSequencerInsufficientAddrs(rollappConfig, *OneDaySequencePrice)
utils.PrettifyErrorIfExists(err)
utils.PrintInsufficientBalancesIfAny(sequencerInsufficientAddrs)
Expand All @@ -47,14 +58,14 @@ var FlagNames = struct {

func printOutput() {
fmt.Println("πŸ’ˆ The Rollapp sequencer is running on your local machine!")

//TODO: either mark the ports as default, or read from configuration file
fmt.Println("πŸ’ˆ Default endpoints:")

fmt.Println("πŸ’ˆ EVM RPC: http://0.0.0.0:8545")
fmt.Println("πŸ’ˆ Node RPC: http://0.0.0.0:26657")
fmt.Println("πŸ’ˆ Rest API: http://0.0.0.0:1317")

//TODO: print the log file path
fmt.Println("πŸ’ˆ Log file path: ", LogPath)
fmt.Println("πŸ’ˆ Rollapp root dir: ", RollappDirPath)
}

func parseError(errMsg string) string {
Expand Down Expand Up @@ -87,6 +98,7 @@ func GetStartRollappCmd(rollappConfig utils.RollappConfig, lightNodeEndpoint str
"--dymint.settlement_config.keyring_home_dir", hubKeysDir,
"--dymint.settlement_config.gas_prices", "0udym",
"--home", rollappConfigDir,
"--log-file", filepath.Join(rollappConfigDir, "rollapp.log"),
"--log_level", "info",
"--max-log-size", "2000",
)
Expand Down
Loading

0 comments on commit a6f207a

Please sign in to comment.