From f7618d3e217e1e8214ec183e0e6a12f63160deb4 Mon Sep 17 00:00:00 2001 From: Duong NV | Decentrio Date: Tue, 3 Sep 2024 22:13:12 +0700 Subject: [PATCH 1/9] feat: int in-place testnet creation (#4297) --- changelog.md | 1 + docs/docs/03-CLI-Commands/01-cli-commands.md | 70 +++++ ignite/cmd/cmd.go | 1 + ignite/cmd/testnet.go | 24 ++ ignite/cmd/testnet_inplace.go | 129 +++++++++ ignite/pkg/chaincmd/chaincmd.go | 4 + ignite/pkg/chaincmd/in-place-testnet.go | 47 ++++ ignite/pkg/chaincmd/runner/chain.go | 12 + ignite/services/chain/runtime.go | 11 + ignite/services/chain/testnet.go | 37 +++ ignite/templates/app/files/app/app.go.plush | 2 +- .../cmd/commands.go.plush | 3 +- .../cmd/testnet.go.plush | 264 ++++++++++++++++++ ignite/templates/app/files/go.mod.plush | 2 + 14 files changed, 605 insertions(+), 2 deletions(-) create mode 100644 ignite/cmd/testnet.go create mode 100644 ignite/cmd/testnet_inplace.go create mode 100644 ignite/pkg/chaincmd/in-place-testnet.go create mode 100644 ignite/services/chain/testnet.go create mode 100644 ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush diff --git a/changelog.md b/changelog.md index 024110c8f9..de749269dd 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,7 @@ - [#4111](https://github.com/ignite/cli/pull/4111) Remove vuex generation - [#4113](https://github.com/ignite/cli/pull/4113) Generate chain config documentation automatically - [#4131](https://github.com/ignite/cli/pull/4131) Support `bytes` as data type in the `scaffold` commands +- [#4297](https://github.com/ignite/cli/pull/4297) Add in-place testnet creation command for apps. - [#4300](https://github.com/ignite/cli/pull/4300) Only panics the module in the most top function level - [#4327](https://github.com/ignite/cli/pull/4327) Use the TxConfig from simState instead create a new one - [#4326](https://github.com/ignite/cli/pull/4326) fAdd `buf.build` version to `ignite version` command diff --git a/docs/docs/03-CLI-Commands/01-cli-commands.md b/docs/docs/03-CLI-Commands/01-cli-commands.md index e15f6b37a1..f029a32fad 100644 --- a/docs/docs/03-CLI-Commands/01-cli-commands.md +++ b/docs/docs/03-CLI-Commands/01-cli-commands.md @@ -39,6 +39,7 @@ To get started, create a blockchain: * [ignite relayer](#ignite-relayer) - Connect blockchains with an IBC relayer * [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more * [ignite version](#ignite-version) - Print the current build information +* [ignite testnet](#ignite-testnet) - Start a testnet local ## ignite account @@ -3658,3 +3659,72 @@ ignite version [flags] * [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain + +## ignite testnet + +Start a testnet local + +**Synopsis** + +The commands in this namespace allow you to start your local testnet for development purposes. Currently there is only one feature to create a testnet from any state network (including mainnet). + + +The "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. + +During development, in-place allows you to quickly reboot the chain from a multi-node network state to a node you have full control over. + +**SEE ALSO** + +* [ignite testnet in-place](#ignite-testnet-in-place) - Create and start a testnet from current local net state + + +## ignite testnet in-place + +Create and start a testnet from current local net state + +**Synopsis** + +The "in-place" command is used to create and start a testnet from current local net state(including mainnet). + +We can create a testnet from the local network state and mint additional coins for the desired accounts from the config.yml file. + +During development, in-place allows you to quickly reboot the chain from a multi-node network state to a node you have full control over. + +By default, the data directory will be initialized in $HOME/.mychain, where "mychain" is the name of the project. To set a custom data directory use the --home flag or set the value in config.yml: + + validators: + - name: alice + bonded: '100000000stake' + home: "~/.customdir" + +Get mint coin just add account in config.yml file: + + accounts: + - name: charlie + coins: + - 20000token + - 200000000stake + + +``` +ignite chain debug [flags] +``` + +**Options** + +``` + -h, --help help for debug + -p, --path string path of the app (default ".") +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, start testnet and launch your blockchain \ No newline at end of file diff --git a/ignite/cmd/cmd.go b/ignite/cmd/cmd.go index 2ebb8b06ac..e2ab8930b8 100644 --- a/ignite/cmd/cmd.go +++ b/ignite/cmd/cmd.go @@ -89,6 +89,7 @@ To get started, create a blockchain: NewApp(), NewDoctor(), NewCompletionCmd(), + NewTestnet(), ) c.AddCommand(deprecated()...) c.SetContext(ctx) diff --git a/ignite/cmd/testnet.go b/ignite/cmd/testnet.go new file mode 100644 index 0000000000..03e0068c06 --- /dev/null +++ b/ignite/cmd/testnet.go @@ -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 +} diff --git a/ignite/cmd/testnet_inplace.go b/ignite/cmd/testnet_inplace.go new file mode 100644 index 0000000000..17afb21f2a --- /dev/null +++ b/ignite/cmd/testnet_inplace.go @@ -0,0 +1,129 @@ +package ignitecmd + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/spf13/cobra" + + "github.com/ignite/cli/v29/ignite/pkg/cliui" + "github.com/ignite/cli/v29/ignite/pkg/cosmosaccount" + "github.com/ignite/cli/v29/ignite/pkg/errors" + "github.com/ignite/cli/v29/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) +} diff --git a/ignite/pkg/chaincmd/chaincmd.go b/ignite/pkg/chaincmd/chaincmd.go index 7034b789e5..c45baf53fc 100644 --- a/ignite/pkg/chaincmd/chaincmd.go +++ b/ignite/pkg/chaincmd/chaincmd.go @@ -27,6 +27,7 @@ const ( commandUnsafeReset = "unsafe-reset-all" commandExport = "export" commandTendermint = "tendermint" + commandTestnetInPlace = "in-place-testnet" optionHome = "--home" optionNode = "--node" @@ -55,6 +56,9 @@ const ( optionBroadcastMode = "--broadcast-mode" optionAccount = "--account" optionIndex = "--index" + optionValidatorPrivateKey = "--validator-privkey" + optionAccountToFund = "--accounts-to-fund" + optionSkipConfirmation = "--skip-confirmation" constTendermint = "tendermint" constJSON = "json" diff --git a/ignite/pkg/chaincmd/in-place-testnet.go b/ignite/pkg/chaincmd/in-place-testnet.go new file mode 100644 index 0000000000..84a3b38465 --- /dev/null +++ b/ignite/pkg/chaincmd/in-place-testnet.go @@ -0,0 +1,47 @@ +package chaincmd + +import ( + "github.com/ignite/cli/v29/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) +} diff --git a/ignite/pkg/chaincmd/runner/chain.go b/ignite/pkg/chaincmd/runner/chain.go index 7e60719713..0b133a47d2 100644 --- a/ignite/pkg/chaincmd/runner/chain.go +++ b/ignite/pkg/chaincmd/runner/chain.go @@ -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, diff --git a/ignite/services/chain/runtime.go b/ignite/services/chain/runtime.go index ac18fb23f8..a0eb21e1df 100644 --- a/ignite/services/chain/runtime.go +++ b/ignite/services/chain/runtime.go @@ -37,6 +37,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) diff --git a/ignite/services/chain/testnet.go b/ignite/services/chain/testnet.go new file mode 100644 index 0000000000..5a3b85d77b --- /dev/null +++ b/ignite/services/chain/testnet.go @@ -0,0 +1,37 @@ +package chain + +import ( + "context" + "os" + + chainconfig "github.com/ignite/cli/v29/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 +} diff --git a/ignite/templates/app/files/app/app.go.plush b/ignite/templates/app/files/app/app.go.plush index 504a3b392f..5d3293fc45 100644 --- a/ignite/templates/app/files/app/app.go.plush +++ b/ignite/templates/app/files/app/app.go.plush @@ -402,4 +402,4 @@ func BlockedAddresses() map[string]bool { } } return result -} +} \ No newline at end of file diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush index 685899a07d..5388a9d8d7 100644 --- a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush @@ -35,6 +35,7 @@ func initRootCmd( ) { rootCmd.AddCommand( genutilcli.InitCmd(basicManager, app.DefaultNodeHome), + NewTestnetCmd(addModuleInitFlags), debug.Cmd(), confixcmd.ConfigCommand(), pruning.Cmd(newApp, app.DefaultNodeHome), @@ -186,4 +187,4 @@ func appExport( } return bApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport) -} \ No newline at end of file +} diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush new file mode 100644 index 0000000000..0a02a3bdd7 --- /dev/null +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush @@ -0,0 +1,264 @@ +package cmd + +import ( + "fmt" + "io" + "strings" + + "cosmossdk.io/log" + "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/libs/bytes" + tmos "github.com/cometbft/cometbft/libs/os" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/client/flags" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/server" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/spf13/cast" + "github.com/spf13/cobra" + + "<%= ModulePath %>/app" +) + +const ( + valVotingPower int64 = 900000000000000 +) + +var ( + flagAccountsToFund = "accounts-to-fund" +) + +type valArgs struct { + newValAddr bytes.HexBytes + newOperatorAddress string + newValPubKey crypto.PubKey + accountsToFund []sdk.AccAddress + upgradeToTrigger string + homeDir string +} + +func NewTestnetCmd(addStartFlags servertypes.ModuleInitFlags) *cobra.Command { + cmd := server.InPlaceTestnetCreator(newTestnetApp) + addStartFlags(cmd) + cmd.Short = "Updates chain's application and consensus state with provided validator info and starts the node" + cmd.Long = `The test command modifies both application and consensus stores within a local mainnet node and starts the node, +with the aim of facilitating testing procedures. This command replaces existing validator data with updated information, +thereby removing the old validator set and introducing a new set suitable for local testing purposes. By altering the state extracted from the mainnet node, +it enables developers to configure their local environments to reflect mainnet conditions more accurately.` + + cmd.Example = fmt.Sprintf(`%sd in-place-testnet testing-1 cosmosvaloper1w7f3xx7e75p4l7qdym5msqem9rd4dyc4mq79dm --home $HOME/.%sd/validator1 --validator-privkey=6dq+/KHNvyiw2TToCgOpUpQKIzrLs69Rb8Az39xvmxPHNoPxY1Cil8FY+4DhT9YwD6s0tFABMlLcpaylzKKBOg== --accounts-to-fund="cosmos1f7twgcq4ypzg7y24wuywy06xmdet8pc4473tnq,cosmos1qvuhm5m644660nd8377d6l7yz9e9hhm9evmx3x"`, "<%= ModulePath %>", "<%= ModulePath %>") + + cmd.Flags().String(flagAccountsToFund, "", "Comma-separated list of account addresses that will be funded for testing purposes") + return cmd +} + +// newTestnetApp starts by running the normal newApp method. From there, the app interface returned is modified in order +// for a testnet to be created from the provided app. +func newTestnetApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { + // Create an app and type cast to an App + newApp := newApp(logger, db, traceStore, appOpts) + testApp, ok := newApp.(*app.App) + if !ok { + panic("app created from newApp is not of type App") + } + + // Get command args + args, err := getCommandArgs(appOpts) + if err != nil { + panic(err) + } + + return initAppForTestnet(testApp, args) +} + +func initAppForTestnet(app *app.App, args valArgs) *app.App { + // Required Changes: + // + ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) + + pubkey := &ed25519.PubKey{Key: args.newValPubKey.Bytes()} + pubkeyAny, err := codectypes.NewAnyWithValue(pubkey) + if err != nil { + tmos.Exit(err.Error()) + } + + // STAKING + // + + // Create Validator struct for our new validator. + newVal := stakingtypes.Validator{ + OperatorAddress: args.newOperatorAddress, + ConsensusPubkey: pubkeyAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: math.NewInt(valVotingPower), + DelegatorShares: math.LegacyMustNewDecFromStr("10000000"), + Description: stakingtypes.Description{ + Moniker: "Testnet Validator", + }, + Commission: stakingtypes.Commission{ + CommissionRates: stakingtypes.CommissionRates{ + Rate: math.LegacyMustNewDecFromStr("0.05"), + MaxRate: math.LegacyMustNewDecFromStr("0.1"), + MaxChangeRate: math.LegacyMustNewDecFromStr("0.05"), + }, + }, + MinSelfDelegation: math.OneInt(), + } + + validator, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(newVal.GetOperator()) + if err != nil { + tmos.Exit(err.Error()) + } + + // Remove all validators from power store + stakingKey := app.GetKey(stakingtypes.ModuleName) + stakingStore := ctx.KVStore(stakingKey) + iterator, err := app.StakingKeeper.ValidatorsPowerStoreIterator(ctx) + if err != nil { + tmos.Exit(err.Error()) + } + for ; iterator.Valid(); iterator.Next() { + stakingStore.Delete(iterator.Key()) + } + iterator.Close() + + // Remove all valdiators from last validators store + iterator, err = app.StakingKeeper.LastValidatorsIterator(ctx) + if err != nil { + tmos.Exit(err.Error()) + } + for ; iterator.Valid(); iterator.Next() { + stakingStore.Delete(iterator.Key()) + } + iterator.Close() + + // Remove all validators from validators store + iterator = stakingStore.Iterator(stakingtypes.ValidatorsKey, storetypes.PrefixEndBytes(stakingtypes.ValidatorsKey)) + for ; iterator.Valid(); iterator.Next() { + stakingStore.Delete(iterator.Key()) + } + iterator.Close() + + // Remove all validators from unbonding queue + iterator = stakingStore.Iterator(stakingtypes.ValidatorQueueKey, storetypes.PrefixEndBytes(stakingtypes.ValidatorQueueKey)) + for ; iterator.Valid(); iterator.Next() { + stakingStore.Delete(iterator.Key()) + } + iterator.Close() + + // Add our validator to power and last validators store + app.StakingKeeper.SetValidator(ctx, newVal) + err = app.StakingKeeper.SetValidatorByConsAddr(ctx, newVal) + if err != nil { + tmos.Exit(err.Error()) + } + app.StakingKeeper.SetValidatorByPowerIndex(ctx, newVal) + app.StakingKeeper.SetLastValidatorPower(ctx, validator, 0) + if err := app.StakingKeeper.Hooks().AfterValidatorCreated(ctx, validator); err != nil { + tmos.Exit(err.Error()) + } + + // DISTRIBUTION + // + + // Initialize records for this validator across all distribution stores + app.DistrKeeper.SetValidatorHistoricalRewards(ctx, validator, 0, distrtypes.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1)) + app.DistrKeeper.SetValidatorCurrentRewards(ctx, validator, distrtypes.NewValidatorCurrentRewards(sdk.DecCoins{}, 1)) + app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, validator, distrtypes.InitialValidatorAccumulatedCommission()) + app.DistrKeeper.SetValidatorOutstandingRewards(ctx, validator, distrtypes.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}}) + +<%= if (!IsChainMinimal) { %> + // SLASHING + // + + // Set validator signing info for our new validator. + newConsAddr := sdk.ConsAddress(args.newValAddr.Bytes()) + newValidatorSigningInfo := slashingtypes.ValidatorSigningInfo{ + Address: newConsAddr.String(), + StartHeight: app.LastBlockHeight() - 1, + Tombstoned: false, + } + app.SlashingKeeper.SetValidatorSigningInfo(ctx, newConsAddr, newValidatorSigningInfo) +<% } %> + + // BANK + // + bondDenom, err := app.StakingKeeper.BondDenom(ctx) + if err != nil { + tmos.Exit(err.Error()) + } + + defaultCoins := sdk.NewCoins(sdk.NewInt64Coin(bondDenom, 1000000000)) + + // Fund local accounts + for _, account := range args.accountsToFund { + err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, defaultCoins) + if err != nil { + tmos.Exit(err.Error()) + } + err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, account, defaultCoins) + if err != nil { + tmos.Exit(err.Error()) + } + } + + return app +} + +// parse the input flags and returns valArgs +func getCommandArgs(appOpts servertypes.AppOptions) (valArgs, error) { + args := valArgs{} + + newValAddr, ok := appOpts.Get(server.KeyNewValAddr).(bytes.HexBytes) + if !ok { + panic("newValAddr is not of type bytes.HexBytes") + } + args.newValAddr = newValAddr + newValPubKey, ok := appOpts.Get(server.KeyUserPubKey).(crypto.PubKey) + if !ok { + panic("newValPubKey is not of type crypto.PubKey") + } + args.newValPubKey = newValPubKey + newOperatorAddress, ok := appOpts.Get(server.KeyNewOpAddr).(string) + if !ok { + panic("newOperatorAddress is not of type string") + } + args.newOperatorAddress = newOperatorAddress + upgradeToTrigger, ok := appOpts.Get(server.KeyTriggerTestnetUpgrade).(string) + if !ok { + panic("upgradeToTrigger is not of type string") + } + args.upgradeToTrigger = upgradeToTrigger + + // validate and set accounts to fund + accountsString := cast.ToString(appOpts.Get(flagAccountsToFund)) + + for _, account := range strings.Split(accountsString, ",") { + if account != "" { + addr, err := sdk.AccAddressFromBech32(account) + if err != nil { + return args, fmt.Errorf("invalid bech32 address format %w", err) + } + args.accountsToFund = append(args.accountsToFund, addr) + } + } + + // home dir + homeDir := cast.ToString(appOpts.Get(flags.FlagHome)) + if homeDir == "" { + return args, fmt.Errorf("invalid home dir") + } + args.homeDir = homeDir + + return args, nil +} diff --git a/ignite/templates/app/files/go.mod.plush b/ignite/templates/app/files/go.mod.plush index 5c3d23ad7e..0f0e4cf1c8 100644 --- a/ignite/templates/app/files/go.mod.plush +++ b/ignite/templates/app/files/go.mod.plush @@ -17,6 +17,7 @@ require ( cosmossdk.io/depinject v1.0.0 cosmossdk.io/errors v1.0.1 cosmossdk.io/log v1.3.1 + cosmossdk.io/math v1.3.0 cosmossdk.io/store v1.1.0 cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/circuit v0.1.0 @@ -36,6 +37,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 + github.com/spf13/cast v1.6.0 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 From 41ec8304e06fa8bcc3db8d03651a371e088e2a0c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 20:12:49 +0300 Subject: [PATCH 2/9] docs(cli): update generated docs (#4339) --- docs/docs/08-references/01-cli.md | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/docs/08-references/01-cli.md b/docs/docs/08-references/01-cli.md index fdafb81c2e..230c63ce2c 100644 --- a/docs/docs/08-references/01-cli.md +++ b/docs/docs/08-references/01-cli.md @@ -37,6 +37,7 @@ To get started, create a blockchain: * [ignite network](#ignite-network) - Launch a blockchain in production * [ignite relayer](#ignite-relayer) - Connect blockchains with an IBC relayer * [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more +* [ignite testnet](#ignite-testnet) - Start a testnet local * [ignite version](#ignite-version) - Print the current build information @@ -3420,6 +3421,60 @@ ignite scaffold vue [flags] * [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more +## ignite testnet + +Start a testnet local + +**Synopsis** + +Start a testnet local + + + +**Options** + +``` + -h, --help help for testnet +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite testnet in-place](#ignite-testnet-in-place) - Create and start a testnet from current local net state + + +## ignite testnet in-place + +Create and start a testnet from current local net state + +**Synopsis** + +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. + +``` +ignite testnet in-place [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + -h, --help help for in-place + --home string directory where the blockchain node is initialized + -p, --path string path of the app (default ".") + --quit-on-fail quit program if the app fails to start + --skip-proto skip file generation from proto + -v, --verbose verbose output +``` + +**SEE ALSO** + +* [ignite testnet](#ignite-testnet) - Start a testnet local + + ## ignite version Print the current build information From 67a029f9f6af7a7737a8a2dc6d42053173b96e0c Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 6 Sep 2024 14:12:00 +0800 Subject: [PATCH 3/9] fix: comment out optimistic execution --- ignite/templates/app/files-minimal/app/app.go.plush | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ignite/templates/app/files-minimal/app/app.go.plush b/ignite/templates/app/files-minimal/app/app.go.plush index 3cee9a8fc7..4ec41f6cd9 100644 --- a/ignite/templates/app/files-minimal/app/app.go.plush +++ b/ignite/templates/app/files-minimal/app/app.go.plush @@ -147,8 +147,8 @@ func New( } // add to default baseapp options - // enable optimistic execution - baseAppOptions = append(baseAppOptions, baseapp.SetOptimisticExecution()) + // uncomment to enable Optimistic Execution + // baseAppOptions = append(baseAppOptions, baseapp.SetOptimisticExecution()) // build app app.App = appBuilder.Build(db, traceStore, baseAppOptions...) From 970956a689dcd946dbede2768f4f17f640b6376c Mon Sep 17 00:00:00 2001 From: Danilo Pantani Date: Fri, 6 Sep 2024 12:25:22 +0200 Subject: [PATCH 4/9] fix(docs): fix the relayer for the ibc guides (#4214) * fix the realayer for the ibc guides * fix chain-id * fix port-id for ibc transfers * fix channel version * fix blog ibc guide for use collections * fix typo into the parameters and set postid to uint * remove unused comments --------- Co-authored-by: Danny Co-authored-by: Julien Robert --- docs/docs/02-guide/04-ibc.md | 211 ++++++++++-------------- integration/relayer/cmd_relayer_test.go | 104 +++++------- 2 files changed, 130 insertions(+), 185 deletions(-) diff --git a/docs/docs/02-guide/04-ibc.md b/docs/docs/02-guide/04-ibc.md index abddcd9c14..4813c51983 100644 --- a/docs/docs/02-guide/04-ibc.md +++ b/docs/docs/02-guide/04-ibc.md @@ -110,21 +110,21 @@ transactions: - Creating blog posts - ```bash - ignite scaffold list post title content creator --no-message --module blog - ``` +```bash +ignite scaffold list post title content creator --no-message --module blog +``` - Processing acknowledgments for sent posts - ```bash - ignite scaffold list sentPost postID title chain creator --no-message --module blog - ``` +```bash +ignite scaffold list sentPost postID:uint title chain creator --no-message --module blog +``` - Managing post timeouts - ```bash - ignite scaffold list timedoutPost title chain creator --no-message --module blog - ``` +```bash +ignite scaffold list timeoutPost title chain creator --no-message --module blog +``` The scaffolded code includes proto files for defining data structures, messages, messages handlers, keepers for modifying the state, and CLI commands. @@ -137,7 +137,7 @@ ignite scaffold list [typeName] [field1] [field2] ... [flags] The first argument of the `ignite scaffold list [typeName]` command specifies the name of the type being created. For the blog app, you created `post`, -`sentPost`, and `timedoutPost` types. +`sentPost`, and `timeoutPost` types. The next arguments define the fields that are associated with the type. For the blog app, you created `title`, `content`, `postID`, and `chain` fields. @@ -168,7 +168,7 @@ to another blockchain. To scaffold a sendable and interpretable IBC packet: ```bash -ignite scaffold packet ibcPost title content --ack postID --module blog +ignite scaffold packet ibcPost title content --ack postID:uint --module blog ``` Notice the fields in the `ibcPost` packet match the fields in the `post` type @@ -222,13 +222,23 @@ the `msg.Creator` value to the IBC `packet`. ```go title="x/blog/keeper/msg_server_ibc_post.go" package keeper -import ( - // ... - "planet/x/blog/types" -) - func (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) (*types.MsgSendIbcPostResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) + // validate incoming message + if _, err := k.addressCodec.StringToBytes(msg.Creator); err != nil { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid address: %s", err)) + } + + if msg.Port == "" { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet port") + } + + if msg.ChannelID == "" { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet channel") + } + + if msg.TimeoutTimestamp == 0 { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet timeout") + } // TODO: logic before transmitting the packet @@ -241,6 +251,7 @@ func (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) packet.Creator = msg.Creator // Transmit the packet + ctx := sdk.UnwrapSDKContext(goCtx) _, err := k.TransmitIbcPostPacket( ctx, packet, @@ -298,44 +309,17 @@ Append the type instance as `PostId` on receiving the packet: - The `title` is the Title of the blog post - The `content` is the Content of the blog post -In the `x/blog/keeper/ibc_post.go` file, make sure to import `"strconv"` below -`"errors"`: - -```go title="x/blog/keeper/ibc_post.go" -import ( - //... - - "strconv" - -// ... -) -``` - Then modify the `OnRecvIbcPostPacket` keeper function with the following code: -```go +```go title="x/blog/keeper/ibc_post.go" package keeper -// ... - func (k Keeper) OnRecvIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) (packetAck types.IbcPostPacketAck, err error) { - // validate packet data upon receiving - if err := data.ValidateBasic(); err != nil { + packetAck.PostId, err = k.PostSeq.Next(ctx) + if err != nil { return packetAck, err } - - id := k.AppendPost( - ctx, - types.Post{ - Creator: packet.SourcePort + "-" + packet.SourceChannel + "-" + data.Creator, - Title: data.Title, - Content: data.Content, - }, - ) - - packetAck.PostId = strconv.FormatUint(id, 10) - - return packetAck, nil + return packetAck, k.Post.Set(ctx, packetAck.PostId, types.Post{Title: data.Title, Content: data.Content}) } ``` @@ -354,9 +338,6 @@ from the packet. ```go title="x/blog/keeper/ibc_post.go" package keeper -// ... - -// x/blog/keeper/ibc_post.go func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData, ack channeltypes.Acknowledgement) error { switch dispatchedAck := ack.Response.(type) { case *channeltypes.Acknowledgement_Error: @@ -365,23 +346,23 @@ func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channelty case *channeltypes.Acknowledgement_Result: // Decode the packet acknowledgment var packetAck types.IbcPostPacketAck - if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { // The counter-party module doesn't implement the correct acknowledgment format return errors.New("cannot unmarshal acknowledgment") } - k.AppendSentPost( - ctx, + seq, err := k.SentPostSeq.Next(ctx) + if err != nil { + return err + } + + return k.SentPost.Set(ctx, seq, types.SentPost{ - Creator: data.Creator, - PostId: packetAck.PostId, - Title: data.Title, - Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + PostId: packetAck.PostId, + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, }, ) - - return nil default: return errors.New("the counter-party module does not implement the correct acknowledgment format") } @@ -390,23 +371,23 @@ func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channelty ### Store information about the timed-out packet -Store posts that have not been received by target chains in `timedoutPost` +Store posts that have not been received by target chains in `timeoutPost` posts. This logic follows the same format as `sentPost`. ```go title="x/blog/keeper/ibc_post.go" func (k Keeper) OnTimeoutIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) error { - k.AppendTimedoutPost( - ctx, - types.TimedoutPost{ - Creator: data.Creator, - Title: data.Title, - Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + seq, err := k.TimeoutPostSeq.Next(ctx) + if err != nil { + return err + } + + return k.TimeoutPost.Set(ctx, seq, + types.TimeoutPost{ + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, }, ) - - return nil } - ``` This last step completes the basic `blog` module setup. The blockchain is now @@ -526,70 +507,52 @@ found` and no action is taken. ### Configure and start the relayer -First, configure the relayer. Use the Ignite CLI `configure` command with the -`--advanced` option: +First, add the Hermes relayer app. ```bash -ignite relayer configure -a \ - --source-rpc "http://0.0.0.0:26657" \ - --source-faucet "http://0.0.0.0:4500" \ - --source-port "blog" \ - --source-version "blog-1" \ - --source-gasprice "0.0000025stake" \ - --source-prefix "cosmos" \ - --source-gaslimit 300000 \ - --target-rpc "http://0.0.0.0:26659" \ - --target-faucet "http://0.0.0.0:4501" \ - --target-port "blog" \ - --target-version "blog-1" \ - --target-gasprice "0.0000025stake" \ - --target-prefix "cosmos" \ - --target-gaslimit 300000 -``` - -When prompted, press Enter to accept the default values for `Source Account` and -`Target Account`. - -The output looks like: - +ignite app install -g github.com/ignite/apps/hermes ``` ---------------------------------------------- -Setting up chains ---------------------------------------------- -๐Ÿ” Account on "source" is "cosmos1xcxgzq75yrxzd0tu2kwmwajv7j550dkj7m00za" - - |ยท received coins from a faucet - |ยท (balance: 100000stake,5token) - -๐Ÿ” Account on "target" is "cosmos1nxg8e4mfp5v7sea6ez23a65rvy0j59kayqr8cx" - - |ยท received coins from a faucet - |ยท (balance: 100000stake,5token) - -โ›“ Configured chains: earth-mars -``` - -In a new terminal window, start the relayer process: +and after configure the relayer. ```bash -ignite relayer connect +ignite relayer hermes configure \ +"earth" "http://localhost:26657" "http://localhost:9090" \ +"mars" "http://localhost:26659" "http://localhost:9092" \ +--chain-a-faucet "http://0.0.0.0:4500" \ +--chain-b-faucet "http://0.0.0.0:4501" \ +--chain-a-port-id "blog" \ +--chain-b-port-id "blog" \ +--channel-version "blog-1" ``` -Results: +When prompted, press Enter to accept the default values for `Chain A Account` and +`Chain B Account`. +The output looks like: + +``` +Hermes config created at /Users/danilopantani/.ignite/relayer/hermes/earth_mars +? Chain earth doesn't have a default Hermes key. Type your mnemonic to continue or type enter to generate a new one: (optional) +New mnemonic generated: danger plate flavor twist chimney myself sketch assist copy expand core tattoo ignore ensure quote mean forum carbon enroll gadget immense grab early maze +Chain earth key created +Chain earth relayer wallet: cosmos1jk6wmyl880j6t9vw6umy9v8ex0yhrfwgx0vv2d +New balance from faucet: 100000stake,5token +? Chain mars doesn't have a default Hermes key. Type your mnemonic to continue or type enter to generate a new one: (optional) +New mnemonic generated: invest box icon session lens demise purse link boss dwarf give minimum jazz eye vocal seven sunset coach express want ask version anger ranch +Chain mars key created +Chain mars relayer wallet: cosmos1x9kt37c0sutanaqwy9gxpvq5990yt0qnpqntmp +New balance from faucet: 100000stake,5token +Client '07-tendermint-0' created (earth -> mars) +Client 07-tendermint-0' created (mars -> earth) +Connection 'earth (connection-0) <-> mars (connection-0)' created +Channel 'earth (channel-0) <-> mars (channel-0)' created ``` ------- -Paths ------- -earth-mars: - earth > (port: blog) (channel: channel-0) - mars > (port: blog) (channel: channel-0) +Now start the relayer: ------- -Listening and relaying packets between chains... ------- +```bash +ignite relayer hermes start "earth" "mars" ``` ### Send packets @@ -649,13 +612,13 @@ planetd tx blog send-ibc-post blog channel-0 "Sorry" "Sorry Mars, you will never Check the timed-out posts: ```bash -planetd q blog list-timedout-post +planetd q blog list-timeout-post ``` Results: ```yaml -TimedoutPost: +TimeoutPost: - chain: blog-channel-0 creator: cosmos1fhpcsxn0g8uask73xpcgwxlfxtuunn3ey5ptjv id: "0" diff --git a/integration/relayer/cmd_relayer_test.go b/integration/relayer/cmd_relayer_test.go index 3e3efef01f..374667f4a1 100644 --- a/integration/relayer/cmd_relayer_test.go +++ b/integration/relayer/cmd_relayer_test.go @@ -127,75 +127,62 @@ var ( }, } - nameSendIbcPost = "SendIbcPost" - funcSendIbcPost = `package keeper -func (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) (*types.MsgSendIbcPostResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - // Construct the packet - var packet types.IbcPostPacketData - packet.Title = msg.Title - packet.Content = msg.Content - // Transmit the packet - _, err := k.TransmitIbcPostPacket( - ctx, - packet, - msg.Port, - msg.ChannelID, - clienttypes.ZeroHeight(), - msg.TimeoutTimestamp, - ) - return &types.MsgSendIbcPostResponse{}, err -}` - nameOnRecvIbcPostPacket = "OnRecvIbcPostPacket" funcOnRecvIbcPostPacket = `package keeper func (k Keeper) OnRecvIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) (packetAck types.IbcPostPacketAck, err error) { - // validate packet data upon receiving - if err := data.ValidateBasic(); err != nil { - return packetAck, err - } - packetAck.PostId = k.AppendPost(ctx, types.Post{Title: data.Title, Content: data.Content}) - return packetAck, nil + packetAck.PostId, err = k.PostSeq.Next(ctx) + if err != nil { + return packetAck, err + } + return packetAck, k.Post.Set(ctx, packetAck.PostId, types.Post{Title: data.Title, Content: data.Content}) }` nameOnAcknowledgementIbcPostPacket = "OnAcknowledgementIbcPostPacket" funcOnAcknowledgementIbcPostPacket = `package keeper func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData, ack channeltypes.Acknowledgement) error { switch dispatchedAck := ack.Response.(type) { - case *channeltypes.Acknowledgement_Error: - // We will not treat acknowledgment error in this tutorial - return nil - case *channeltypes.Acknowledgement_Result: - // Decode the packet acknowledgment - var packetAck types.IbcPostPacketAck - if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { - // The counter-party module doesn't implement the correct acknowledgment format - return errors.New("cannot unmarshal acknowledgment") - } - - k.AppendSentPost(ctx, - types.SentPost{ - PostId: packetAck.PostId, - Title: data.Title, - Chain: packet.DestinationPort + "-" + packet.DestinationChannel, - }, - ) - return nil - default: - return errors.New("the counter-party module does not implement the correct acknowledgment format") - } + case *channeltypes.Acknowledgement_Error: + // We will not treat acknowledgment error in this tutorial + return nil + case *channeltypes.Acknowledgement_Result: + // Decode the packet acknowledgment + var packetAck types.IbcPostPacketAck + if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("cannot unmarshal acknowledgment") + } + + seq, err := k.SentPostSeq.Next(ctx) + if err != nil { + return err + } + + return k.SentPost.Set(ctx, seq, + types.SentPost{ + PostId: packetAck.PostId, + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + }, + ) + default: + return errors.New("the counter-party module does not implement the correct acknowledgment format") + } }` nameOnTimeoutIbcPostPacket = "OnTimeoutIbcPostPacket" funcOnTimeoutIbcPostPacket = `package keeper func (k Keeper) OnTimeoutIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) error { - k.AppendTimeoutPost(ctx, - types.TimeoutPost{ - Title: data.Title, - Chain: packet.DestinationPort + "-" + packet.DestinationChannel, - }, - ) - return nil + seq, err := k.TimeoutPostSeq.Next(ctx) + if err != nil { + return err + } + + return k.TimeoutPost.Set(ctx, seq, + types.TimeoutPost{ + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + }, + ) }` ) @@ -278,7 +265,7 @@ func runChain( func TestBlogIBC(t *testing.T) { var ( env = envtest.New(t) - app = env.Scaffold("github.com/test/planet") + app = env.Scaffold("github.com/ignite/blog", "--no-module") ctx = env.Ctx() tmpDir = t.TempDir() ) @@ -374,11 +361,6 @@ func TestBlogIBC(t *testing.T) { )) blogKeeperPath := filepath.Join(app.SourcePath(), "x/blog/keeper") - require.NoError(t, goanalysis.ReplaceCode( - blogKeeperPath, - nameSendIbcPost, - funcSendIbcPost, - )) require.NoError(t, goanalysis.ReplaceCode( blogKeeperPath, nameOnRecvIbcPostPacket, From 56c3730eb32d42fb98d3c0b15ef3f9343eaca61f Mon Sep 17 00:00:00 2001 From: Ashish Khuraishy Date: Tue, 10 Sep 2024 20:24:27 +0530 Subject: [PATCH 5/9] feat: added survey link (#4345) * feat: added survey link on exit * chore: added change log * feat(view): added announcement api support * added api support to dynamically show announcements * fallbacks to a survey link * unit tests for the same --- changelog.md | 1 + ignite/cmd/model/chain_serve.go | 2 + ignite/pkg/announcements/announcement.go | 50 ++++++++++++++ ignite/pkg/announcements/announcement_test.go | 65 +++++++++++++++++++ ignite/pkg/cliui/icons/icon.go | 10 +-- 5 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 ignite/pkg/announcements/announcement.go create mode 100644 ignite/pkg/announcements/announcement_test.go diff --git a/changelog.md b/changelog.md index de749269dd..9520275964 100644 --- a/changelog.md +++ b/changelog.md @@ -43,6 +43,7 @@ - [#4322](https://github.com/ignite/cli/pull/4322) Create a message for authenticate buf for generate ts-client - [#4319](https://github.com/ignite/cli/pull/4319) Remove fee abstraction module from open api code generation - [#4317](https://github.com/ignite/cli/pull/4317) Remove xchisel dependency +- [#4345](https://github.com/ignite/cli/pull/4345) Added survey link ### Fixes diff --git a/ignite/cmd/model/chain_serve.go b/ignite/cmd/model/chain_serve.go index 5e4429b1d1..0a24d0d9e0 100644 --- a/ignite/cmd/model/chain_serve.go +++ b/ignite/cmd/model/chain_serve.go @@ -7,6 +7,7 @@ import ( tea "github.com/charmbracelet/bubbletea" + "github.com/ignite/cli/v29/ignite/pkg/announcements" "github.com/ignite/cli/v29/ignite/pkg/cliui/colors" "github.com/ignite/cli/v29/ignite/pkg/cliui/icons" cliuimodel "github.com/ignite/cli/v29/ignite/pkg/cliui/model" @@ -260,6 +261,7 @@ func (m ChainServe) renderQuitView() string { } fmt.Fprintf(&view, "%s %s\n", icons.Info, colors.Info("Stopped")) + view.WriteString(announcements.GetAnnouncements()) return view.String() } diff --git a/ignite/pkg/announcements/announcement.go b/ignite/pkg/announcements/announcement.go new file mode 100644 index 0000000000..3de7b67eee --- /dev/null +++ b/ignite/pkg/announcements/announcement.go @@ -0,0 +1,50 @@ +package announcements + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/ignite/cli/v29/ignite/pkg/cliui/icons" +) + +var ( + SurveyLink = "https://bit.ly/3WZS2uS" + AnnouncementAPI = "http://api.ignite.com/announcements" +) + +type announcement struct { + Announcements []string `json:"announcements"` +} + +func GetAnnouncements() string { + resp, err := http.Get(AnnouncementAPI) + if err != nil || resp.StatusCode != 200 { + return fallbackData() + } + defer resp.Body.Close() + + var data announcement + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return fallbackData() + } + + // is this needed? or if its empty we don't want to show anything? + if len(data.Announcements) == 0 { + return fallbackData() + } + + var out strings.Builder + fmt.Fprintf(&out, "\n%s %s\n", icons.Announcement, "Announcements") + + for _, announcement := range data.Announcements { + fmt.Fprintf(&out, "%s %s\n", icons.Bullet, announcement) + } + + return out.String() +} + +func fallbackData() string { + return fmt.Sprintf("\n%s Survey: %s\n", icons.Survey, SurveyLink) +} diff --git a/ignite/pkg/announcements/announcement_test.go b/ignite/pkg/announcements/announcement_test.go new file mode 100644 index 0000000000..fa06e3518f --- /dev/null +++ b/ignite/pkg/announcements/announcement_test.go @@ -0,0 +1,65 @@ +package announcements_test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/ignite/cli/v29/ignite/pkg/announcements" +) + +func TestGetAnnouncements(t *testing.T) { + fallbackData := fmt.Sprintf("\n๐Ÿ’ฌ Survey: %s\n", announcements.SurveyLink) + + tests := []struct { + name string + mockResponse string + statusCode int + expected string + }{ + { + name: "successful retrieval", + mockResponse: `{"announcements":["Announcement 1","Announcement 2"]}`, + statusCode: http.StatusOK, + expected: "\n๐Ÿ—ฃ๏ธ Announcements\nโ‹† Announcement 1\nโ‹† Announcement 2\n", + }, + { + name: "empty announcements", + mockResponse: `{"announcements":[]}`, + statusCode: http.StatusOK, + expected: fallbackData, + }, + { + name: "invalid JSON response", + mockResponse: `invalid json`, + statusCode: http.StatusOK, + expected: fallbackData, + }, + { + name: "non-200 HTTP response", + mockResponse: ``, + statusCode: http.StatusInternalServerError, + expected: fallbackData, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(tt.statusCode) + w.Write([]byte(tt.mockResponse)) + })) + defer server.Close() + + originalAPI := announcements.AnnouncementAPI + announcements.AnnouncementAPI = server.URL + defer func() { announcements.AnnouncementAPI = originalAPI }() + + result := announcements.GetAnnouncements() + if result != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, result) + } + }) + } +} diff --git a/ignite/pkg/cliui/icons/icon.go b/ignite/pkg/cliui/icons/icon.go index 31ad42fc68..85a05dd93b 100644 --- a/ignite/pkg/cliui/icons/icon.go +++ b/ignite/pkg/cliui/icons/icon.go @@ -5,10 +5,12 @@ import ( ) var ( - Earth = "๐ŸŒ" - CD = "๐Ÿ’ฟ" - User = "๐Ÿ‘ค" - Tada = "๐ŸŽ‰" + Earth = "๐ŸŒ" + CD = "๐Ÿ’ฟ" + User = "๐Ÿ‘ค" + Tada = "๐ŸŽ‰" + Survey = "๐Ÿ’ฌ" + Announcement = "๐Ÿ—ฃ๏ธ" // OK is an OK mark. OK = colors.SprintFunc(colors.Green)("โœ”") From 05681456db25910fdd594c8a3d8974f26c1b0e96 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 11 Sep 2024 12:38:31 +0200 Subject: [PATCH 6/9] chore: bump ibc to 8.5.0 (#4341) * chore: bump ibc to 8.5.0 * cl * use pseudo version until 8.5.1 --- changelog.md | 1 + ignite/templates/app/files/go.mod.plush | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 9520275964..4a2aa73e6c 100644 --- a/changelog.md +++ b/changelog.md @@ -43,6 +43,7 @@ - [#4322](https://github.com/ignite/cli/pull/4322) Create a message for authenticate buf for generate ts-client - [#4319](https://github.com/ignite/cli/pull/4319) Remove fee abstraction module from open api code generation - [#4317](https://github.com/ignite/cli/pull/4317) Remove xchisel dependency +- [#4341](https://github.com/ignite/cli/pull/4341) Bump `ibc-go` to `8.5.0` - [#4345](https://github.com/ignite/cli/pull/4345) Added survey link ### Fixes diff --git a/ignite/templates/app/files/go.mod.plush b/ignite/templates/app/files/go.mod.plush index 0f0e4cf1c8..74208d2438 100644 --- a/ignite/templates/app/files/go.mod.plush +++ b/ignite/templates/app/files/go.mod.plush @@ -16,34 +16,34 @@ require ( cosmossdk.io/core v0.11.1 cosmossdk.io/depinject v1.0.0 cosmossdk.io/errors v1.0.1 - cosmossdk.io/log v1.3.1 + cosmossdk.io/log v1.4.0 cosmossdk.io/math v1.3.0 cosmossdk.io/store v1.1.0 - cosmossdk.io/tools/confix v0.1.1 - cosmossdk.io/x/circuit v0.1.0 - cosmossdk.io/x/evidence v0.1.0 - cosmossdk.io/x/feegrant v0.1.0 + cosmossdk.io/tools/confix v0.1.2 + cosmossdk.io/x/circuit v0.1.1 + cosmossdk.io/x/evidence v0.1.1 + cosmossdk.io/x/feegrant v0.1.1 cosmossdk.io/x/nft v0.1.0 cosmossdk.io/x/upgrade v0.1.4 github.com/bufbuild/buf v1.32.1 - github.com/cometbft/cometbft v0.38.10 + github.com/cometbft/cometbft v0.38.11 github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.9 - github.com/cosmos/gogoproto v1.6.0 - github.com/cosmos/ibc-go/modules/capability v1.0.0 - github.com/cosmos/ibc-go/v8 v8.3.1 + github.com/cosmos/gogoproto v1.7.0 + github.com/cosmos/ibc-go/modules/capability v1.0.1 + github.com/cosmos/ibc-go/v8 v8.5.1-0.20240911080511-34a0d0147847 github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 github.com/spf13/cast v1.6.0 - github.com/spf13/cobra v1.8.0 + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d - google.golang.org/genproto/googleapis/api v0.0.0-20240515191416-fc5f0ca64291 + golang.org/x/tools v0.22.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d google.golang.org/grpc v1.64.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/protobuf v1.34.2 From b88bf2f63e995f5b3614763dca56d9f870749dc4 Mon Sep 17 00:00:00 2001 From: Danilo Pantani Date: Wed, 11 Sep 2024 12:52:40 +0200 Subject: [PATCH 7/9] fix: skip upgrade prefix for sim tests (#4350) * skip upgrade prefix * add changelog --- changelog.md | 1 + ignite/templates/app/files/app/sim_test.go.plush | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 4a2aa73e6c..8078f8e125 100644 --- a/changelog.md +++ b/changelog.md @@ -53,6 +53,7 @@ - [#4128](https://github.com/ignite/cli/pull/4128) Check for duplicate proto fields in config - [#4309](https://github.com/ignite/cli/pull/4309) Fix chain id for chain simulations - [#4323](https://github.com/ignite/cli/pull/4323) Add missing `--config` handling in the `chain` commands +- [#4350](https://github.com/ignite/cli/pull/4350) Skip upgrade prefix for sim tests ## [`v28.5.1`](https://github.com/ignite/cli/releases/tag/v28.5.1) diff --git a/ignite/templates/app/files/app/sim_test.go.plush b/ignite/templates/app/files/app/sim_test.go.plush index 0e6fa0ec4d..2f5b4b0aee 100644 --- a/ignite/templates/app/files/app/sim_test.go.plush +++ b/ignite/templates/app/files/app/sim_test.go.plush @@ -15,6 +15,7 @@ import ( "cosmossdk.io/store" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/feegrant" + upgradetypes "cosmossdk.io/x/upgrade/types" abci "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" dbm "github.com/cosmos/cosmos-db" @@ -200,6 +201,9 @@ func TestAppImportExport(t *testing.T) { // skip certain prefixes skipPrefixes := map[string][][]byte{ + upgradetypes.StoreKey: { + []byte{upgradetypes.VersionMapByte}, + }, stakingtypes.StoreKey: { stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIDKey, stakingtypes.UnbondingIndexKey, From b5cdee2ebdc74bbd7bb29fd2afbcff710f298463 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 11 Sep 2024 13:02:46 +0200 Subject: [PATCH 8/9] chore: rename in place testnet command (#4349) Co-authored-by: Danilo Pantani --- ignite/templates/app/files-minimal/app/app.go.plush | 10 ++++++++++ ignite/templates/app/files/app/app.go.plush | 10 ++++++++++ .../cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush | 2 +- .../cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush | 2 +- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/ignite/templates/app/files-minimal/app/app.go.plush b/ignite/templates/app/files-minimal/app/app.go.plush index 4ec41f6cd9..34af557321 100644 --- a/ignite/templates/app/files-minimal/app/app.go.plush +++ b/ignite/templates/app/files-minimal/app/app.go.plush @@ -196,6 +196,16 @@ func (app *App) AppCodec() codec.Codec { return app.appCodec } +// InterfaceRegistry returns App's interfaceRegistry. +func (app *App) InterfaceRegistry() codectypes.InterfaceRegistry { + return app.interfaceRegistry +} + +// TxConfig returns App's tx config. +func (app *App) TxConfig() client.TxConfig { + return app.txConfig +} + // GetKey returns the KVStoreKey for the provided store key. func (app *App) GetKey(storeKey string) *storetypes.KVStoreKey { kvStoreKey, ok := app.UnsafeFindStoreKey(storeKey).(*storetypes.KVStoreKey) diff --git a/ignite/templates/app/files/app/app.go.plush b/ignite/templates/app/files/app/app.go.plush index 5d3293fc45..8bf99b4886 100644 --- a/ignite/templates/app/files/app/app.go.plush +++ b/ignite/templates/app/files/app/app.go.plush @@ -308,6 +308,16 @@ func (app *App) AppCodec() codec.Codec { return app.appCodec } +// InterfaceRegistry returns App's interfaceRegistry. +func (app *App) InterfaceRegistry() codectypes.InterfaceRegistry { + return app.interfaceRegistry +} + +// TxConfig returns App's tx config. +func (app *App) TxConfig() client.TxConfig { + return app.txConfig +} + // GetKey returns the KVStoreKey for the provided store key. func (app *App) GetKey(storeKey string) *storetypes.KVStoreKey { kvStoreKey, ok := app.UnsafeFindStoreKey(storeKey).(*storetypes.KVStoreKey) diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush index 5388a9d8d7..46df14a4b9 100644 --- a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush @@ -35,7 +35,7 @@ func initRootCmd( ) { rootCmd.AddCommand( genutilcli.InitCmd(basicManager, app.DefaultNodeHome), - NewTestnetCmd(addModuleInitFlags), + NewInPlaceTestnetCmd(addModuleInitFlags), debug.Cmd(), confixcmd.ConfigCommand(), pruning.Cmd(newApp, app.DefaultNodeHome), diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush index 0a02a3bdd7..890ce499be 100644 --- a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/testnet.go.plush @@ -46,7 +46,7 @@ type valArgs struct { homeDir string } -func NewTestnetCmd(addStartFlags servertypes.ModuleInitFlags) *cobra.Command { +func NewInPlaceTestnetCmd(addStartFlags servertypes.ModuleInitFlags) *cobra.Command { cmd := server.InPlaceTestnetCreator(newTestnetApp) addStartFlags(cmd) cmd.Short = "Updates chain's application and consensus state with provided validator info and starts the node" From 4efc0997a698873f09a3510aa88ec1975552de5b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:05:16 +0200 Subject: [PATCH 9/9] chore(docs): deploy version v28.5.2 (#4353) Co-authored-by: julienrbrt --- .../version-v28/02-guide/06-ibc.md | 211 ++++++++---------- 1 file changed, 87 insertions(+), 124 deletions(-) diff --git a/docs/versioned_docs/version-v28/02-guide/06-ibc.md b/docs/versioned_docs/version-v28/02-guide/06-ibc.md index faa01a1cae..4c4f426351 100644 --- a/docs/versioned_docs/version-v28/02-guide/06-ibc.md +++ b/docs/versioned_docs/version-v28/02-guide/06-ibc.md @@ -110,21 +110,21 @@ transactions: - Creating blog posts - ```bash - ignite scaffold list post title content creator --no-message --module blog - ``` +```bash +ignite scaffold list post title content creator --no-message --module blog +``` - Processing acknowledgments for sent posts - ```bash - ignite scaffold list sentPost postID title chain creator --no-message --module blog - ``` +```bash +ignite scaffold list sentPost postID:uint title chain creator --no-message --module blog +``` - Managing post timeouts - ```bash - ignite scaffold list timedoutPost title chain creator --no-message --module blog - ``` +```bash +ignite scaffold list timeoutPost title chain creator --no-message --module blog +``` The scaffolded code includes proto files for defining data structures, messages, messages handlers, keepers for modifying the state, and CLI commands. @@ -137,7 +137,7 @@ ignite scaffold list [typeName] [field1] [field2] ... [flags] The first argument of the `ignite scaffold list [typeName]` command specifies the name of the type being created. For the blog app, you created `post`, -`sentPost`, and `timedoutPost` types. +`sentPost`, and `timeoutPost` types. The next arguments define the fields that are associated with the type. For the blog app, you created `title`, `content`, `postID`, and `chain` fields. @@ -168,7 +168,7 @@ to another blockchain. To scaffold a sendable and interpretable IBC packet: ```bash -ignite scaffold packet ibcPost title content --ack postID --module blog +ignite scaffold packet ibcPost title content --ack postID:uint --module blog ``` Notice the fields in the `ibcPost` packet match the fields in the `post` type @@ -222,13 +222,23 @@ the `msg.Creator` value to the IBC `packet`. ```go title="x/blog/keeper/msg_server_ibc_post.go" package keeper -import ( - // ... - "planet/x/blog/types" -) - func (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) (*types.MsgSendIbcPostResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) + // validate incoming message + if _, err := k.addressCodec.StringToBytes(msg.Creator); err != nil { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid address: %s", err)) + } + + if msg.Port == "" { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet port") + } + + if msg.ChannelID == "" { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet channel") + } + + if msg.TimeoutTimestamp == 0 { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet timeout") + } // TODO: logic before transmitting the packet @@ -241,6 +251,7 @@ func (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) packet.Creator = msg.Creator // Transmit the packet + ctx := sdk.UnwrapSDKContext(goCtx) _, err := k.TransmitIbcPostPacket( ctx, packet, @@ -298,44 +309,17 @@ Append the type instance as `PostId` on receiving the packet: - The `title` is the Title of the blog post - The `content` is the Content of the blog post -In the `x/blog/keeper/ibc_post.go` file, make sure to import `"strconv"` below -`"errors"`: - -```go title="x/blog/keeper/ibc_post.go" -import ( - //... - - "strconv" - -// ... -) -``` - Then modify the `OnRecvIbcPostPacket` keeper function with the following code: -```go +```go title="x/blog/keeper/ibc_post.go" package keeper -// ... - func (k Keeper) OnRecvIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) (packetAck types.IbcPostPacketAck, err error) { - // validate packet data upon receiving - if err := data.ValidateBasic(); err != nil { + packetAck.PostId, err = k.PostSeq.Next(ctx) + if err != nil { return packetAck, err } - - id := k.AppendPost( - ctx, - types.Post{ - Creator: packet.SourcePort + "-" + packet.SourceChannel + "-" + data.Creator, - Title: data.Title, - Content: data.Content, - }, - ) - - packetAck.PostId = strconv.FormatUint(id, 10) - - return packetAck, nil + return packetAck, k.Post.Set(ctx, packetAck.PostId, types.Post{Title: data.Title, Content: data.Content}) } ``` @@ -354,9 +338,6 @@ from the packet. ```go title="x/blog/keeper/ibc_post.go" package keeper -// ... - -// x/blog/keeper/ibc_post.go func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData, ack channeltypes.Acknowledgement) error { switch dispatchedAck := ack.Response.(type) { case *channeltypes.Acknowledgement_Error: @@ -365,23 +346,23 @@ func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channelty case *channeltypes.Acknowledgement_Result: // Decode the packet acknowledgment var packetAck types.IbcPostPacketAck - if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { // The counter-party module doesn't implement the correct acknowledgment format return errors.New("cannot unmarshal acknowledgment") } - k.AppendSentPost( - ctx, + seq, err := k.SentPostSeq.Next(ctx) + if err != nil { + return err + } + + return k.SentPost.Set(ctx, seq, types.SentPost{ - Creator: data.Creator, - PostId: packetAck.PostId, - Title: data.Title, - Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + PostId: packetAck.PostId, + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, }, ) - - return nil default: return errors.New("the counter-party module does not implement the correct acknowledgment format") } @@ -390,23 +371,23 @@ func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channelty ### Store information about the timed-out packet -Store posts that have not been received by target chains in `timedoutPost` +Store posts that have not been received by target chains in `timeoutPost` posts. This logic follows the same format as `sentPost`. ```go title="x/blog/keeper/ibc_post.go" func (k Keeper) OnTimeoutIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) error { - k.AppendTimedoutPost( - ctx, - types.TimedoutPost{ - Creator: data.Creator, - Title: data.Title, - Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + seq, err := k.TimeoutPostSeq.Next(ctx) + if err != nil { + return err + } + + return k.TimeoutPost.Set(ctx, seq, + types.TimeoutPost{ + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, }, ) - - return nil } - ``` This last step completes the basic `blog` module setup. The blockchain is now @@ -532,70 +513,52 @@ found` and no action is taken. ### Configure and start the relayer -First, configure the relayer. Use the Ignite CLI `configure` command with the -`--advanced` option: +First, add the Hermes relayer app. ```bash -ignite relayer configure -a \ - --source-rpc "http://0.0.0.0:26657" \ - --source-faucet "http://0.0.0.0:4500" \ - --source-port "blog" \ - --source-version "blog-1" \ - --source-gasprice "0.0000025stake" \ - --source-prefix "cosmos" \ - --source-gaslimit 300000 \ - --target-rpc "http://0.0.0.0:26659" \ - --target-faucet "http://0.0.0.0:4501" \ - --target-port "blog" \ - --target-version "blog-1" \ - --target-gasprice "0.0000025stake" \ - --target-prefix "cosmos" \ - --target-gaslimit 300000 -``` - -When prompted, press Enter to accept the default values for `Source Account` and -`Target Account`. - -The output looks like: - +ignite app install -g github.com/ignite/apps/hermes ``` ---------------------------------------------- -Setting up chains ---------------------------------------------- -๐Ÿ” Account on "source" is "cosmos1xcxgzq75yrxzd0tu2kwmwajv7j550dkj7m00za" - - |ยท received coins from a faucet - |ยท (balance: 100000stake,5token) - -๐Ÿ” Account on "target" is "cosmos1nxg8e4mfp5v7sea6ez23a65rvy0j59kayqr8cx" - - |ยท received coins from a faucet - |ยท (balance: 100000stake,5token) - -โ›“ Configured chains: earth-mars -``` - -In a new terminal window, start the relayer process: +and after configure the relayer. ```bash -ignite relayer connect +ignite relayer hermes configure \ +"earth" "http://localhost:26657" "http://localhost:9090" \ +"mars" "http://localhost:26659" "http://localhost:9092" \ +--chain-a-faucet "http://0.0.0.0:4500" \ +--chain-b-faucet "http://0.0.0.0:4501" \ +--chain-a-port-id "blog" \ +--chain-b-port-id "blog" \ +--channel-version "blog-1" ``` -Results: +When prompted, press Enter to accept the default values for `Chain A Account` and +`Chain B Account`. +The output looks like: + +``` +Hermes config created at /Users/danilopantani/.ignite/relayer/hermes/earth_mars +? Chain earth doesn't have a default Hermes key. Type your mnemonic to continue or type enter to generate a new one: (optional) +New mnemonic generated: danger plate flavor twist chimney myself sketch assist copy expand core tattoo ignore ensure quote mean forum carbon enroll gadget immense grab early maze +Chain earth key created +Chain earth relayer wallet: cosmos1jk6wmyl880j6t9vw6umy9v8ex0yhrfwgx0vv2d +New balance from faucet: 100000stake,5token +? Chain mars doesn't have a default Hermes key. Type your mnemonic to continue or type enter to generate a new one: (optional) +New mnemonic generated: invest box icon session lens demise purse link boss dwarf give minimum jazz eye vocal seven sunset coach express want ask version anger ranch +Chain mars key created +Chain mars relayer wallet: cosmos1x9kt37c0sutanaqwy9gxpvq5990yt0qnpqntmp +New balance from faucet: 100000stake,5token +Client '07-tendermint-0' created (earth -> mars) +Client 07-tendermint-0' created (mars -> earth) +Connection 'earth (connection-0) <-> mars (connection-0)' created +Channel 'earth (channel-0) <-> mars (channel-0)' created ``` ------- -Paths ------- -earth-mars: - earth > (port: blog) (channel: channel-0) - mars > (port: blog) (channel: channel-0) +Now start the relayer: ------- -Listening and relaying packets between chains... ------- +```bash +ignite relayer hermes start "earth" "mars" ``` ### Send packets @@ -655,13 +618,13 @@ planetd tx blog send-ibc-post blog channel-0 "Sorry" "Sorry Mars, you will never Check the timed-out posts: ```bash -planetd q blog list-timedout-post +planetd q blog list-timeout-post ``` Results: ```yaml -TimedoutPost: +TimeoutPost: - chain: blog-channel-0 creator: cosmos1fhpcsxn0g8uask73xpcgwxlfxtuunn3ey5ptjv id: "0"