From 31290e0624942f53f6256d6446671b7ee9a438dc Mon Sep 17 00:00:00 2001 From: shreyasbhat0 Date: Thu, 7 Dec 2023 18:36:59 +0530 Subject: [PATCH] feat: update ibc command for starting ibc bridge --- cli/cmd/bridge/bridge.go | 27 +++- cli/cmd/bridge/{relays/btp.go => btp/cmd.go} | 13 +- cli/cmd/bridge/btp/run.go | 1 + cli/cmd/bridge/ibc/cmd.go | 52 ++++++++ cli/cmd/bridge/ibc/run.go | 130 +++++++++++++++++++ cli/cmd/bridge/relays/common.go | 1 - cli/cmd/bridge/relays/ibc.go | 16 --- cli/cmd/bridge/utils/types.go | 108 +++++++++++++++ cli/common/cli.go | 4 +- cli/common/constants.go | 1 + cli/common/errors.go | 1 + cli/common/logs.go | 3 +- 12 files changed, 333 insertions(+), 24 deletions(-) rename cli/cmd/bridge/{relays/btp.go => btp/cmd.go} (56%) create mode 100644 cli/cmd/bridge/btp/run.go create mode 100644 cli/cmd/bridge/ibc/cmd.go create mode 100644 cli/cmd/bridge/ibc/run.go delete mode 100644 cli/cmd/bridge/relays/common.go delete mode 100644 cli/cmd/bridge/relays/ibc.go create mode 100644 cli/cmd/bridge/utils/types.go diff --git a/cli/cmd/bridge/bridge.go b/cli/cmd/bridge/bridge.go index ab6650f4..9906dcf3 100644 --- a/cli/cmd/bridge/bridge.go +++ b/cli/cmd/bridge/bridge.go @@ -1,7 +1,11 @@ package bridge import ( - "github.com/hugobyte/dive-core/cli/cmd/bridge/relays" + "os" + "slices" + + "github.com/hugobyte/dive-core/cli/cmd/bridge/btp" + "github.com/hugobyte/dive-core/cli/cmd/bridge/ibc" "github.com/hugobyte/dive-core/cli/common" "github.com/spf13/cobra" ) @@ -12,8 +16,23 @@ var BridgeCmd = common.NewDiveCommandBuilder(). SetLong(`To connect two different chains using any of the supported cross chain communication protocols. This will create an relay to connect two different chains and pass any messages between them.`). SetRun(bridge). - AddCommand(relays.BtpRelayCmd). - AddCommand(relays.IbcRelayCmd). + AddCommand(btp.BtpRelayCmd). + AddCommand(ibc.IbcRelayCmd). Build() -func bridge(cmd *cobra.Command, args []string) {} +func bridge(cmd *cobra.Command, args []string) { + cli := common.GetCli() + validArgs := cmd.ValidArgs + for _, c := range cmd.Commands() { + validArgs = append(validArgs, c.Name()) + } + cmd.ValidArgs = validArgs + + if len(args) == 0 { + cmd.Help() + + } else if !slices.Contains(cmd.ValidArgs, args[0]) { + cli.Error(common.WrapMessageToErrorf(common.ErrInvalidCommand, "%s", cmd.UsageString())) + os.Exit(1) + } +} diff --git a/cli/cmd/bridge/relays/btp.go b/cli/cmd/bridge/btp/cmd.go similarity index 56% rename from cli/cmd/bridge/relays/btp.go rename to cli/cmd/bridge/btp/cmd.go index 3b1c0cee..a15916c9 100644 --- a/cli/cmd/bridge/relays/btp.go +++ b/cli/cmd/bridge/btp/cmd.go @@ -1,10 +1,21 @@ -package relays +package btp import ( "github.com/hugobyte/dive-core/cli/common" "github.com/spf13/cobra" ) +const bridgeMainFunction = "run_btp_setup" +const runbridgeicon2icon = "start_btp_for_already_running_icon_nodes" +const runbridgeicon2ethhardhat = "start_btp_icon_to_eth_for_already_running_nodes" + +var ( + chainA string + chainB string + serviceA string + serviceB string +) + var BtpRelayCmd = common.NewDiveCommandBuilder(). SetUse("btp"). SetShort("Starts BTP Relay to bridge between ChainA and ChainB"). diff --git a/cli/cmd/bridge/btp/run.go b/cli/cmd/bridge/btp/run.go new file mode 100644 index 00000000..3e68666e --- /dev/null +++ b/cli/cmd/bridge/btp/run.go @@ -0,0 +1 @@ +package btp diff --git a/cli/cmd/bridge/ibc/cmd.go b/cli/cmd/bridge/ibc/cmd.go new file mode 100644 index 00000000..9ccdcd08 --- /dev/null +++ b/cli/cmd/bridge/ibc/cmd.go @@ -0,0 +1,52 @@ +package ibc + +import ( + "fmt" + + "github.com/hugobyte/dive-core/cli/common" + "github.com/spf13/cobra" +) + +var ( + chainA string + chainB string + serviceA string + serviceB string +) + +var IbcRelayCmd = common.NewDiveCommandBuilder(). + SetUse("ibc"). + SetShort("Start connection between Cosmos based chainA and ChainB and initiate communication between them"). + SetLong(`This Command deploy , initialize the contracts and make it ready for ibc. +Along with that setup and starts the ibc relayer to establish communication between chains specified`). + SetRun(ibcRelay). + AddStringFlag(&chainA, "chainA", "", "Mention Name of Supported Chain"). + AddStringFlag(&chainB, "chainB", "", "Mention Name of Supported Chain"). + AddStringFlag(&serviceA, "chainAServiceName", "", "Service Name of Chain A from services.json"). + AddStringFlag(&serviceB, "chainBServiceName", "", "Service Name of Chain B from services.json"). + MarkFlagRequired("chainA"). + MarkFlagRequired("chainB"). + Build() + +func ibcRelay(cmd *cobra.Command, args []string) { + + cliContext := common.GetCliWithKurtosisContext() + + err := common.ValidateArgs(args) + + if err != nil { + cliContext.Fatalf("Error %s. %s", err, cmd.UsageString()) + } + cliContext.Spinner().StartWithMessage("Starting IBC Setup", "green") + result, err := RunIbcRelay(cliContext) + if err != nil { + cliContext.Fatal(err) + } + + err = cliContext.FileHandler().WriteFile("dive.json", []byte(result)) + if err != nil { + cliContext.Fatal(err) + } + + cliContext.Spinner().StopWithMessage(fmt.Sprintf("IBC Setup Completed between %s and %s. Please find service details in current working directory(dive.json)", chainA, chainB)) +} diff --git a/cli/cmd/bridge/ibc/run.go b/cli/cmd/bridge/ibc/run.go new file mode 100644 index 00000000..537d5d78 --- /dev/null +++ b/cli/cmd/bridge/ibc/run.go @@ -0,0 +1,130 @@ +package ibc + +import ( + "fmt" + + "github.com/hugobyte/dive-core/cli/cmd/bridge/utils" + "github.com/hugobyte/dive-core/cli/common" + "github.com/kurtosis-tech/kurtosis/api/golang/core/lib/enclaves" +) + +func RunIbcRelay(cli *common.Cli) (string, error) { + var starlarkExecutionResponse string + chains := utils.InitChains(chainA, chainB, serviceA, serviceB, false) + + err := chains.CheckForIbcSupportedChains() + + if err != nil { + + return "", common.WrapMessageToError(common.ErrInvalidChain, err.Error()) + } + + enclaveContext, err := cli.Context().GetEnclaveContext(common.DiveEnclave) + if err != nil { + return "", common.WrapMessageToError(err, "IBC Setup Failed") + } + + if chains.CheckChainServiceNamesEmpty() { + srcChainServiceResponse, dstChainServiceResponse, err := chains.GetServicesResponse(cli) + if err != nil { + return "", err + } + starlarkExecutionResponse, err = setupIbcRelayforAlreadyRunningCosmosChain(cli, enclaveContext, chains.ChainA, chains.ChainB, srcChainServiceResponse, dstChainServiceResponse) + + if err != nil { + return "", err + } + + } else { + starlarkExecutionResponse, err = startCosmosChainsAndSetupIbcRelay(cli, enclaveContext, chains) + if err != nil { + return "", err + } + } + + if chainA == "icon" { + _, err := startIbcRelayIconToCosmos(cli, enclaveContext, common.RelayServiceNameIconToCosmos) + if err != nil { + return "", err + } + } + + return starlarkExecutionResponse, nil +} + +func startIbcRelayIconToCosmos(cli *common.Cli, enclaveContext *enclaves.EnclaveContext, serviceName string) (string, error) { + params := fmt.Sprintf(`{"service_name": "%s"}`, serviceName) + starlarkConfig := common.GetStarlarkRunConfig(params, "services/bridges/ibc/src/bridge.star", "start_relay") + executionData, _, err := enclaveContext.RunStarlarkRemotePackage(cli.Context().GetContext(), common.DiveRemotePackagePath, starlarkConfig) + + if err != nil { + return "", common.WrapMessageToError(common.ErrStarlarkRunFailed, err.Error()) + } + + executionSerializedData, services, _, err := common.GetSerializedData(cli, executionData) + + if err != nil { + errRemove := cli.Context().RemoveServicesByServiceNames(services, common.DiveEnclave) + if errRemove != nil { + return "", common.WrapMessageToError(errRemove, "IBC Setup Run Failed") + } + + return "", common.WrapMessageToError(err, "IBC Setup Run Failed") + + } + + return executionSerializedData, nil +} + +func startCosmosChainsAndSetupIbcRelay(cli *common.Cli, enclaveCtx *enclaves.EnclaveContext, chains *utils.Chains) (string, error) { + + params := chains.GetIbcRelayParams() + + executionResult, err := runStarlarkPackage(cli, enclaveCtx, params, "run_cosmos_ibc_setup") + + if err != nil { + return "", err + } + + return executionResult, nil +} + +func setupIbcRelayforAlreadyRunningCosmosChain(cli *common.Cli, enclaveCtx *enclaves.EnclaveContext, chainA, chainB, chainAServiceResponse, chainBServiceResponse string) (string, error) { + + params := fmt.Sprintf(`{"src_chain":"%s","dst_chain":"%s", "src_chain_config":%s, "dst_chain_config":%s}`, chainA, chainB, chainAServiceResponse, chainBServiceResponse) + + executionResult, err := runStarlarkPackage(cli, enclaveCtx, params, "run_cosmos_ibc_relay_for_already_running_chains") + + if err != nil { + return "", err + } + + return executionResult, nil +} + +func runStarlarkPackage(cli *common.Cli, enclaveContext *enclaves.EnclaveContext, params, functionName string) (string, error) { + starlarkConfig := common.GetStarlarkRunConfig(params, common.DiveBridgeIbcScript, functionName) + executionData, _, err := enclaveContext.RunStarlarkRemotePackage(cli.Context().GetContext(), common.DiveRemotePackagePath, starlarkConfig) + + if err != nil { + return "", err + } + + executionSerializedData, services, skippedInstructions, err := common.GetSerializedData(cli, executionData) + + if err != nil { + errRemove := cli.Context().RemoveServicesByServiceNames(services, common.DiveEnclave) + if errRemove != nil { + return "", common.WrapMessageToError(errRemove, "IBC Setup Run Failed") + } + + return "", common.WrapMessageToError(err, "IBC Setup Run Failed") + + } + + if cli.Context().CheckSkippedInstructions(skippedInstructions) { + return "", common.WrapMessageToError(common.ErrStarlarkResponse, "Already Running") + } + + return executionSerializedData, nil +} diff --git a/cli/cmd/bridge/relays/common.go b/cli/cmd/bridge/relays/common.go deleted file mode 100644 index ed71b5fb..00000000 --- a/cli/cmd/bridge/relays/common.go +++ /dev/null @@ -1 +0,0 @@ -package relays diff --git a/cli/cmd/bridge/relays/ibc.go b/cli/cmd/bridge/relays/ibc.go deleted file mode 100644 index 94013461..00000000 --- a/cli/cmd/bridge/relays/ibc.go +++ /dev/null @@ -1,16 +0,0 @@ -package relays - -import ( - "github.com/hugobyte/dive-core/cli/common" - "github.com/spf13/cobra" -) - -var IbcRelayCmd = common.NewDiveCommandBuilder(). - SetUse("ibc"). - SetShort("Start connection between Cosmos based chainA and ChainB and initiate communication between them"). - SetLong(`This Command deploy , initialize the contracts and make it ready for ibc. -Along with that setup and starts the ibc relayer to establish communication between chains specified`). - SetRun(ibcRelay). - Build() - -func ibcRelay(cmd *cobra.Command, args []string) {} diff --git a/cli/cmd/bridge/utils/types.go b/cli/cmd/bridge/utils/types.go new file mode 100644 index 00000000..ed860391 --- /dev/null +++ b/cli/cmd/bridge/utils/types.go @@ -0,0 +1,108 @@ +package utils + +import ( + "fmt" + "slices" + "strconv" + "strings" + + "github.com/hugobyte/dive-core/cli/common" +) + +const ( + IconChain = "icon" + EthChain = "eth" + HardhatChain = "hardhat" + ArchwayChain = "archway" + NeutronChain = "neutron" +) + +var supportedChainsForBtp = []string{IconChain, EthChain, HardhatChain} +var supportedChainsForIbc = []string{ArchwayChain, NeutronChain, IconChain} + +type Chains struct { + ChainA string + ChainB string + ChainAServiceName string + ChainBServiceName string + Bridge string +} + +func InitChains(chainA, chainB, serviceA, serviceB string, bridge bool) *Chains { + return &Chains{ + + ChainA: strings.ToLower(chainA), + ChainB: strings.ToLower(chainB), + ChainAServiceName: serviceA, + ChainBServiceName: serviceB, + Bridge: strconv.FormatBool(bridge), + } +} + +func (c *Chains) AreChainsIcon() bool { + return (c.ChainA == "icon" && c.ChainB == "icon") +} + +func (chains *Chains) GetParams() string { + return fmt.Sprintf(`{"src_chain": "%s", "dst_chain": "%s", "bridge":"%s"}`, chains.ChainA, chains.ChainB, chains.Bridge) +} +func (chains *Chains) GetIbcRelayParams() string { + + return fmt.Sprintf(`{"src_chain": "%s", "dst_chain": "%s"}`, chains.ChainA, chains.ChainB) +} + +func (chains *Chains) GetServicesResponse(cli *common.Cli) (string, string, error) { + + var serviceConfig = common.Services{} + + err := cli.FileHandler().ReadJson("services.json", &serviceConfig) + + if err != nil { + return "", "", err + } + + chainAServiceResponse, OK := serviceConfig[chains.ChainAServiceName] + if !OK { + return "", "", fmt.Errorf("service name not found") + } + chainBServiceResponse, OK := serviceConfig[chains.ChainBServiceName] + if !OK { + return "", "", fmt.Errorf("service name not found") + } + + srcChainServiceResponse, err := chainAServiceResponse.EncodeToString() + if err != nil { + return "", "", err + } + dstChainServiceResponse, err := chainBServiceResponse.EncodeToString() + + if err != nil { + return "", "", err + } + + return srcChainServiceResponse, dstChainServiceResponse, nil +} + +func (chains *Chains) CheckForBtpSupportedChains() error { + if !slices.Contains(supportedChainsForBtp, chains.ChainA) { + return fmt.Errorf("invalid Chain: %s", chains.ChainA) + } + if !slices.Contains(supportedChainsForBtp, chains.ChainB) { + return fmt.Errorf("invalid Chain: %s", chains.ChainB) + } + return nil +} + +func (chains *Chains) CheckForIbcSupportedChains() error { + if !slices.Contains(supportedChainsForIbc, chains.ChainA) { + return fmt.Errorf("invalid Chain: %s", chains.ChainA) + } + if !slices.Contains(supportedChainsForIbc, chains.ChainB) { + return fmt.Errorf("invalid Chain: %s", chains.ChainB) + } + return nil +} + +func (chains *Chains) CheckChainServiceNamesEmpty() bool { + return (chains.ChainAServiceName != "" && chains.ChainBServiceName != "") +} diff --git a/cli/common/cli.go b/cli/common/cli.go index eb152426..c734e272 100644 --- a/cli/common/cli.go +++ b/cli/common/cli.go @@ -96,8 +96,10 @@ func (c *Cli) Fatalf(format string, err error, args ...interface{}) { func (c *Cli) Error(err error) { c.spinner.Stop() + c.log.SetErrorToStderr() - c.log.Error(CodeOf(err), err.Error()) + actualError, _ := CoderOf(err) + c.log.Error(actualError.ErrorCode(), fmt.Sprintf("%s. message: %s", actualError.Error(), err.Error())) } func (c *Cli) Fatal(err error) { diff --git a/cli/common/constants.go b/cli/common/constants.go index 873fc3f7..11297fe4 100644 --- a/cli/common/constants.go +++ b/cli/common/constants.go @@ -76,4 +76,5 @@ const ( InvalidPathError InvalidFileError KurtosisServiceError + InvalidChain ) diff --git a/cli/common/errors.go b/cli/common/errors.go index a742f7a0..e4c24651 100644 --- a/cli/common/errors.go +++ b/cli/common/errors.go @@ -33,6 +33,7 @@ var ( ErrPath = NewBase(InvalidPathError, "Failed To Resolve Path") ErrInvalidFile = NewBase(InvalidFileError, "Failed To Resolve to File") ErrKurtosisService = NewBase(KurtosisServiceError, "Kurtosis Service Error") + ErrInvalidChain = NewBase(InvalidChain, "Not A Valid Chain") ) func (c ErrorCode) New(msg string) error { diff --git a/cli/common/logs.go b/cli/common/logs.go index 787b61cc..4b926aef 100644 --- a/cli/common/logs.go +++ b/cli/common/logs.go @@ -86,7 +86,7 @@ func (d *diveLogger) Warn(message string) { } func (d *diveLogger) Error(errorCode ErrorCode, errorMessage string) { - d.logWithFields(logrus.ErrorLevel, "🛑", "%s", errorMessage) + d.logWithFields(logrus.ErrorLevel, "🛑", "Code:%d Error: %s", errorCode, errorMessage) } func (d *diveLogger) Fatal(errorCode ErrorCode, errorMessage string) { @@ -108,6 +108,7 @@ func (d *diveLogger) Debugf(format string, args ...interface{}) { func (d *diveLogger) Errorf(errorCode ErrorCode, format string, args ...interface{}) { d.logWithFields(logrus.ErrorLevel, "🛑", format, args...) + } func (d *diveLogger) Fatalf(errorCode ErrorCode, format string, args ...interface{}) {