Skip to content

Commit

Permalink
feat: int in-place testnet creation (backport #4297) (#4338)
Browse files Browse the repository at this point in the history
* feat: int in-place testnet creation (#4297)

(cherry picked from commit f7618d3)

# Conflicts:
#	docs/docs/03-CLI-Commands/01-cli-commands.md
#	ignite/pkg/chaincmd/chaincmd.go

* updates

* cl

* re

* consumer handling

---------

Co-authored-by: Duong NV | Decentrio <[email protected]>
Co-authored-by: Julien Robert <[email protected]>
  • Loading branch information
3 people committed Sep 4, 2024
1 parent da90c3e commit abbe7f3
Show file tree
Hide file tree
Showing 13 changed files with 549 additions and 2 deletions.
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

0 comments on commit abbe7f3

Please sign in to comment.