From 720c73293c4cc194b1c5c2817dd82beab21e2db5 Mon Sep 17 00:00:00 2001 From: Robert Pirtle Date: Wed, 24 Apr 2024 11:30:27 -0700 Subject: [PATCH] feat: add ExposeAdditionalPorts config option for cosmos chains, ExposeAdditionalPorts takes a list of port ids that will be exposed on all validators and full nodes for the chains on random host ports. extends the ethermint test by exposing the EVM & verifying the port actually gets exposed. --- chain/cosmos/chain_node.go | 16 +++++++++++++ chainspec.go | 4 ++++ examples/cosmos/ethermint_test.go | 40 +++++++++++++++++++++++++++---- ibc/types.go | 11 +++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/chain/cosmos/chain_node.go b/chain/cosmos/chain_node.go index c43ed78e7..9c45321f6 100644 --- a/chain/cosmos/chain_node.go +++ b/chain/cosmos/chain_node.go @@ -1058,6 +1058,9 @@ func (tn *ChainNode) CreateNodeContainer(ctx context.Context) error { for k, v := range sentryPorts { usingPorts[k] = v } + for _, port := range chainCfg.ExposeAdditionalPorts { + usingPorts[nat.Port(port)] = []nat.PortBinding{} + } // to prevent port binding conflicts, host port overrides are only exposed on the first validator node. if tn.Validator && tn.Index == 0 && chainCfg.HostPortOverride != nil { @@ -1377,3 +1380,16 @@ func (tn *ChainNode) SendICABankTransfer(ctx context.Context, connectionID, from _, err := tn.SendICATx(ctx, fromAddr, connectionID, ir, msgs, icaTxMemo, "proto3") return err } + +// GetHostAddress returns the host-accessible url for a port in the container. +// This is useful for finding the url & random host port for ports exposed via ChainConfig.ExposeAdditionalPorts +func (tn *ChainNode) GetHostAddress(ctx context.Context, portID string) (string, error) { + ports, err := tn.containerLifecycle.GetHostPorts(ctx, portID) + if err != nil { + return "", err + } + if len(ports) == 0 || ports[0] == "" { + return "", fmt.Errorf("no port with id '%s' found", portID) + } + return "http://" + ports[0], nil +} diff --git a/chainspec.go b/chainspec.go index cd8b74c62..dd8196400 100644 --- a/chainspec.go +++ b/chainspec.go @@ -62,6 +62,10 @@ func (s *ChainSpec) Config(log *zap.Logger) (*ibc.ChainConfig, error) { } } + if len(s.ExposeAdditionalPorts) > 0 { + s.ChainConfig.ExposeAdditionalPorts = append(s.ChainConfig.ExposeAdditionalPorts, s.ExposeAdditionalPorts...) + } + // s.Name and chainConfig.Name are interchangeable if s.Name == "" && s.ChainConfig.Name != "" { s.Name = s.ChainConfig.Name diff --git a/examples/cosmos/ethermint_test.go b/examples/cosmos/ethermint_test.go index 46622fc3f..04012122d 100644 --- a/examples/cosmos/ethermint_test.go +++ b/examples/cosmos/ethermint_test.go @@ -1,7 +1,11 @@ package cosmos_test import ( + "bytes" "context" + "encoding/json" + "io" + "net/http" "testing" "time" @@ -11,6 +15,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -27,9 +32,6 @@ func TestEthermintChain(t *testing.T) { t.Skip("skipping in short mode") } - numVals := 1 - numFullNodes := 0 - cosmos.SetSDKConfig(wallet) genesis := []cosmos.GenesisKV{ @@ -83,6 +85,11 @@ func TestEthermintChain(t *testing.T) { }), } + jsonRpcOverrides := make(testutil.Toml) + jsonRpcOverrides["address"] = "0.0.0.0:8545" + appTomlOverrides := make(testutil.Toml) + appTomlOverrides["json-rpc"] = jsonRpcOverrides + decimals := int64(decimals) cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { @@ -100,9 +107,10 @@ func TestEthermintChain(t *testing.T) { TrustingPeriod: "168h0m0s", ModifyGenesis: cosmos.ModifyGenesis(genesis), CoinDecimals: &decimals, + // open the port for the EVM on all nodes + ExposeAdditionalPorts: []string{"8545/tcp"}, + ConfigFileOverrides: map[string]any{"config/app.toml": appTomlOverrides}, }, - NumValidators: &numVals, - NumFullNodes: &numFullNodes, }, }) @@ -133,6 +141,28 @@ func TestEthermintChain(t *testing.T) { balance, err := chain.GetNode().Chain.GetBalance(ctx, user.FormattedAddress(), denom) require.NoError(t, err) require.Equal(t, "10000000000", balance.String()) + + // verify access to port exposed via ExposeAdditionalPorts + evmJsonRpcUrl, err := chain.FullNodes[0].GetHostAddress(ctx, "8545/tcp") + require.NoError(t, err) + + data := []byte(`{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0x1", null]}`) + resp, err := http.Post(evmJsonRpcUrl, "application/json", bytes.NewBuffer(data)) + require.NoError(t, err) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + response := struct { + Result struct { + Number string `json:"number"` + } `json:"result"` + }{} + err = json.Unmarshal(body, &response) + require.NoError(t, err) + + require.Equal(t, "0x1", response.Result.Number) } type evmEpoch struct { diff --git a/ibc/types.go b/ibc/types.go index 6434c62d3..b7dd6d117 100644 --- a/ibc/types.go +++ b/ibc/types.go @@ -67,6 +67,9 @@ type ChainConfig struct { // HostPortOverride exposes ports to the host. // To avoid port binding conflicts, ports are only exposed on the 0th validator. HostPortOverride map[int]int `yaml:"host-port-override"` + // ExposeAdditionalPorts exposes each port id to the host on a random port. ex: "8080/tcp" + // Access the address with ChainNode.GetHostAddress + ExposeAdditionalPorts []string // Additional start command arguments AdditionalStartArgs []string // Environment variables for chain nodes @@ -84,6 +87,10 @@ func (c ChainConfig) Clone() ChainConfig { copy(sidecars, c.SidecarConfigs) x.SidecarConfigs = sidecars + additionalPorts := make([]string, len(c.ExposeAdditionalPorts)) + copy(additionalPorts, c.ExposeAdditionalPorts) + x.ExposeAdditionalPorts = additionalPorts + if c.CoinDecimals != nil { coinDecimals := *c.CoinDecimals x.CoinDecimals = &coinDecimals @@ -206,6 +213,10 @@ func (c ChainConfig) MergeChainSpecConfig(other ChainConfig) ChainConfig { c.Env = append(c.Env, other.Env...) } + if len(other.ExposeAdditionalPorts) > 0 { + c.ExposeAdditionalPorts = append(c.ExposeAdditionalPorts, other.ExposeAdditionalPorts...) + } + return c }