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: int in-place testnet creation (backport #4297) #4338

Merged
merged 5 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- [#4297](https://github.com/ignite/cli/pull/4297) Add in-place testnet creation command for apps.

### Changes

- [#4292](https://github.com/ignite/cli/pull/4292) Bump Cosmos SDK to `v0.50.9`
Expand Down
1 change: 1 addition & 0 deletions ignite/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ To get started, create a blockchain:
NewApp(),
NewDoctor(),
NewCompletionCmd(),
NewTestnet(),
)
c.AddCommand(deprecated()...)
c.SetContext(ctx)
Expand Down
24 changes: 24 additions & 0 deletions ignite/cmd/testnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ignitecmd

import (
"github.com/spf13/cobra"
)

// NewTestnet returns a command that groups scaffolding related sub commands.
func NewTestnet() *cobra.Command {
c := &cobra.Command{
Use: "testnet [command]",
Short: "Start a testnet local",
Long: `Start a testnet local

`,
Aliases: []string{"s"},
Args: cobra.ExactArgs(1),
}

c.AddCommand(
NewTestnetInPlace(),
)

return c
}
129 changes: 129 additions & 0 deletions ignite/cmd/testnet_inplace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ignitecmd

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/spf13/cobra"

"github.com/ignite/cli/v28/ignite/pkg/cliui"
"github.com/ignite/cli/v28/ignite/pkg/cosmosaccount"
"github.com/ignite/cli/v28/ignite/pkg/errors"
"github.com/ignite/cli/v28/ignite/services/chain"
)

func NewTestnetInPlace() *cobra.Command {
c := &cobra.Command{
Use: "in-place",
Short: "Create and start a testnet from current local net state",
Long: `Testnet in-place command is used to create and start a testnet from current local net state(including mainnet).
After using this command in the repo containing the config.yml file, the network will start.
We can create a testnet from the local network state and mint additional coins for the desired accounts from the config.yml file.`,
Args: cobra.NoArgs,
RunE: testnetInPlaceHandler,
}
flagSetPath(c)
flagSetClearCache(c)
c.Flags().AddFlagSet(flagSetHome())
c.Flags().AddFlagSet(flagSetCheckDependencies())
c.Flags().AddFlagSet(flagSetSkipProto())
c.Flags().AddFlagSet(flagSetVerbose())

c.Flags().Bool(flagQuitOnFail, false, "quit program if the app fails to start")
return c
}

func testnetInPlaceHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(
cliui.WithVerbosity(getVerbosity(cmd)),
)
defer session.End()

// Otherwise run the serve command directly
return testnetInplace(cmd, session)
}

func testnetInplace(cmd *cobra.Command, session *cliui.Session) error {
chainOption := []chain.Option{
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
chain.CheckCosmosSDKVersion(),
}

if flagGetCheckDependencies(cmd) {
chainOption = append(chainOption, chain.CheckDependencies())
}

// check if custom config is defined
config, _ := cmd.Flags().GetString(flagConfig)
if config != "" {
chainOption = append(chainOption, chain.ConfigFile(config))
}

c, err := chain.NewWithHomeFlags(cmd, chainOption...)
if err != nil {
return err
}

cfg, err := c.Config()
if err != nil {
return err
}
home, err := c.Home()
if err != nil {
return err
}
keyringBackend, err := c.KeyringBackend()
if err != nil {
return err
}
ca, err := cosmosaccount.New(
cosmosaccount.WithKeyringBackend(cosmosaccount.KeyringBackend(keyringBackend)),
cosmosaccount.WithHome(home),
)
if err != nil {
return err
}

var (
operatorAddress sdk.ValAddress
accounts string
accErr *cosmosaccount.AccountDoesNotExistError
)
for _, acc := range cfg.Accounts {
sdkAcc, err := ca.GetByName(acc.Name)
if errors.As(err, &accErr) {
sdkAcc, _, err = ca.Create(acc.Name)
}
if err != nil {
return err
}

sdkAddr, err := sdkAcc.Address(getAddressPrefix(cmd))
if err != nil {
return err
}
if len(cfg.Validators) == 0 {
return errors.Errorf("no validators found for account %s", sdkAcc.Name)
}
if cfg.Validators[0].Name == acc.Name {
accAddr, err := sdk.AccAddressFromBech32(sdkAddr)
if err != nil {
return err
}
operatorAddress = sdk.ValAddress(accAddr)
}
accounts = accounts + "," + sdkAddr
}

chainID, err := c.ID()
if err != nil {
return err
}

args := chain.InPlaceArgs{
NewChainID: chainID,
NewOperatorAddress: operatorAddress.String(),
AccountsToFund: accounts,
}
return c.TestnetInPlace(cmd.Context(), args)
}
5 changes: 5 additions & 0 deletions ignite/pkg/chaincmd/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
commandUnsafeReset = "unsafe-reset-all"
commandExport = "export"
commandTendermint = "tendermint"
commandTestnetInPlace = "in-place-testnet"

optionHome = "--home"
optionNode = "--node"
Expand All @@ -53,6 +54,10 @@ const (
optionVestingAmount = "--vesting-amount"
optionVestingEndTime = "--vesting-end-time"
optionBroadcastMode = "--broadcast-mode"
optionAccount = "--account"
optionValidatorPrivateKey = "--validator-privkey"
optionAccountToFund = "--accounts-to-fund"
optionSkipConfirmation = "--skip-confirmation"

constTendermint = "tendermint"
constJSON = "json"
Expand Down
47 changes: 47 additions & 0 deletions ignite/pkg/chaincmd/in-place-testnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package chaincmd

import (
"github.com/ignite/cli/v28/ignite/pkg/cmdrunner/step"
)

type InPlaceOption func([]string) []string

func InPlaceWithPrvKey(prvKey string) InPlaceOption {
return func(s []string) []string {
if len(prvKey) > 0 {
return append(s, optionValidatorPrivateKey, prvKey)
}
return s
}
}

func InPlaceWithAccountToFund(accounts string) InPlaceOption {
return func(s []string) []string {
if len(accounts) > 0 {
return append(s, optionAccountToFund, accounts)
}
return s
}
}

func InPlaceWithSkipConfirmation() InPlaceOption {
return func(s []string) []string {
return append(s, optionSkipConfirmation)
}
}

// TestnetInPlaceCommand return command to start testnet in-place.
func (c ChainCmd) TestnetInPlaceCommand(newChainID, newOperatorAddress string, options ...InPlaceOption) step.Option {
command := []string{
commandTestnetInPlace,
newChainID,
newOperatorAddress,
}

// Apply the options provided by the user
for _, apply := range options {
command = apply(command)
}

return c.daemonCommand(command)
}
12 changes: 12 additions & 0 deletions ignite/pkg/chaincmd/runner/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ func NewKV(key, value string) KV {

var gentxRe = regexp.MustCompile(`(?m)"(.+?)"`)

func (r Runner) InPlace(ctx context.Context, newChainID, newOperatorAddress string, options ...chaincmd.InPlaceOption) error {
runOptions := runOptions{
stdout: os.Stdout,
stderr: os.Stderr,
}
return r.run(
ctx,
runOptions,
r.chainCmd.TestnetInPlaceCommand(newChainID, newOperatorAddress, options...),
)
}

// Gentx generates a genesis tx carrying a self delegation.
func (r Runner) Gentx(
ctx context.Context,
Expand Down
11 changes: 11 additions & 0 deletions ignite/services/chain/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ func (c Chain) Gentx(ctx context.Context, runner chaincmdrunner.Runner, v Valida
)
}

func (c Chain) InPlace(ctx context.Context, runner chaincmdrunner.Runner, args InPlaceArgs) error {
err := runner.InPlace(ctx,
args.NewChainID,
args.NewOperatorAddress,
chaincmd.InPlaceWithPrvKey(args.PrvKeyValidator),
chaincmd.InPlaceWithAccountToFund(args.AccountsToFund),
chaincmd.InPlaceWithSkipConfirmation(),
)
return err
}

// Start wraps the "appd start" command to begin running a chain from the daemon.
func (c Chain) Start(ctx context.Context, runner chaincmdrunner.Runner, cfg *chainconfig.Config) error {
validator, err := chainconfig.FirstValidator(cfg)
Expand Down
37 changes: 37 additions & 0 deletions ignite/services/chain/testnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package chain

import (
"context"
"os"

chainconfig "github.com/ignite/cli/v28/ignite/config/chain"
)

type InPlaceArgs struct {
NewChainID string
NewOperatorAddress string
PrvKeyValidator string
AccountsToFund string
}

func (c Chain) TestnetInPlace(ctx context.Context, args InPlaceArgs) error {
commands, err := c.Commands(ctx)
if err != nil {
return err
}

// make sure that config.yml exists
if c.options.ConfigFile != "" {
if _, err := os.Stat(c.options.ConfigFile); err != nil {
return err
}
} else if _, err := chainconfig.LocateDefault(c.app.Path); err != nil {
return err
}

err = c.InPlace(ctx, commands, args)
if err != nil {
return err
}
return nil
}
2 changes: 1 addition & 1 deletion ignite/templates/app/files/app/app.go.plush
Original file line number Diff line number Diff line change
Expand Up @@ -404,4 +404,4 @@ func BlockedAddresses() map[string]bool {
}
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func initRootCmd(
) {
rootCmd.AddCommand(
genutilcli.InitCmd(basicManager, app.DefaultNodeHome),
NewTestnetCmd(addModuleInitFlags),
debug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(newApp, app.DefaultNodeHome),
Expand Down Expand Up @@ -186,4 +187,4 @@ func appExport(
}

return bApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
}
}
Loading
Loading