diff --git a/changelog.md b/changelog.md index 040da93de2..6774e6d873 100644 --- a/changelog.md +++ b/changelog.md @@ -5,10 +5,13 @@ ### Features - [#2955](https://github.com/ignite/cli/pull/2955/) Add `ignite network request add-account` command. +- [#2995](https://github.com/ignite/cli/pull/2995/) Add `ignite network request remove-validator` command. ### Changes - [#2957](https://github.com/ignite/cli/pull/2957) Change generate commands to print the path to the generated code. +- [#2981](https://github.com/ignite/cli/issues/2981) Change CLI to also search chain binary in Go binary path. +- [#2993](https://github.com/ignite/cli/pull/2993) Hide `ignite scaffold band` command and deprecate functionality. ## [`v0.25.1`](https://github.com/ignite/cli/releases/tag/v0.25.1) diff --git a/docs/docs/07-cli.md b/docs/docs/07-cli.md index 68fb0603cb..8feb5feb80 100644 --- a/docs/docs/07-cli.md +++ b/docs/docs/07-cli.md @@ -2664,7 +2664,6 @@ with an "--ibc" flag. Note that the default module is not IBC-enabled. **SEE ALSO** * [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain -* [ignite scaffold band](#ignite-scaffold-band) - Scaffold an IBC BandChain query oracle to request real-time data * [ignite scaffold chain](#ignite-scaffold-chain) - Fully-featured Cosmos SDK blockchain * [ignite scaffold flutter](#ignite-scaffold-flutter) - A Flutter app for your chain * [ignite scaffold list](#ignite-scaffold-list) - CRUD for data stored as an array @@ -2677,35 +2676,6 @@ with an "--ibc" flag. Note that the default module is not IBC-enabled. * [ignite scaffold type](#ignite-scaffold-type) - Scaffold only a type definition * [ignite scaffold vue](#ignite-scaffold-vue) - Vue 3 web app template - -## ignite scaffold band - -Scaffold an IBC BandChain query oracle to request real-time data - -**Synopsis** - -Scaffold an IBC BandChain query oracle to request real-time data from BandChain scripts in a specific IBC-enabled Cosmos SDK module - -``` -ignite scaffold band [queryName] --module [moduleName] [flags] -``` - -**Options** - -``` - --clear-cache clear the build cache (advanced) - -h, --help help for band - --module string IBC Module to add the packet into - -p, --path string path of the app (default ".") - --signer string Label for the message signer (default: creator) - -y, --yes answers interactive yes/no questions with yes -``` - -**SEE ALSO** - -* [ignite scaffold](#ignite-scaffold) - Scaffold a new blockchain, module, message, query, and more - - ## ignite scaffold chain Fully-featured Cosmos SDK blockchain diff --git a/docs/docs/guide/03-blog/02-connect-blockchain.md b/docs/docs/guide/03-blog/02-connect-blockchain.md index c82258c33a..1bd03c04c2 100644 --- a/docs/docs/guide/03-blog/02-connect-blockchain.md +++ b/docs/docs/guide/03-blog/02-connect-blockchain.md @@ -48,7 +48,7 @@ go 1.18 require ( blog v0.0.0-00010101000000-000000000000 - github.com/ignite/cli v0.23.0 + github.com/ignite/cli v0.25.1 ) replace blog => ../blog diff --git a/docs/docs/kb/10-band.md b/docs/docs/kb/10-band.md deleted file mode 100644 index ddd3d9e0e0..0000000000 --- a/docs/docs/kb/10-band.md +++ /dev/null @@ -1,364 +0,0 @@ ---- -sidebar_position: 10 -description: IBC oracle integration with BandChain ---- - -# BandChain oracle - -The BandChain oracle communication module has built-in compliance using IBC protocol that can query data points of various types from BandChain. - -Other chains can query this oracle module for real-time information. - -BandChain has multiple scripts deployed into the network. You can request any data using the script id. - -## High-level overview - -Steps to scaffold an IBC BandChain query oracle to request real-time data from BandChain scripts in a specific IBC-enabled Cosmos SDK module. - -## IBC module packet scaffold - -BandChain oracle queries can be scaffolded only in IBC modules. - -The basic syntax to scaffold a band oracle module is: - -```bash -ignite scaffold band [queryName] --module [moduleName] -``` - -Customize your band oracle with flags: - -- --module string - name of the new IBC Module to add the packets to -- --path string - path of the app, default is the current directory (`"."`) -- --signer string - signer label, default is `creator` - -### Acknowledgement messages - -The BandChain oracle returns the ack messages with the request id. The last request id is saved for future queries. - -## Files and directories - -When you scaffold a BandChain oracle module, the following files and directories are created and modified: - -- `proto`: oracle request and response data. -- `x/module_name/keeper`: IBC hooks, gRPC message server. -- `x/module_name/types`: message types, IBC events. -- `x/module_name/client/cli`: CLI command to broadcast a transaction containing a message with a packet. -- `x/module_name/oracle.go`: BandChain oracle packet handlers. - -## Scaffold a BandChain oracle chain - -First, scaffold a chain but don't scaffold a default module: - -```bash -ignite scaffold chain oracle --no-module -``` - -Next, change to the new `oracle` directory and scaffold an IBC-enabled module named `consuming`: - -```bash -cd oracle -ignite scaffold module consuming --ibc -``` - -Finally, scaffold a BandChain query oracle that can request real-time data: - -```bash -ignite scaffold band coinRates --module consuming -``` - -So far, you have scaffolded: - -- A new `oracle` chain without a default module -- A new IBC-enabled `consuming` module -- A new `coinRates` BandChain query oracle - -Now it's time to change the data. - -## Update version - -The output of the `ignite scaffold band coinRates --module consuming` command prompts you to update the `keys.go` file. - -In the `x/consuming/types/keys.go` file, update the `Version` variable in the `const` block to the required version that the IBC module supports: - -```go -const ( - // ... - - // Version defines the current version the IBC module supports - Version = "bandchain-1" - - // ... -) -``` - -## Start your chain in development - -To run the chain from the `oracle` directory: - -```bash -ignite chain serve -``` - -Keep this terminal window open. - -## Configure and connect the Ignite CLI relayer - -If you previously used the Ignite CLI relayer, it is a good idea to remove existing relayer and Ignite CLI configurations: - -1. Stop your blockchains. -2. Delete previous configuration files: - - ```bash - rm -rf ~/.ignite/relayer - ``` - -3. Restart your blockchains. - -In another terminal tab, configure the [Ignite CLI relayer](./08-relayer.md): - -```bash -ignite relayer configure -a \ ---source-rpc "http://rpc-laozi-testnet4.bandchain.org:80" \ ---source-faucet "https://laozi-testnet4.bandchain.org/faucet" \ ---source-port "oracle" \ ---source-gasprice "0uband" \ ---source-gaslimit 5000000 \ ---source-prefix "band" \ ---source-version "bandchain-1" \ ---target-rpc "http://localhost:26657" \ ---target-faucet "http://localhost:4500" \ ---target-port "consuming" \ ---target-gasprice "0.0stake" \ ---target-gaslimit 300000 \ ---target-prefix "cosmos" \ ---target-version "bandchain-1" -``` - -When prompted, press Enter to accept the default source and target accounts. - -The command output confirms the relayer is successfully configured: - -``` -? Source Account default -? Target Account default - -๐Ÿ” Account on "source" is default(band1dscvlx0mhpys9fazuk7ej9z4cq7qknzn09pjpq) - - |ยท received coins from a faucet - |ยท (balance: 10000000uband) - -๐Ÿ” Account on "target" is default(cosmos1dscvlx0mhpys9fazuk7ej9z4cq7qknznk2pseg) - - |ยท received coins from a faucet - |ยท (balance: 100000stake,5token) - -โ›“ Configured chains: band-laozi-testnet4-oracle -``` - -Connect the relayer: - -```bash -ignite relayer connect -``` - -You can see the paths of the `oracle` port on the testnet and the `consuming` port on your local oracle module in the relayer connection status that is output to the terminal: - -``` ------- -Paths ------- - -band-laozi-testnet4-oracle: - band-laozi-testnet4 > (port: oracle) (channel: channel-405) - oracle > (port: consuming) (channel: channel-0) - ------- -Listening and relaying packets between chains... ------- -``` - -Leave this terminal tab open so you can monitor the relayer. - -## Make a request transaction - -In another terminal tab, use the `oracled` binary to make a request transaction. Because BandChain has multiple scripts already deployed into the network, you can request any data using the BandChain script id. In this case, use script 37 for Coin Rates: - -```bash -# Coin Rates (script 37 into the testnet) -oracled tx consuming coin-rates-data 37 4 3 --channel channel-0 --symbols "BTC,ETH,XRP,BCH" --multiplier 1000000 --fee-limit 30uband --prepare-gas 600000 --execute-gas 600000 --from alice --chain-id oracle -``` - -You can check the last request id that was returned by ack: - -```bash -oracled query consuming last-coin-rates-id -# output: request_id: "101276" -``` - -Now you can check the data by request id to receive the data packet: - -```bash -oracled query consuming coin-rates-result 101276 -``` - -### Multiple oracles - -You can scaffold multiples oracles by module. After scaffold, you must change the `Calldata` and `Result` parameters in the proto file `moduleName.proto` and then adapt the request in the `cli/client/tx_module_name.go` file. - -To create an example for the [gold price](https://laozi-testnet4.cosmoscan.io/oracle-script/33#bridge) bridge: - -```bash -ignite scaffold band goldPrice --module consuming -``` - -In the `proto/consuming/gold_price.proto` file: - -```protobuf -syntax = "proto3"; -package oracle.consuming; - -option go_package = "oracle/x/consuming/types"; - -message GoldPriceCallData { - uint64 multiplier = 2; -} - -message GoldPriceResult { - uint64 price = 1; -} -``` - -In the `x/consuming/cli/client/tx_gold_price.go` file: - -```go -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/cobra" - - "oracle/x/consuming/types" -) - -// CmdRequestGoldPriceData creates and broadcast a GoldPrice request transaction -func CmdRequestGoldPriceData() *cobra.Command { - cmd := &cobra.Command{ - Use: "gold-price-data [oracle-script-id] [requested-validator-count] [sufficient-validator-count]", - Short: "Make a new GoldPrice query request via an existing BandChain oracle script", - Args: cobra.ExactArgs(3), - RunE: func(cmd *cobra.Command, args []string) error { - // retrieve the oracle script id. - uint64OracleScriptID, err := strconv.ParseUint(args[0], 10, 64) - if err != nil { - return err - } - oracleScriptID := types.OracleScriptID(uint64OracleScriptID) - - // retrieve the requested validator count. - askCount, err := strconv.ParseUint(args[1], 10, 64) - if err != nil { - return err - } - - // retrieve the sufficient(minimum) validator count. - minCount, err := strconv.ParseUint(args[2], 10, 64) - if err != nil { - return err - } - - channel, err := cmd.Flags().GetString(flagChannel) - if err != nil { - return err - } - - // retrieve the multiplier for the symbols' price. - multiplier, err := cmd.Flags().GetUint64(flagMultiplier) - if err != nil { - return err - } - - calldata := &types.GoldPriceCallData{ - Multiplier: multiplier, - } - - // retrieve the amount of coins allowed to be paid for oracle request fee from the pool account. - coinStr, err := cmd.Flags().GetString(flagFeeLimit) - if err != nil { - return err - } - feeLimit, err := sdk.ParseCoinsNormalized(coinStr) - if err != nil { - return err - } - - // retrieve the amount of gas allowed for the prepare step of the oracle script. - prepareGas, err := cmd.Flags().GetUint64(flagPrepareGas) - if err != nil { - return err - } - - // retrieve the amount of gas allowed for the execute step of the oracle script. - executeGas, err := cmd.Flags().GetUint64(flagExecuteGas) - if err != nil { - return err - } - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgGoldPriceData( - clientCtx.GetFromAddress().String(), - oracleScriptID, - channel, - calldata, - askCount, - minCount, - feeLimit, - prepareGas, - executeGas, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - cmd.Flags().String(flagChannel, "", "The channel id") - cmd.MarkFlagRequired(flagChannel) - cmd.Flags().Uint64(flagMultiplier, 1000000, "Multiplier used in calling the oracle script") - cmd.Flags().String(flagFeeLimit, "", "the maximum tokens that will be paid to all data source providers") - cmd.Flags().Uint64(flagPrepareGas, 200000, "Prepare gas used in fee counting for prepare request") - cmd.Flags().Uint64(flagExecuteGas, 200000, "Execute gas used in fee counting for execute request") - flags.AddTxFlagsToCmd(cmd) - - return cmd -} -``` - -Make the request transaction: - -```bash -# Gold Price (script 33 into the testnet) -oracled tx consuming gold-price-data 33 4 3 --channel channel-0 --multiplier 1000000 --fee-limit 30uband --prepare-gas 600000 --execute-gas 600000 --from alice --chain-id oracle -``` - -Check the last request id that was returned by ack: - -```bash -oracled query consuming last-gold-price-id -# output: request_id: "101290" -``` - -Request the package data: - -```bash -oracled query consuming gold-price-result 101290 -``` diff --git a/docs/docs/kb/11-simapp.md b/docs/docs/kb/10-simapp.md similarity index 99% rename from docs/docs/kb/11-simapp.md rename to docs/docs/kb/10-simapp.md index f482740878..40740f4c63 100644 --- a/docs/docs/kb/11-simapp.md +++ b/docs/docs/kb/10-simapp.md @@ -1,5 +1,5 @@ --- -sidebar_position: 11 +sidebar_position: 10 description: Test different scenarios for your chain. --- diff --git a/docs/docs/kb/12-params.md b/docs/docs/kb/11-params.md similarity index 98% rename from docs/docs/kb/12-params.md rename to docs/docs/kb/11-params.md index cb25b29c2b..3d6d52ff46 100644 --- a/docs/docs/kb/12-params.md +++ b/docs/docs/kb/11-params.md @@ -1,5 +1,5 @@ --- -sidebar_position: 12 +sidebar_position: 11 description: Scaffold module parameters to be accessible to the module. --- diff --git a/ignite/cmd/network_request.go b/ignite/cmd/network_request.go index 49e37b9c02..72c5d55118 100644 --- a/ignite/cmd/network_request.go +++ b/ignite/cmd/network_request.go @@ -17,6 +17,7 @@ func NewNetworkRequest() *cobra.Command { NewNetworkRequestReject(), NewNetworkRequestVerify(), NewNetworkRequestAddAccount(), + NewNetworkRequestRemoveValidator(), ) return c diff --git a/ignite/cmd/network_request_remove_validator.go b/ignite/cmd/network_request_remove_validator.go new file mode 100644 index 0000000000..296076addf --- /dev/null +++ b/ignite/cmd/network_request_remove_validator.go @@ -0,0 +1,60 @@ +package ignitecmd + +import ( + "github.com/spf13/cobra" + + "github.com/ignite/cli/ignite/pkg/cliui" + "github.com/ignite/cli/ignite/pkg/cosmosutil" + "github.com/ignite/cli/ignite/services/network" + "github.com/ignite/cli/ignite/services/network/networktypes" +) + +// NewNetworkRequestRemoveValidator creates a new command to send remove validator request +func NewNetworkRequestRemoveValidator() *cobra.Command { + c := &cobra.Command{ + Use: "remove-validator [launch-id] [address]", + Short: "Send request to remove validator", + RunE: networkRequestRemoveValidatorHandler, + Args: cobra.ExactArgs(2), + } + + flagSetClearCache(c) + c.Flags().AddFlagSet(flagNetworkFrom()) + c.Flags().AddFlagSet(flagSetHome()) + c.Flags().AddFlagSet(flagSetKeyringBackend()) + c.Flags().AddFlagSet(flagSetKeyringDir()) + return c +} + +func networkRequestRemoveValidatorHandler(cmd *cobra.Command, args []string) error { + session := cliui.New(cliui.StartSpinner()) + defer session.End() + + nb, err := newNetworkBuilder(cmd, CollectEvents(session.EventBus())) + if err != nil { + return err + } + + // parse launch ID + launchID, err := network.ParseID(args[0]) + if err != nil { + return err + } + + // get the address for the account and change the prefix for Ignite Chain + address, err := cosmosutil.ChangeAddressPrefix(args[1], networktypes.SPN) + if err != nil { + return err + } + + n, err := nb.Network() + if err != nil { + return err + } + + return n.SendValidatorRemoveRequest( + cmd.Context(), + launchID, + address, + ) +} diff --git a/ignite/cmd/scaffold_band.go b/ignite/cmd/scaffold_band.go index a8a4f5cf87..df6e35e9e5 100644 --- a/ignite/cmd/scaffold_band.go +++ b/ignite/cmd/scaffold_band.go @@ -12,13 +12,10 @@ import ( const tplScaffoldBandSuccess = ` ๐ŸŽ‰ Created a Band oracle query "%[1]v". - Note: BandChain module uses version "bandchain-1". Make sure to update the keys.go file accordingly. - // x/%[2]v/types/keys.go const Version = "bandchain-1" - ` // NewScaffoldBandchain creates a new BandChain oracle in the module @@ -30,6 +27,7 @@ func NewScaffoldBandchain() *cobra.Command { Args: cobra.MinimumNArgs(1), PreRunE: gitChangesConfirmPreRunHandler, RunE: createBandchainHandler, + Hidden: true, } flagSetPath(c) @@ -67,7 +65,7 @@ func createBandchainHandler(cmd *cobra.Command, args []string) error { var options []scaffolder.OracleOption if signer != "" { - options = append(options, scaffolder.OracleWithSigner(signer)) + options = append(options, scaffolder.OracleWithSigner(signer)) // nolint: staticcheck } sc, err := newApp(appPath) @@ -75,6 +73,7 @@ func createBandchainHandler(cmd *cobra.Command, args []string) error { return err } + // nolint: staticcheck sm, err := sc.AddOracle(cmd.Context(), cacheStorage, placeholder.New(), module, oracle, options...) if err != nil { return err diff --git a/ignite/pkg/checksum/checksum.go b/ignite/pkg/checksum/checksum.go index 4961e1b636..36f6b7e099 100644 --- a/ignite/pkg/checksum/checksum.go +++ b/ignite/pkg/checksum/checksum.go @@ -6,8 +6,9 @@ import ( "fmt" "io" "os" - "os/exec" "path/filepath" + + "github.com/ignite/cli/ignite/pkg/xexec" ) // Sum reads files from dirPath, calculates sha256 for each file and creates a new checksum @@ -44,7 +45,7 @@ func Sum(dirPath, outPath string) error { // Binary returns SHA256 hash of executable file, file is searched by name in PATH func Binary(binaryName string) (string, error) { // get binary path - binaryPath, err := exec.LookPath(binaryName) + binaryPath, err := xexec.ResolveAbsPath(binaryName) if err != nil { return "", err } diff --git a/ignite/pkg/xexec/command.go b/ignite/pkg/xexec/command.go deleted file mode 100644 index 06b275e04b..0000000000 --- a/ignite/pkg/xexec/command.go +++ /dev/null @@ -1,9 +0,0 @@ -package xexec - -import "os/exec" - -// IsCommandAvailable checks if command is available on user's path. -func IsCommandAvailable(name string) bool { - _, err := exec.LookPath(name) - return err == nil -} diff --git a/ignite/pkg/xexec/testdata/bin.sh b/ignite/pkg/xexec/testdata/bin.sh new file mode 100755 index 0000000000..a9bf588e2f --- /dev/null +++ b/ignite/pkg/xexec/testdata/bin.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/ignite/pkg/xexec/testdata/nobin b/ignite/pkg/xexec/testdata/nobin new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ignite/pkg/xexec/xexec.go b/ignite/pkg/xexec/xexec.go new file mode 100644 index 0000000000..2d185f517f --- /dev/null +++ b/ignite/pkg/xexec/xexec.go @@ -0,0 +1,68 @@ +package xexec + +import ( + "os" + "os/exec" + "path/filepath" + + "github.com/ignite/cli/ignite/pkg/goenv" +) + +// IsExec checks if a file is executable by anyone. +func IsExec(binaryPath string) (bool, error) { + info, err := os.Stat(binaryPath) + if err != nil { + return false, err + } + + if m := info.Mode(); !m.IsDir() && m&0o111 != 0 { + return true, nil + } + + return false, nil +} + +// ResolveAbsPath searches for an executable file in the current +// working directory, the directories defined by the PATH environment +// variable and in the Go binary path. Once found returns the absolute +// path to the file. +func ResolveAbsPath(filePath string) (path string, err error) { + // Check if file exists and it's an executable file + if path, err = filepath.Abs(filePath); err == nil { + if ok, _ := IsExec(path); ok { + return path, nil + } + } + + // Search file in the directories defined by the PATH env variable + path, err = exec.LookPath(filePath) + if err == nil { + return path, nil + } + + // When PATH search fails check if file is located in the Go binary path + path = filepath.Join(goenv.Bin(), filePath) + if ok, _ := IsExec(path); ok { + return path, nil + } + + return "", err +} + +// TryResolveAbsPath searches for an executable file in the current +// working directory, the directories defined by the PATH environment +// variable and in the Go binary path. Once found returns the absolute +// path to the file or otherwise it returns the file path unmodified. +func TryResolveAbsPath(filePath string) string { + if path, err := ResolveAbsPath(filePath); err == nil { + return path + } + + return filePath +} + +// IsCommandAvailable checks if command is available on user's path. +func IsCommandAvailable(name string) bool { + _, err := ResolveAbsPath(name) + return err == nil +} diff --git a/ignite/pkg/xexec/xexec_test.go b/ignite/pkg/xexec/xexec_test.go new file mode 100644 index 0000000000..e55c1eddca --- /dev/null +++ b/ignite/pkg/xexec/xexec_test.go @@ -0,0 +1,152 @@ +package xexec_test + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ignite/cli/ignite/pkg/xexec" +) + +func TestIsExec(t *testing.T) { + cases := []struct { + name, path string + want bool + }{ + { + name: "executable", + path: "testdata/bin.sh", + want: true, + }, + { + name: "not_executable", + path: "testdata/nobin", + want: false, + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + // Act + ok, err := xexec.IsExec(tt.path) + + // Assert + require.NoError(t, err) + require.Equal(t, tt.want, ok) + }) + } +} + +func TestResolveAbsPath(t *testing.T) { + // Get the absolute path to the testdata directory + testdata, err := filepath.Abs("testdata") + require.NoError(t, err) + + cases := []struct { + name, path, want string + env []string + }{ + { + name: "relative", + path: "testdata/bin.sh", + want: filepath.Join(testdata, "bin.sh"), + }, + { + name: "path", + path: "bin.sh", + env: []string{"PATH", testdata}, + want: filepath.Join(testdata, "bin.sh"), + }, + { + name: "go bin path", + path: "bin.sh", + env: []string{"GOBIN", testdata}, + want: filepath.Join(testdata, "bin.sh"), + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + // Arrange + if tt.env != nil { + t.Setenv(tt.env[0], tt.env[1]) + } + + // Act + path, err := xexec.ResolveAbsPath(tt.path) + + // Assert + require.NoError(t, err) + require.Equal(t, tt.want, path) + }) + } +} + +func TestResolveAbsPathError(t *testing.T) { + // Arrange + fileName := "invalid-file.ko" + + // Act + _, err := xexec.ResolveAbsPath(fileName) + + // Assert + require.Errorf(t, err, `exec: "%s": executable file not found in $PATH`, fileName) +} + +func TestTryResolveAbsPath(t *testing.T) { + // Get the absolute path to the testdata directory + testdata, err := filepath.Abs("testdata") + require.NoError(t, err) + + cases := []struct { + name, path, want string + env []string + }{ + { + name: "valid file", + path: "testdata/bin.sh", + want: filepath.Join(testdata, "bin.sh"), + }, + { + name: "invalid file", + path: "invalid-file.ko", + want: "invalid-file.ko", + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + // Act + path := xexec.TryResolveAbsPath(tt.path) + + // Assert + require.NoError(t, err) + require.Equal(t, tt.want, path) + }) + } +} + +func TestIsCommandAvailable(t *testing.T) { + cases := []struct { + name, path string + want bool + }{ + { + name: "available", + path: "testdata/bin.sh", + want: true, + }, + { + name: "not_available", + path: "invalid-file.ko", + want: false, + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + // Act + ok := xexec.IsCommandAvailable(tt.path) + + // Assert + require.Equal(t, tt.want, ok) + }) + } +} diff --git a/ignite/services/chain/chain.go b/ignite/services/chain/chain.go index 9cc82b70f2..6213b81558 100644 --- a/ignite/services/chain/chain.go +++ b/ignite/services/chain/chain.go @@ -18,6 +18,7 @@ import ( "github.com/ignite/cli/ignite/pkg/cosmosver" "github.com/ignite/cli/ignite/pkg/events" "github.com/ignite/cli/ignite/pkg/repoversion" + "github.com/ignite/cli/ignite/pkg/xexec" "github.com/ignite/cli/ignite/pkg/xurl" ) @@ -444,6 +445,11 @@ func (c *Chain) Commands(ctx context.Context) (chaincmdrunner.Runner, error) { return chaincmdrunner.Runner{}, err } + // Try to make the binary path absolute. This will also + // find the binary path when the Go bin path is not part + // of the PATH environment variable. + binary = xexec.TryResolveAbsPath(binary) + backend, err := c.KeyringBackend() if err != nil { return chaincmdrunner.Runner{}, err diff --git a/ignite/services/chain/serve.go b/ignite/services/chain/serve.go index 4d9b0df286..2382401481 100644 --- a/ignite/services/chain/serve.go +++ b/ignite/services/chain/serve.go @@ -329,7 +329,7 @@ func (c *Chain) serve(ctx context.Context, cacheStorage cache.Storage, forceRese if err != nil { return err } - binaryPath, err := exec.LookPath(binaryName) + binaryPath, err := xexec.ResolveAbsPath(binaryName) if err != nil { if !errors.Is(err, exec.ErrNotFound) { return err @@ -397,7 +397,7 @@ func (c *Chain) serve(ctx context.Context, cacheStorage cache.Storage, forceRese if err := dirchange.SaveDirChecksum(dirCache, sourceChecksumKey, c.app.Path, appBackendSourceWatchPaths...); err != nil { return err } - binaryPath, err = exec.LookPath(binaryName) + binaryPath, err = xexec.ResolveAbsPath(binaryName) if err != nil { return err } diff --git a/ignite/services/network/join.go b/ignite/services/network/join.go index 0261c3c4af..7646567db1 100644 --- a/ignite/services/network/join.go +++ b/ignite/services/network/join.go @@ -2,14 +2,11 @@ package network import ( "context" - "fmt" sdk "github.com/cosmos/cosmos-sdk/types" launchtypes "github.com/tendermint/spn/x/launch/types" - "github.com/ignite/cli/ignite/pkg/cliui/icons" "github.com/ignite/cli/ignite/pkg/cosmosutil" - "github.com/ignite/cli/ignite/pkg/events" "github.com/ignite/cli/ignite/pkg/xurl" "github.com/ignite/cli/ignite/services/network/networkchain" "github.com/ignite/cli/ignite/services/network/networktypes" @@ -102,103 +99,3 @@ func (n Network) SendAccountRequestForCoordinator(ctx context.Context, launchID return n.SendAccountRequest(ctx, launchID, addr, amount) } - -// SendAccountRequest creates an add AddAccount request message. -func (n Network) SendAccountRequest( - ctx context.Context, - launchID uint64, - address string, - amount sdk.Coins, -) error { - addr, err := n.account.Address(networktypes.SPN) - if err != nil { - return err - } - - msg := launchtypes.NewMsgSendRequest( - addr, - launchID, - launchtypes.NewGenesisAccount( - launchID, - address, - amount, - ), - ) - - n.ev.Send("Broadcasting account transactions", events.ProgressStart()) - - res, err := n.cosmos.BroadcastTx(ctx, n.account, msg) - if err != nil { - return err - } - - var requestRes launchtypes.MsgSendRequestResponse - if err := res.Decode(&requestRes); err != nil { - return err - } - - if requestRes.AutoApproved { - n.ev.Send( - "Account added to the network by the coordinator!", - events.Icon(icons.Bullet), - events.ProgressFinish(), - ) - } else { - n.ev.Send( - fmt.Sprintf("Request %d to add account to the network has been submitted!", requestRes.RequestID), - events.Icon(icons.Bullet), - events.ProgressFinish(), - ) - } - return nil -} - -// SendValidatorRequest creates the RequestAddValidator message into the SPN -func (n Network) SendValidatorRequest( - ctx context.Context, - launchID uint64, - peer launchtypes.Peer, - valAddress string, - gentx []byte, - gentxInfo cosmosutil.GentxInfo, -) error { - addr, err := n.account.Address(networktypes.SPN) - if err != nil { - return err - } - - msg := launchtypes.NewMsgSendRequest( - addr, - launchID, - launchtypes.NewGenesisValidator( - launchID, - valAddress, - gentx, - gentxInfo.PubKey, - gentxInfo.SelfDelegation, - peer, - ), - ) - - n.ev.Send("Broadcasting validator transaction", events.ProgressStart()) - - res, err := n.cosmos.BroadcastTx(ctx, n.account, msg) - if err != nil { - return err - } - - var requestRes launchtypes.MsgSendRequestResponse - if err := res.Decode(&requestRes); err != nil { - return err - } - - if requestRes.AutoApproved { - n.ev.Send("Validator added to the network by the coordinator!", events.ProgressFinish()) - } else { - n.ev.Send( - fmt.Sprintf("Request %d to join the network as a validator has been submitted!", requestRes.RequestID), - events.ProgressFinish(), - ) - } - return nil -} diff --git a/ignite/services/network/request.go b/ignite/services/network/request.go index f3c2453c6f..5a479c81d4 100644 --- a/ignite/services/network/request.go +++ b/ignite/services/network/request.go @@ -2,6 +2,10 @@ package network import ( "context" + "fmt" + + "github.com/ignite/cli/ignite/pkg/cliui/icons" + "github.com/ignite/cli/ignite/pkg/cosmosutil" sdk "github.com/cosmos/cosmos-sdk/types" launchtypes "github.com/tendermint/spn/x/launch/types" @@ -99,3 +103,147 @@ func (n Network) SubmitRequest(ctx context.Context, launchID uint64, reviewal .. var requestRes launchtypes.MsgSettleRequestResponse return res.Decode(&requestRes) } + +// SendAccountRequest creates an add AddAccount request message. +func (n Network) SendAccountRequest( + ctx context.Context, + launchID uint64, + address string, + amount sdk.Coins, +) error { + addr, err := n.account.Address(networktypes.SPN) + if err != nil { + return err + } + + msg := launchtypes.NewMsgSendRequest( + addr, + launchID, + launchtypes.NewGenesisAccount( + launchID, + address, + amount, + ), + ) + + n.ev.Send("Broadcasting account transactions", events.ProgressStart()) + + res, err := n.cosmos.BroadcastTx(ctx, n.account, msg) + if err != nil { + return err + } + + var requestRes launchtypes.MsgSendRequestResponse + if err := res.Decode(&requestRes); err != nil { + return err + } + + if requestRes.AutoApproved { + n.ev.Send( + "Account added to the network by the coordinator!", + events.Icon(icons.Bullet), + events.ProgressFinish(), + ) + } else { + n.ev.Send( + fmt.Sprintf("Request %d to add account to the network has been submitted!", requestRes.RequestID), + events.Icon(icons.Bullet), + events.ProgressFinish(), + ) + } + return nil +} + +// SendValidatorRequest creates the RequestAddValidator message into the SPN +func (n Network) SendValidatorRequest( + ctx context.Context, + launchID uint64, + peer launchtypes.Peer, + valAddress string, + gentx []byte, + gentxInfo cosmosutil.GentxInfo, +) error { + addr, err := n.account.Address(networktypes.SPN) + if err != nil { + return err + } + + msg := launchtypes.NewMsgSendRequest( + addr, + launchID, + launchtypes.NewGenesisValidator( + launchID, + valAddress, + gentx, + gentxInfo.PubKey, + gentxInfo.SelfDelegation, + peer, + ), + ) + + n.ev.Send("Broadcasting validator transaction", events.ProgressStart()) + + res, err := n.cosmos.BroadcastTx(ctx, n.account, msg) + if err != nil { + return err + } + + var requestRes launchtypes.MsgSendRequestResponse + if err := res.Decode(&requestRes); err != nil { + return err + } + + if requestRes.AutoApproved { + n.ev.Send("Validator added to the network by the coordinator!", events.ProgressFinish()) + } else { + n.ev.Send( + fmt.Sprintf("Request %d to join the network as a validator has been submitted!", requestRes.RequestID), + events.ProgressFinish(), + ) + } + return nil +} + +// SendValidatorRemoveRequest creates the RequestRemoveValidator message to SPN +func (n Network) SendValidatorRemoveRequest( + ctx context.Context, + launchID uint64, + valAddress string, +) error { + addr, err := n.account.Address(networktypes.SPN) + if err != nil { + return err + } + + msg := launchtypes.NewMsgSendRequest( + addr, + launchID, + launchtypes.NewValidatorRemoval( + valAddress, + ), + ) + + n.ev.Send("Broadcasting transaction", events.ProgressStart()) + + res, err := n.cosmos.BroadcastTx(ctx, n.account, msg) + if err != nil { + return err + } + + var requestRes launchtypes.MsgSendRequestResponse + if err := res.Decode(&requestRes); err != nil { + return err + } + + if requestRes.AutoApproved { + n.ev.Send("Validator removed from network by the coordinator!", events.ProgressFinish()) + } else { + n.ev.Send( + fmt.Sprintf( + "Request %d to remove validator from the network has been submitted!", requestRes.RequestID, + ), + events.ProgressFinish(), + ) + } + return nil +} diff --git a/ignite/services/scaffolder/oracle.go b/ignite/services/scaffolder/oracle.go index 11c2ce7701..454e35ffd1 100644 --- a/ignite/services/scaffolder/oracle.go +++ b/ignite/services/scaffolder/oracle.go @@ -29,6 +29,8 @@ type oracleOptions struct { } // newOracleOptions returns a oracleOptions with default options +// +// Deprecated: This function is no longer maintained func newOracleOptions() oracleOptions { return oracleOptions{ signer: "creator", @@ -36,6 +38,8 @@ func newOracleOptions() oracleOptions { } // OracleWithSigner provides a custom signer name for the message +// +// Deprecated: This function is no longer maintained func OracleWithSigner(signer string) OracleOption { return func(m *oracleOptions) { m.signer = signer @@ -43,6 +47,8 @@ func OracleWithSigner(signer string) OracleOption { } // AddOracle adds a new BandChain oracle envtest. +// +// Deprecated: This function is no longer maintained func (s *Scaffolder) AddOracle( ctx context.Context, cacheStorage cache.Storage, @@ -112,6 +118,7 @@ func (s *Scaffolder) AddOracle( return sm, finish(ctx, cacheStorage, opts.AppPath, s.modpath.RawPath) } +// Deprecated: This function is no longer maintained func (s Scaffolder) installBandPacket() error { return cmdrunner.New(). Run(context.Background(), diff --git a/ignite/templates/ibc/oracle.go b/ignite/templates/ibc/oracle.go index 5f4146cba4..8010ac5c29 100644 --- a/ignite/templates/ibc/oracle.go +++ b/ignite/templates/ibc/oracle.go @@ -32,6 +32,8 @@ type OracleOptions struct { } // NewOracle returns the generator to scaffold the implementation of the Oracle interface inside a module +// +// Deprecated: This function is no longer maintained func NewOracle(replacer placeholder.Replacer, opts *OracleOptions) (*genny.Generator, error) { g := genny.New() @@ -74,6 +76,7 @@ func NewOracle(replacer placeholder.Replacer, opts *OracleOptions) (*genny.Gener return g, nil } +// Deprecated: This function is no longer maintained func moduleOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "module_ibc.go") @@ -111,6 +114,7 @@ func moduleOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genn } } +// Deprecated: This function is no longer maintained func protoQueryOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "proto", opts.AppName, opts.ModuleName, "query.proto") @@ -165,6 +169,7 @@ message QueryLast%[2]vIdResponse {int64 request_id = 1;} } } +// Deprecated: This function is no longer maintained func protoTxOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "proto", opts.AppName, opts.ModuleName, "tx.proto") @@ -226,6 +231,7 @@ message Msg%[2]vDataResponse { } } +// Deprecated: This function is no longer maintained func clientCliQueryOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "client/cli/query.go") @@ -244,6 +250,7 @@ func clientCliQueryOracleModify(replacer placeholder.Replacer, opts *OracleOptio } } +// Deprecated: This function is no longer maintained func clientCliTxOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "client/cli/tx.go") @@ -260,6 +267,7 @@ func clientCliTxOracleModify(replacer placeholder.Replacer, opts *OracleOptions) } } +// Deprecated: This function is no longer maintained func codecOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "types/codec.go") @@ -291,6 +299,7 @@ func codecOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny } } +// Deprecated: This function is no longer maintained func packetHandlerOracleModify(replacer placeholder.Replacer, opts *OracleOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "oracle.go")