diff --git a/.github/workflows/e2e-conformance.yml b/.github/workflows/e2e-conformance.yml new file mode 100644 index 000000000..24e2c0235 --- /dev/null +++ b/.github/workflows/e2e-conformance.yml @@ -0,0 +1,32 @@ +name: Conformance End-To-End Tests + +on: + pull_request: + +# Ensures that only a single workflow per PR will run at a time. Cancels in-progress jobs if new commit is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + test-conformance: + name: test-conformance + runs-on: ubuntu-latest + steps: + # Install and setup go + - name: Set up Go 1.22 + uses: actions/setup-go@v5 + with: + go-version: '1.22' + + - name: checkout interchaintest + uses: actions/checkout@v4 + + # cleanup environment on self-hosted test runner + - name: clean + run: |- + rm -rf ~/.interchaintest + + # run tests + - name: run conformance tests + run: (go test -race -timeout 30m -failfast -v -p 2 ./cmd/interchaintest) || (echo "\n\n*****CHAIN and RELAYER LOGS*****" && cat "$HOME/.interchaintest/logs/interchaintest.log" && exit 1) diff --git a/.github/workflows/e2e-cosmos.yml b/.github/workflows/e2e-cosmos.yml new file mode 100644 index 000000000..14a38b4dd --- /dev/null +++ b/.github/workflows/e2e-cosmos.yml @@ -0,0 +1,33 @@ +name: Cosmos End-To-End Tests + +on: + pull_request: + +# Ensures that only a single workflow per PR will run at a time. Cancels in-progress jobs if new commit is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + test-cosmos-examples: + name: test-cosmos-examples + runs-on: ubuntu-latest + steps: + # Install and setup go + - name: Set up Go 1.22 + uses: actions/setup-go@v5 + with: + go-version: '1.22' + cache: false + + - name: checkout interchaintest + uses: actions/checkout@v4 + + # cleanup environment on self-hosted test runner + - name: clean + run: |- + rm -rf ~/.interchaintest + + # run tests + - name: run example cosmos tests + run: go test -race -failfast -timeout 30m -v -p 2 ./examples/cosmos diff --git a/.github/workflows/e2e-ibc.yml b/.github/workflows/e2e-ibc.yml new file mode 100644 index 000000000..7538da63e --- /dev/null +++ b/.github/workflows/e2e-ibc.yml @@ -0,0 +1,33 @@ +name: IBC End-To-End Tests + +on: + pull_request: + +# Ensures that only a single workflow per PR will run at a time. Cancels in-progress jobs if new commit is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + test-ibc-examples: + name: test-ibc-examples + runs-on: ubuntu-latest + steps: + # Install and setup go + - name: Set up Go 1.22 + uses: actions/setup-go@v5 + with: + go-version: '1.22' + cache: false + + - name: checkout interchaintest + uses: actions/checkout@v4 + + # cleanup environment on self-hosted test runner + - name: clean + run: |- + rm -rf ~/.interchaintest + + # run tests + - name: run example ibc tests + run: go test -race -timeout 30m -failfast -v -p 2 ./examples/ibc diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml deleted file mode 100644 index 54f8a71a3..000000000 --- a/.github/workflows/e2e-tests.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Run End-To-End Tests - -on: - pull_request: - -# Ensures that only a single workflow per PR will run at a time. Cancels in-progress jobs if new commit is pushed. -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - test-conformance: - name: test-conformance - runs-on: ubuntu-latest - steps: - # Install and setup go - - name: Set up Go 1.22 - uses: actions/setup-go@v5 - with: - go-version: '1.22' - - - name: checkout interchaintest - uses: actions/checkout@v4 - - # cleanup environment on self-hosted test runner - - name: clean - run: |- - rm -rf ~/.interchaintest - - # run tests - - name: run conformance tests - run: (go test -race -timeout 30m -failfast -v -p 2 ./cmd/interchaintest) || (echo "\n\n*****CHAIN and RELAYER LOGS*****" && cat "$HOME/.interchaintest/logs/interchaintest.log" && exit 1) - test-ibc-examples: - name: test-ibc-examples - runs-on: ubuntu-latest - steps: - # Install and setup go - - name: Set up Go 1.22 - uses: actions/setup-go@v5 - with: - go-version: '1.22' - cache: false - - - name: checkout interchaintest - uses: actions/checkout@v4 - - # cleanup environment on self-hosted test runner - - name: clean - run: |- - rm -rf ~/.interchaintest - - # run tests - - name: run example ibc tests - run: go test -race -timeout 30m -failfast -v -p 2 ./examples/ibc - test-cosmos-examples: - name: test-cosmos-examples - runs-on: ubuntu-latest - steps: - # Install and setup go - - name: Set up Go 1.22 - uses: actions/setup-go@v5 - with: - go-version: '1.22' - cache: false - - - name: checkout interchaintest - uses: actions/checkout@v4 - - # cleanup environment on self-hosted test runner - - name: clean - run: |- - rm -rf ~/.interchaintest - - # run tests - - name: run example cosmos tests - run: go test -race -failfast -timeout 30m -v -p 2 ./examples/cosmos diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 40440e00e..c0691dfb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,7 @@ on: types: [created] env: - GO_VERSION: 1.21 + GO_VERSION: 1.22 jobs: release-static-binary: @@ -31,4 +31,4 @@ jobs: with: token: ${{ github.token }} files: | - local-ic \ No newline at end of file + local-ic diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33c0c91c3..0323f8c95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,7 +58,7 @@ Before you start contributing, make sure you have the following prerequisites in - [Go](https://golang.org/dl/) - [Docker](https://www.docker.com/get-started) -- [VSCode (recommended editor)](https://code.visualstudio.com/) +- [VSCode (recommended editor)](https://code.visualstudio.com/) To get started, follow these steps: @@ -76,7 +76,7 @@ To get started, follow these steps: git checkout -b name/broad-description-of-feature ``` -4. Make your changes and commit them with descriptive commit messages. +4. Make your changes and commit them with descriptive commit messages. 5. Test your changes locally with `go test ./...`, or by running the specific test affecting your feature or fix. @@ -86,4 +86,4 @@ To get started, follow these steps: git push origin name/broad-description-of-feature ``` -7. Create a pull request (PR) against the main branch of the interchaintest repository. If the PR is still a work-in-progress, please mark the PR as draft. \ No newline at end of file +7. Create a pull request (PR) against the main branch of the interchaintest repository. If the PR is still a work-in-progress, please mark the PR as draft. diff --git a/Dockerfile.local-interchain b/Dockerfile.local-interchain index 9ff14ae95..57fe4b6d6 100644 --- a/Dockerfile.local-interchain +++ b/Dockerfile.local-interchain @@ -3,7 +3,7 @@ # docker build . -t local-interchain:local -f Dockerfile.local-interchain # docker run -it local-interchain:local -FROM golang:1.22.2 as builder +FROM golang:1.22.5 AS builder # Set destination for COPY WORKDIR /app @@ -21,8 +21,16 @@ RUN cd local-interchain && make build RUN mv ./bin/local-ic /go/bin -# Reduces the size of the final image from 7GB -> 0.1GB -FROM busybox:1.35.0 as final +# Final stage +FROM debian:bookworm-slim AS final + +# Install certificates and required libraries +RUN apt-get update && \ + apt-get install -y ca-certificates libc6 && \ + update-ca-certificates && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + RUN mkdir -p /usr/local/bin COPY --from=builder /go/bin/local-ic /usr/local/bin/local-ic diff --git a/chain/cosmos/broadcaster.go b/chain/cosmos/broadcaster.go index b51ba1801..43afde424 100644 --- a/chain/cosmos/broadcaster.go +++ b/chain/cosmos/broadcaster.go @@ -103,7 +103,7 @@ func (b *Broadcaster) GetFactory(ctx context.Context, user User) (tx.Factory, er // client.Context. func (b *Broadcaster) GetClientContext(ctx context.Context, user User) (client.Context, error) { chain := b.chain - cn := chain.getFullNode() + cn := chain.GetFullNode() _, ok := b.keyrings[user] if !ok { @@ -160,7 +160,7 @@ func (b *Broadcaster) defaultClientContext(fromUser User, sdkAdd sdk.AccAddress) // initialize a clean buffer each time b.buf.Reset() kr := b.keyrings[fromUser] - cn := b.chain.getFullNode() + cn := b.chain.GetFullNode() return cn.CliContext(). WithOutput(b.buf). WithFrom(fromUser.FormattedAddress()). diff --git a/chain/cosmos/cosmos_chain.go b/chain/cosmos/cosmos_chain.go index 1096d4b54..e69472341 100644 --- a/chain/cosmos/cosmos_chain.go +++ b/chain/cosmos/cosmos_chain.go @@ -30,7 +30,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" @@ -155,7 +155,7 @@ func (c *CosmosChain) AddFullNodes(ctx context.Context, configFileOverrides map[ prevCount := c.numFullNodes c.numFullNodes += inc - if err := c.initializeChainNodes(ctx, c.testName, c.getFullNode().DockerClient, c.getFullNode().NetworkID); err != nil { + if err := c.initializeChainNodes(ctx, c.testName, c.GetFullNode().DockerClient, c.GetFullNode().NetworkID); err != nil { return err } @@ -211,7 +211,7 @@ func (c *CosmosChain) Initialize(ctx context.Context, testName string, cli *clie return c.initializeChainNodes(ctx, testName, cli, networkID) } -func (c *CosmosChain) getFullNode() *ChainNode { +func (c *CosmosChain) GetFullNode() *ChainNode { return c.GetNode() } @@ -221,75 +221,75 @@ func (c *CosmosChain) GetNode() *ChainNode { // Exec implements ibc.Chain. func (c *CosmosChain) Exec(ctx context.Context, cmd []string, env []string) (stdout, stderr []byte, err error) { - return c.getFullNode().Exec(ctx, cmd, env) + return c.GetFullNode().Exec(ctx, cmd, env) } // Implements Chain interface. func (c *CosmosChain) GetRPCAddress() string { if c.Config().UsesCometMock() { - return fmt.Sprintf("http://%s:22331", c.getFullNode().HostnameCometMock()) + return fmt.Sprintf("http://%s:22331", c.GetFullNode().HostnameCometMock()) } - return fmt.Sprintf("http://%s:26657", c.getFullNode().HostName()) + return fmt.Sprintf("http://%s:26657", c.GetFullNode().HostName()) } // Implements Chain interface. func (c *CosmosChain) GetAPIAddress() string { - return fmt.Sprintf("http://%s:1317", c.getFullNode().HostName()) + return fmt.Sprintf("http://%s:1317", c.GetFullNode().HostName()) } // Implements Chain interface. func (c *CosmosChain) GetGRPCAddress() string { - return fmt.Sprintf("%s:9090", c.getFullNode().HostName()) + return fmt.Sprintf("%s:9090", c.GetFullNode().HostName()) } // GetHostRPCAddress returns the address of the RPC server accessible by the host. // This will not return a valid address until the chain has been started. func (c *CosmosChain) GetHostRPCAddress() string { - return "http://" + c.getFullNode().hostRPCPort + return "http://" + c.GetFullNode().hostRPCPort } // GetHostAPIAddress returns the address of the REST API server accessible by the host. // This will not return a valid address until the chain has been started. func (c *CosmosChain) GetHostAPIAddress() string { - return "http://" + c.getFullNode().hostAPIPort + return "http://" + c.GetFullNode().hostAPIPort } // GetHostGRPCAddress returns the address of the gRPC server accessible by the host. // This will not return a valid address until the chain has been started. func (c *CosmosChain) GetHostGRPCAddress() string { - return c.getFullNode().hostGRPCPort + return c.GetFullNode().hostGRPCPort } // GetHostP2PAddress returns the address of the P2P server accessible by the host. // This will not return a valid address until the chain has been started. func (c *CosmosChain) GetHostPeerAddress() string { - return c.getFullNode().hostP2PPort + return c.GetFullNode().hostP2PPort } // HomeDir implements ibc.Chain. func (c *CosmosChain) HomeDir() string { - return c.getFullNode().HomeDir() + return c.GetFullNode().HomeDir() } // Implements Chain interface. func (c *CosmosChain) CreateKey(ctx context.Context, keyName string) error { - return c.getFullNode().CreateKey(ctx, keyName) + return c.GetFullNode().CreateKey(ctx, keyName) } // Implements Chain interface. func (c *CosmosChain) RecoverKey(ctx context.Context, keyName, mnemonic string) error { - return c.getFullNode().RecoverKey(ctx, keyName, mnemonic) + return c.GetFullNode().RecoverKey(ctx, keyName, mnemonic) } // Implements Chain interface. func (c *CosmosChain) GetAddress(ctx context.Context, keyName string) ([]byte, error) { - b32Addr, err := c.getFullNode().AccountKeyBech32(ctx, keyName) + b32Addr, err := c.GetFullNode().AccountKeyBech32(ctx, keyName) if err != nil { return nil, err } - return types.GetFromBech32(b32Addr, c.Config().Bech32Prefix) + return sdk.GetFromBech32(b32Addr, c.Config().Bech32Prefix) } // BuildWallet will return a Cosmos wallet @@ -344,12 +344,12 @@ func (c *CosmosChain) BuildRelayerWallet(ctx context.Context, keyName string) (i // Implements Chain interface. func (c *CosmosChain) SendFunds(ctx context.Context, keyName string, amount ibc.WalletAmount) error { - return c.getFullNode().BankSend(ctx, keyName, amount) + return c.GetFullNode().BankSend(ctx, keyName, amount) } // Implements Chain interface. func (c *CosmosChain) SendFundsWithNote(ctx context.Context, keyName string, amount ibc.WalletAmount, note string) (string, error) { - return c.getFullNode().BankSendWithNote(ctx, keyName, amount, note) + return c.GetFullNode().BankSendWithNote(ctx, keyName, amount, note) } // Implements Chain interface. @@ -360,7 +360,7 @@ func (c *CosmosChain) SendIBCTransfer( amount ibc.WalletAmount, options ibc.TransferOptions, ) (tx ibc.Tx, _ error) { - txHash, err := c.getFullNode().SendIBCTransfer(ctx, channelID, keyName, amount, options) + txHash, err := c.GetFullNode().SendIBCTransfer(ctx, channelID, keyName, amount, options) if err != nil { return tx, fmt.Errorf("send ibc transfer: %w", err) } @@ -418,18 +418,18 @@ func (c *CosmosChain) SendIBCTransfer( // RegisterICA will attempt to register an interchain account on the given counterparty chain. func (c *CosmosChain) RegisterICA(ctx context.Context, keyName string, connectionID string) (string, error) { - return c.getFullNode().RegisterICA(ctx, keyName, connectionID) + return c.GetFullNode().RegisterICA(ctx, keyName, connectionID) } // QueryICA will query for an interchain account controlled by the given address on the counterparty chain. func (c *CosmosChain) QueryICAAddress(ctx context.Context, connectionID, address string) (string, error) { - return c.getFullNode().QueryICA(ctx, connectionID, address) + return c.GetFullNode().QueryICA(ctx, connectionID, address) } // SendICATx sends an interchain account transaction for a specified address and sends it to the respective // interchain account on the counterparty chain. -func (c *CosmosChain) SendICATx(ctx context.Context, keyName, connectionID string, msgs []types.Msg, icaTxMemo string) (string, error) { - node := c.getFullNode() +func (c *CosmosChain) SendICATx(ctx context.Context, keyName, connectionID string, msgs []sdk.Msg, icaTxMemo string) (string, error) { + node := c.GetFullNode() registry := node.Chain.Config().EncodingConfig.InterfaceRegistry encoding := "proto3" return node.SendICATx(ctx, keyName, connectionID, registry, msgs, icaTxMemo, encoding) @@ -449,7 +449,7 @@ func (c *CosmosChain) PushNewWasmClientProposal(ctx context.Context, keyName str return tx, "", err } message := wasmtypes.MsgStoreCode{ - Signer: types.MustBech32ifyAddressBytes(c.cfg.Bech32Prefix, authtypes.NewModuleAddress(govtypes.ModuleName)), + Signer: sdk.MustBech32ifyAddressBytes(c.cfg.Bech32Prefix, authtypes.NewModuleAddress(govtypes.ModuleName)), WasmByteCode: content, } msg, err := c.cfg.EncodingConfig.Codec.MarshalInterfaceJSON(&message) @@ -457,7 +457,7 @@ func (c *CosmosChain) PushNewWasmClientProposal(ctx context.Context, keyName str return tx, "", err } prop.Messages = append(prop.Messages, msg) - txHash, err := c.getFullNode().SubmitProposal(ctx, keyName, prop) + txHash, err := c.GetFullNode().SubmitProposal(ctx, keyName, prop) if err != nil { return tx, "", fmt.Errorf("failed to submit wasm client proposal: %w", err) } @@ -467,7 +467,7 @@ func (c *CosmosChain) PushNewWasmClientProposal(ctx context.Context, keyName str // UpgradeProposal submits a software-upgrade governance proposal to the chain. func (c *CosmosChain) UpgradeProposal(ctx context.Context, keyName string, prop SoftwareUpgradeProposal) (tx TxProposal, _ error) { - txHash, err := c.getFullNode().UpgradeProposal(ctx, keyName, prop) + txHash, err := c.GetFullNode().UpgradeProposal(ctx, keyName, prop) if err != nil { return tx, fmt.Errorf("failed to submit upgrade proposal: %w", err) } @@ -476,7 +476,7 @@ func (c *CosmosChain) UpgradeProposal(ctx context.Context, keyName string, prop // SubmitProposal submits a gov v1 proposal to the chain. func (c *CosmosChain) SubmitProposal(ctx context.Context, keyName string, prop TxProposalv1) (tx TxProposal, _ error) { - txHash, err := c.getFullNode().SubmitProposal(ctx, keyName, prop) + txHash, err := c.GetFullNode().SubmitProposal(ctx, keyName, prop) if err != nil { return tx, fmt.Errorf("failed to submit gov v1 proposal: %w", err) } @@ -485,7 +485,7 @@ func (c *CosmosChain) SubmitProposal(ctx context.Context, keyName string, prop T // TextProposal submits a text governance proposal to the chain. func (c *CosmosChain) TextProposal(ctx context.Context, keyName string, prop TextProposal) (tx TxProposal, _ error) { - txHash, err := c.getFullNode().TextProposal(ctx, keyName, prop) + txHash, err := c.GetFullNode().TextProposal(ctx, keyName, prop) if err != nil { return tx, fmt.Errorf("failed to submit upgrade proposal: %w", err) } @@ -494,7 +494,7 @@ func (c *CosmosChain) TextProposal(ctx context.Context, keyName string, prop Tex // ParamChangeProposal submits a param change proposal to the chain, signed by keyName. func (c *CosmosChain) ParamChangeProposal(ctx context.Context, keyName string, prop *paramsutils.ParamChangeProposalJSON) (tx TxProposal, _ error) { - txHash, err := c.getFullNode().ParamChangeProposal(ctx, keyName, prop) + txHash, err := c.GetFullNode().ParamChangeProposal(ctx, keyName, prop) if err != nil { return tx, fmt.Errorf("failed to submit param change proposal: %w", err) } @@ -504,17 +504,17 @@ func (c *CosmosChain) ParamChangeProposal(ctx context.Context, keyName string, p // QueryParam returns the param state of a given key. func (c *CosmosChain) QueryParam(ctx context.Context, subspace, key string) (*ParamChange, error) { - return c.getFullNode().QueryParam(ctx, subspace, key) + return c.GetFullNode().QueryParam(ctx, subspace, key) } // QueryBankMetadata returns the metadata of a given token denomination. func (c *CosmosChain) QueryBankMetadata(ctx context.Context, denom string) (*BankMetaData, error) { - return c.getFullNode().QueryBankMetadata(ctx, denom) + return c.GetFullNode().QueryBankMetadata(ctx, denom) } // ConsumerAdditionProposal submits a legacy governance proposal to add a consumer to the chain. func (c *CosmosChain) ConsumerAdditionProposal(ctx context.Context, keyName string, prop ccvclient.ConsumerAdditionProposalJSON) (tx TxProposal, _ error) { - txHash, err := c.getFullNode().ConsumerAdditionProposal(ctx, keyName, prop) + txHash, err := c.GetFullNode().ConsumerAdditionProposal(ctx, keyName, prop) if err != nil { return tx, fmt.Errorf("failed to submit consumer addition proposal: %w", err) } @@ -543,57 +543,57 @@ func (c *CosmosChain) txProposal(txHash string) (tx TxProposal, _ error) { // StoreContract takes a file path to smart contract and stores it on-chain. Returns the contracts code id. func (c *CosmosChain) StoreContract(ctx context.Context, keyName string, fileName string, extraExecTxArgs ...string) (string, error) { - return c.getFullNode().StoreContract(ctx, keyName, fileName, extraExecTxArgs...) + return c.GetFullNode().StoreContract(ctx, keyName, fileName, extraExecTxArgs...) } // InstantiateContract takes a code id for a smart contract and initialization message and returns the instantiated contract address. func (c *CosmosChain) InstantiateContract(ctx context.Context, keyName string, codeID string, initMessage string, needsNoAdminFlag bool, extraExecTxArgs ...string) (string, error) { - return c.getFullNode().InstantiateContract(ctx, keyName, codeID, initMessage, needsNoAdminFlag, extraExecTxArgs...) + return c.GetFullNode().InstantiateContract(ctx, keyName, codeID, initMessage, needsNoAdminFlag, extraExecTxArgs...) } // ExecuteContract executes a contract transaction with a message using it's address. -func (c *CosmosChain) ExecuteContract(ctx context.Context, keyName string, contractAddress string, message string, extraExecTxArgs ...string) (res *types.TxResponse, err error) { - return c.getFullNode().ExecuteContract(ctx, keyName, contractAddress, message, extraExecTxArgs...) +func (c *CosmosChain) ExecuteContract(ctx context.Context, keyName string, contractAddress string, message string, extraExecTxArgs ...string) (res *sdk.TxResponse, err error) { + return c.GetFullNode().ExecuteContract(ctx, keyName, contractAddress, message, extraExecTxArgs...) } // MigrateContract performs contract migration. -func (c *CosmosChain) MigrateContract(ctx context.Context, keyName string, contractAddress string, codeID string, message string, extraExecTxArgs ...string) (res *types.TxResponse, err error) { - return c.getFullNode().MigrateContract(ctx, keyName, contractAddress, codeID, message, extraExecTxArgs...) +func (c *CosmosChain) MigrateContract(ctx context.Context, keyName string, contractAddress string, codeID string, message string, extraExecTxArgs ...string) (res *sdk.TxResponse, err error) { + return c.GetFullNode().MigrateContract(ctx, keyName, contractAddress, codeID, message, extraExecTxArgs...) } // QueryContract performs a smart query, taking in a query struct and returning a error with the response struct populated. func (c *CosmosChain) QueryContract(ctx context.Context, contractAddress string, query any, response any) error { - return c.getFullNode().QueryContract(ctx, contractAddress, query, response) + return c.GetFullNode().QueryContract(ctx, contractAddress, query, response) } // DumpContractState dumps the state of a contract at a block height. func (c *CosmosChain) DumpContractState(ctx context.Context, contractAddress string, height int64) (*DumpContractStateResponse, error) { - return c.getFullNode().DumpContractState(ctx, contractAddress, height) + return c.GetFullNode().DumpContractState(ctx, contractAddress, height) } // StoreClientContract takes a file path to a client smart contract and stores it on-chain. Returns the contracts code id. func (c *CosmosChain) StoreClientContract(ctx context.Context, keyName string, fileName string, extraExecTxArgs ...string) (string, error) { - return c.getFullNode().StoreClientContract(ctx, keyName, fileName, extraExecTxArgs...) + return c.GetFullNode().StoreClientContract(ctx, keyName, fileName, extraExecTxArgs...) } // QueryClientContractCode performs a query with the contract codeHash as the input and code as the output. func (c *CosmosChain) QueryClientContractCode(ctx context.Context, codeHash string, response any) error { - return c.getFullNode().QueryClientContractCode(ctx, codeHash, response) + return c.GetFullNode().QueryClientContractCode(ctx, codeHash, response) } // ExportState exports the chain state at specific height. // Implements Chain interface. func (c *CosmosChain) ExportState(ctx context.Context, height int64) (string, error) { - return c.getFullNode().ExportState(ctx, height) + return c.GetFullNode().ExportState(ctx, height) } // QueryContractInfo queries the chain for the contract metadata. func (c *CosmosChain) QueryContractInfo(ctx context.Context, contractAddress string) (*ContractInfoResponse, error) { - return c.getFullNode().QueryContractInfo(ctx, contractAddress) + return c.GetFullNode().QueryContractInfo(ctx, contractAddress) } -func (c *CosmosChain) GetTransaction(txhash string) (*types.TxResponse, error) { - fn := c.getFullNode() +func (c *CosmosChain) GetTransaction(txhash string) (*sdk.TxResponse, error) { + fn := c.GetFullNode() return fn.GetTransaction(fn.CliContext(), txhash) } @@ -844,15 +844,15 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene decimalPow := int64(math.Pow10(int(*chainCfg.CoinDecimals))) - genesisAmounts := make([][]types.Coin, len(c.Validators)) - genesisSelfDelegation := make([]types.Coin, len(c.Validators)) + genesisAmounts := make([][]sdk.Coin, len(c.Validators)) + genesisSelfDelegation := make([]sdk.Coin, len(c.Validators)) for i := range c.Validators { - genesisAmounts[i] = []types.Coin{{Amount: sdkmath.NewInt(10_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom}} - genesisSelfDelegation[i] = types.Coin{Amount: sdkmath.NewInt(5_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom} + genesisAmounts[i] = []sdk.Coin{{Amount: sdkmath.NewInt(10_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom}} + genesisSelfDelegation[i] = sdk.Coin{Amount: sdkmath.NewInt(5_000_000).MulRaw(decimalPow), Denom: chainCfg.Denom} if chainCfg.ModifyGenesisAmounts != nil { amount, selfDelegation := chainCfg.ModifyGenesisAmounts(i) - genesisAmounts[i] = []types.Coin{amount} + genesisAmounts[i] = []sdk.Coin{amount} genesisSelfDelegation[i] = selfDelegation } } @@ -958,7 +958,7 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene } for _, wallet := range additionalGenesisWallets { - if err := validator0.AddGenesisAccount(ctx, wallet.Address, []types.Coin{{Denom: wallet.Denom, Amount: wallet.Amount}}); err != nil { + if err := validator0.AddGenesisAccount(ctx, wallet.Address, []sdk.Coin{{Denom: wallet.Denom, Amount: wallet.Amount}}); err != nil { return err } } @@ -1055,18 +1055,18 @@ func (c *CosmosChain) Start(testName string, ctx context.Context, additionalGene } // Wait for blocks before considering the chains "started" - return testutil.WaitForBlocks(ctx, 2, c.getFullNode()) + return testutil.WaitForBlocks(ctx, 2, c.GetFullNode()) } // Height implements ibc.Chain. func (c *CosmosChain) Height(ctx context.Context) (int64, error) { - return c.getFullNode().Height(ctx) + return c.GetFullNode().Height(ctx) } // Acknowledgements implements ibc.Chain, returning all acknowledgments in block at height. func (c *CosmosChain) Acknowledgements(ctx context.Context, height int64) ([]ibc.PacketAcknowledgement, error) { var acks []*chanTypes.MsgAcknowledgement - err := RangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.getFullNode().Client, height, func(msg types.Msg) bool { + err := RangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.GetFullNode().Client, height, func(msg sdk.Msg) bool { found, ok := msg.(*chanTypes.MsgAcknowledgement) if ok { acks = append(acks, found) @@ -1098,7 +1098,7 @@ func (c *CosmosChain) Acknowledgements(ctx context.Context, height int64) ([]ibc // Timeouts implements ibc.Chain, returning all timeouts in block at height. func (c *CosmosChain) Timeouts(ctx context.Context, height int64) ([]ibc.PacketTimeout, error) { var timeouts []*chanTypes.MsgTimeout - err := RangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.getFullNode().Client, height, func(msg types.Msg) bool { + err := RangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.GetFullNode().Client, height, func(msg sdk.Msg) bool { found, ok := msg.(*chanTypes.MsgTimeout) if ok { timeouts = append(timeouts, found) @@ -1128,7 +1128,7 @@ func (c *CosmosChain) Timeouts(ctx context.Context, height int64) ([]ibc.PacketT // FindTxs implements blockdb.BlockSaver. func (c *CosmosChain) FindTxs(ctx context.Context, height int64) ([]blockdb.Tx, error) { - fn := c.getFullNode() + fn := c.GetFullNode() c.findTxMu.Lock() defer c.findTxMu.Unlock() return fn.FindTxs(ctx, height) diff --git a/chain/cosmos/ics.go b/chain/cosmos/ics.go index 892da244c..7a731031b 100644 --- a/chain/cosmos/ics.go +++ b/chain/cosmos/ics.go @@ -130,7 +130,7 @@ func (c *CosmosChain) StartProvider(testName string, ctx context.Context, additi return fmt.Errorf("failed to add proposer key: %s", err) } - proposerAddr, err := c.getFullNode().AccountKeyBech32(ctx, proposerKeyName) + proposerAddr, err := c.GetFullNode().AccountKeyBech32(ctx, proposerKeyName) if err != nil { return fmt.Errorf("failed to get proposer key: %s", err) } @@ -404,7 +404,7 @@ func (c *CosmosChain) StartConsumer(testName string, ctx context.Context, additi } // Wait for 5 blocks before considering the chains "started" - return testutil.WaitForBlocks(ctx, 5, c.getFullNode()) + return testutil.WaitForBlocks(ctx, 5, c.GetFullNode()) } func (c *CosmosChain) transformCCVState(ctx context.Context, ccvState []byte, consumerVersion, providerVersion string, icsCfg ibc.ICSConfig) ([]byte, error) { diff --git a/chain/cosmos/module_tokenfactory.go b/chain/cosmos/module_tokenfactory.go index 76f16a7bd..ae35f66d0 100644 --- a/chain/cosmos/module_tokenfactory.go +++ b/chain/cosmos/module_tokenfactory.go @@ -87,7 +87,7 @@ func (tn *ChainNode) TokenFactoryMetadata(ctx context.Context, keyName, fullDeno // TokenFactoryQueryAdmin returns the admin of a tokenfactory token. func (c *CosmosChain) TokenFactoryQueryAdmin(ctx context.Context, fullDenom string) (*QueryDenomAuthorityMetadataResponse, error) { res := &QueryDenomAuthorityMetadataResponse{} - stdout, stderr, err := c.getFullNode().ExecQuery(ctx, "tokenfactory", "denom-authority-metadata", fullDenom) + stdout, stderr, err := c.GetFullNode().ExecQuery(ctx, "tokenfactory", "denom-authority-metadata", fullDenom) if err != nil { return nil, fmt.Errorf("failed to query tokenfactory denom-authority-metadata: %w\nstdout: %s\nstderr: %s", err, stdout, stderr) } diff --git a/chain/cosmos/osmosis.go b/chain/cosmos/osmosis.go index c6ecbb77f..f850d0a4c 100644 --- a/chain/cosmos/osmosis.go +++ b/chain/cosmos/osmosis.go @@ -20,7 +20,7 @@ type OsmosisPoolParams struct { } func OsmosisCreatePool(c *CosmosChain, ctx context.Context, keyName string, params OsmosisPoolParams) (string, error) { - tn := c.getFullNode() + tn := c.GetFullNode() poolbz, err := json.Marshal(params) if err != nil { return "", err @@ -57,7 +57,7 @@ func OsmosisCreatePool(c *CosmosChain, ctx context.Context, keyName string, para } func OsmosisSwapExactAmountIn(c *CosmosChain, ctx context.Context, keyName string, coinIn string, minAmountOut string, poolIDs []string, swapDenoms []string) (string, error) { - return c.getFullNode().ExecTx(ctx, keyName, + return c.GetFullNode().ExecTx(ctx, keyName, "gamm", "swap-exact-amount-in", coinIn, minAmountOut, "--swap-route-pool-ids", strings.Join(poolIDs, ","), diff --git a/chain/cosmos/poll.go b/chain/cosmos/poll.go index 72ccffeb8..0d6fa8908 100644 --- a/chain/cosmos/poll.go +++ b/chain/cosmos/poll.go @@ -58,7 +58,7 @@ func PollForMessage[T any](ctx context.Context, chain *CosmosChain, registry cod } doPoll := func(ctx context.Context, height int64) (T, error) { h := height - block, err := chain.getFullNode().Client.Block(ctx, &h) + block, err := chain.GetFullNode().Client.Block(ctx, &h) if err != nil { return zero, err } diff --git a/chain/thorchain/sidecar.go b/chain/thorchain/sidecar.go index e59a0f1ec..077394a2b 100644 --- a/chain/thorchain/sidecar.go +++ b/chain/thorchain/sidecar.go @@ -71,6 +71,10 @@ func NewSidecar( homeDir = "/home/sidecar" } + // Give each sidecard their own env copy for runtime changes + envCopy := make([]string, len(env)) + copy(envCopy, env) + s := &SidecarProcess{ log: log, Index: index, @@ -85,7 +89,7 @@ func NewSidecar( homeDir: homeDir, ports: processPorts, startCmd: startCmd, - env: env, + env: envCopy, } s.containerLifecycle = dockerutil.NewContainerLifecycle(log, dockerClient, s.Name()) diff --git a/chain/utxo/cli.go b/chain/utxo/cli.go index a3bd732be..c32b8e59f 100644 --- a/chain/utxo/cli.go +++ b/chain/utxo/cli.go @@ -109,10 +109,12 @@ func (c *UtxoChain) CreateWallet(ctx context.Context, keyName string) error { return err } + c.MapAccess.Lock() c.KeyNameToWalletMap[keyName] = &NodeWallet{ keyName: keyName, loadCount: 1, } + c.MapAccess.Unlock() } return c.UnloadWallet(ctx, keyName) @@ -151,12 +153,14 @@ func (c *UtxoChain) GetNewAddress(ctx context.Context, keyName string, mweb bool addr = splitAddr[1] } + c.MapAccess.Lock() wallet.address = addr c.AddrToKeyNameMap[addr] = keyName if c.WalletVersion >= noDefaultKeyWalletVersion { wallet.ready = true } + c.MapAccess.Unlock() if err := c.UnloadWallet(ctx, keyName); err != nil { return "", err diff --git a/chain/utxo/utxo_chain.go b/chain/utxo/utxo_chain.go index 1fc6efb3d..dae15697f 100644 --- a/chain/utxo/utxo_chain.go +++ b/chain/utxo/utxo_chain.go @@ -7,6 +7,7 @@ import ( "math" "strconv" "strings" + "sync" "time" dockertypes "github.com/docker/docker/api/types" @@ -40,6 +41,7 @@ var natPorts = nat.PortMap{ type UtxoChain struct { testName string cfg ibc.ChainConfig + cancel context.CancelFunc log *zap.Logger @@ -60,6 +62,9 @@ type UtxoChain struct { AddrToKeyNameMap map[string]string KeyNameToWalletMap map[string]*NodeWallet + // Mutex for reading/writing AddrToKeyNameMap and KeyNameToWalletMap + MapAccess sync.Mutex + WalletVersion int unloadWalletAfterUse bool } @@ -259,52 +264,6 @@ func (c *UtxoChain) Start(testName string, ctx context.Context, additionalGenesi // Wait for rpc to come up time.Sleep(time.Second * 5) - go func() { - ctx := context.Background() - amount := "100" - nextBlockHeight := 100 - if c.cfg.CoinType == "3" { - amount = "1000" // Dogecoin needs more blocks for more coins - nextBlockHeight = 1000 - } - for { - faucetWallet, found := c.KeyNameToWalletMap[faucetKeyName] - if !found || !faucetWallet.ready { - time.Sleep(time.Second) - continue - } - - // If faucet exists, chain is up and running. Any future error should return from this go routine. - // If the chain stops, we will then error and return from this go routine - // Don't use ctx from Start(), it gets cancelled soon after returning. - cmd = append(c.BaseCli, "generatetoaddress", amount, faucetWallet.address) - _, _, err = c.Exec(ctx, cmd, nil) - if err != nil { - c.logger().Error("generatetoaddress error", zap.Error(err)) - return - } - amount = "1" - if nextBlockHeight == 431 && c.cfg.CoinType == "2" { - keyName := "mweb" - if err := c.CreateWallet(ctx, keyName); err != nil { - c.logger().Error("error creating mweb wallet at block 431", zap.String("chain", c.cfg.ChainID), zap.Error(err)) - return - } - addr, err := c.GetNewAddress(ctx, keyName, true) - if err != nil { - c.logger().Error("error creating mweb wallet at block 431", zap.String("chain", c.cfg.ChainID), zap.Error(err)) - return - } - if err := c.sendToMwebAddress(ctx, faucetKeyName, addr, 1); err != nil { - c.logger().Error("error sending to mweb wallet at block 431", zap.String("chain", c.cfg.ChainID), zap.Error(err)) - return - } - } - nextBlockHeight++ - time.Sleep(time.Second * 2) - } - }() - c.WalletVersion, _ = c.GetWalletVersion(ctx, "") if err := c.CreateWallet(ctx, faucetKeyName); err != nil { @@ -327,6 +286,63 @@ func (c *UtxoChain) Start(testName string, ctx context.Context, additionalGenesi return err } + go func() { + // Don't use ctx from Start(), it gets cancelled soon after returning. + goRoutineCtx := context.Background() + goRoutineCtx, c.cancel = context.WithCancel(goRoutineCtx) + amount := "100" + nextBlockHeight := 100 + if c.cfg.CoinType == "3" { + amount = "1000" // Dogecoin needs more blocks for more coins + nextBlockHeight = 1000 + } + + c.MapAccess.Lock() + faucetWallet, found := c.KeyNameToWalletMap[faucetKeyName] + if !found || !faucetWallet.ready { + c.logger().Error("faucet wallet not found or not ready") + c.MapAccess.Unlock() + return + } + c.MapAccess.Unlock() + + utxoBlockTime := time.Second * 2 + timer := time.NewTimer(utxoBlockTime) + defer timer.Stop() + for { + select { + case <-goRoutineCtx.Done(): + return + case <-timer.C: + cmd = append(c.BaseCli, "generatetoaddress", amount, faucetWallet.address) + _, _, err := c.Exec(goRoutineCtx, cmd, nil) + if err != nil { + c.logger().Error("generatetoaddress error", zap.Error(err)) + return + } + amount = "1" + if nextBlockHeight == 431 && c.cfg.CoinType == "2" { + keyName := "mweb" + if err := c.CreateWallet(goRoutineCtx, keyName); err != nil { + c.logger().Error("error creating mweb wallet at block 431", zap.String("chain", c.cfg.ChainID), zap.Error(err)) + return + } + addr, err := c.GetNewAddress(goRoutineCtx, keyName, true) + if err != nil { + c.logger().Error("error creating mweb wallet at block 431", zap.String("chain", c.cfg.ChainID), zap.Error(err)) + return + } + if err := c.sendToMwebAddress(goRoutineCtx, faucetKeyName, addr, 1); err != nil { + c.logger().Error("error sending to mweb wallet at block 431", zap.String("chain", c.cfg.ChainID), zap.Error(err)) + return + } + } + nextBlockHeight++ + timer.Reset(utxoBlockTime) + } + } + }() + // Wait for 100 blocks to be created, coins mature after 100 blocks and the faucet starts getting 50 spendable coins/block onwards // Then wait the standard 2 blocks which also gives the faucet a starting balance of 100 coins for height, err := c.Height(ctx); err == nil && height < int64(102); { @@ -397,6 +413,8 @@ func (c *UtxoChain) CreateKey(ctx context.Context, keyName string) error { // Get address of account, cast to a string to use. func (c *UtxoChain) GetAddress(ctx context.Context, keyName string) ([]byte, error) { + c.MapAccess.Lock() + defer c.MapAccess.Unlock() wallet, ok := c.KeyNameToWalletMap[keyName] if ok { return []byte(wallet.address), nil @@ -474,10 +492,12 @@ func (c *UtxoChain) Height(ctx context.Context) (int64, error) { } func (c *UtxoChain) GetBalance(ctx context.Context, address string, denom string) (sdkmath.Int, error) { + c.MapAccess.Lock() keyName, ok := c.AddrToKeyNameMap[address] if !ok { return sdkmath.Int{}, fmt.Errorf("wallet not found for address: %s", address) } + c.MapAccess.Unlock() var coinsWithDecimal float64 if c.WalletVersion >= noDefaultKeyWalletVersion { @@ -534,3 +554,7 @@ func (c *UtxoChain) BuildWallet(ctx context.Context, keyName string, mnemonic st } return NewWallet(keyName, string(address)), nil } + +func (c *UtxoChain) Stop() { + c.cancel() +} diff --git a/chain/utxo/wallet.go b/chain/utxo/wallet.go index 420d4ad1d..4bcbd1ba8 100644 --- a/chain/utxo/wallet.go +++ b/chain/utxo/wallet.go @@ -50,6 +50,8 @@ type NodeWallet struct { } func (c *UtxoChain) getWalletForNewAddress(keyName string) (*NodeWallet, error) { + c.MapAccess.Lock() + defer c.MapAccess.Unlock() wallet, found := c.KeyNameToWalletMap[keyName] if c.WalletVersion >= noDefaultKeyWalletVersion { if !found { @@ -75,6 +77,8 @@ func (c *UtxoChain) getWalletForNewAddress(keyName string) (*NodeWallet, error) } func (c *UtxoChain) getWalletForSetAccount(keyName string, addr string) (*NodeWallet, error) { + c.MapAccess.Lock() + defer c.MapAccess.Unlock() wallet, found := c.KeyNameToWalletMap[keyName] if !found { return nil, fmt.Errorf("wallet keyname (%s) not found, get new address not called", keyName) @@ -100,6 +104,8 @@ func (c *UtxoChain) getWalletForUse(keyName string) (*NodeWallet, error) { } func (c *UtxoChain) getWallet(keyName string) (*NodeWallet, error) { + c.MapAccess.Lock() + defer c.MapAccess.Unlock() wallet, found := c.KeyNameToWalletMap[keyName] if !found { return nil, fmt.Errorf("wallet keyname (%s) not found", keyName) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 32320fc7a..ec7c52bf3 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -21,6 +21,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v8/ibc" "github.com/strangelove-ventures/interchaintest/v8/relayer" "github.com/strangelove-ventures/interchaintest/v8/testreporter" + "github.com/strangelove-ventures/interchaintest/v8/testutil" ) func init() { @@ -101,11 +102,14 @@ func setUpTestMatrix() error { if extraFlags.MatrixFile == "" { fmt.Fprintln(os.Stderr, "No matrix file provided, falling back to rly with gaia and osmosis") + numValsOne := 1 + numFullNodesZero := 0 + testMatrix.Relayers = []string{"rly", "hermes"} testMatrix.ChainSets = [][]*interchaintest.ChainSpec{ { - {Name: "gaia", Version: "v7.0.1"}, - {Name: "osmosis", Version: "v7.2.0"}, + {Name: testutil.TestSimd, Version: testutil.SimdVersion, ChainName: "c1", NumValidators: &numValsOne, NumFullNodes: &numFullNodesZero}, + {Name: testutil.TestSimd, Version: testutil.SimdVersion, ChainName: "c2", NumValidators: &numValsOne, NumFullNodes: &numFullNodesZero}, }, } diff --git a/docs/ciTests.md b/docs/ciTests.md index 3b740c569..22764377b 100644 --- a/docs/ciTests.md +++ b/docs/ciTests.md @@ -18,7 +18,7 @@ Seamlessly build and test current iterations of your chain both locally and with We recommend creating a separate directory in your chain's repo and importing Interchaintest as its own module. This will allow you to keep the extra imports needed for `interchaintest` separate from your chain. -See [`noble`](https://github.com/strangelove-ventures/noble) chains `interchaintest` [folder](https://github.com/strangelove-ventures/noble/tree/main/interchaintest) as an example. +See [`noble`](https://github.com/noble-assets/noble) chains `interchaintest e2e` [folder](https://github.com/noble-assets/noble/tree/main/e2e) as an example. Nobles' CI workflow is a great example to follow along with throughout this guide. diff --git a/examples/cosmos/bad_genesis_params_test.go b/examples/cosmos/bad_genesis_params_test.go index f39e83542..abbc59453 100644 --- a/examples/cosmos/bad_genesis_params_test.go +++ b/examples/cosmos/bad_genesis_params_test.go @@ -16,6 +16,10 @@ var badGenesis = []cosmos.GenesisKV{ } func TestBadInputParams(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { Name: "juno", diff --git a/blockdb/messages_view_test.go b/examples/cosmos/blockdb_messages_view_test.go similarity index 81% rename from blockdb/messages_view_test.go rename to examples/cosmos/blockdb_messages_view_test.go index ae4e0c0ee..2a0bbd9ae 100644 --- a/blockdb/messages_view_test.go +++ b/examples/cosmos/blockdb_messages_view_test.go @@ -1,7 +1,4 @@ -package blockdb_test - -// This test is in a separate file, so it can be in the blockdb_test package, -// so it can import interchaintest without creating an import cycle. +package cosmos_test import ( "context" @@ -23,7 +20,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v8/testutil" ) -func TestMessagesView(t *testing.T) { +func TestBlockDBMessagesView(t *testing.T) { if testing.Short() { t.Skip("skipping in short mode") } @@ -32,11 +29,11 @@ func TestMessagesView(t *testing.T) { client, network := interchaintest.DockerSetup(t) - const gaia0ChainID = "g0" - const gaia1ChainID = "g1" + const chainID0 = "c0" + const chainID1 = "c1" cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ - {Name: "gaia", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: gaia0ChainID}}, - {Name: "gaia", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: gaia1ChainID}}, + {Name: testutil.TestSimd, Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: chainID0}, NumValidators: &numVals, NumFullNodes: &numFullNodes}, + {Name: testutil.TestSimd, Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: chainID1}, NumValidators: &numVals, NumFullNodes: &numFullNodes}, }) chains, err := cf.Chains(t.Name()) @@ -93,7 +90,7 @@ func TestMessagesView(t *testing.T) { // Generate the path. // No transactions happen here. const pathName = "p" - require.NoError(t, r.GeneratePath(ctx, eRep, gaia0ChainID, gaia1ChainID, pathName)) + require.NoError(t, r.GeneratePath(ctx, eRep, chainID0, chainID1, pathName)) t.Run("create clients", func(t *testing.T) { // Creating the clients will cause transactions. @@ -105,11 +102,11 @@ client_chain_id FROM v_cosmos_messages WHERE type = "/ibc.core.client.v1.MsgCreateClient" AND chain_id = ?;` var clientChainID string - require.NoError(t, db.QueryRow(qCreateClient, gaia0ChainID).Scan(&clientChainID)) - require.Equal(t, gaia1ChainID, clientChainID) + require.NoError(t, db.QueryRow(qCreateClient, chainID0).Scan(&clientChainID)) + require.Equal(t, chainID1, clientChainID) - require.NoError(t, db.QueryRow(qCreateClient, gaia1ChainID).Scan(&clientChainID)) - require.Equal(t, gaia0ChainID, clientChainID) + require.NoError(t, db.QueryRow(qCreateClient, chainID1).Scan(&clientChainID)) + require.Equal(t, chainID0, clientChainID) }) if t.Failed() { return @@ -126,7 +123,7 @@ WHERE type = "/ibc.core.client.v1.MsgCreateClient" AND chain_id = ?;` // Wait for another block before retrieving the connections and querying for them. require.NoError(t, testutil.WaitForBlocks(ctx, 1, gaia0, gaia1)) - conns, err := r.GetConnections(ctx, eRep, gaia0ChainID) + conns, err := r.GetConnections(ctx, eRep, chainID0) require.NoError(t, err) // Collect the reported client IDs. @@ -142,7 +139,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.core.connection.v1.MsgConnectionOpenInit" AND chain_id = ? ` var clientID, counterpartyClientID string - require.NoError(t, db.QueryRow(qConnectionOpenInit, gaia0ChainID).Scan(&clientID, &counterpartyClientID)) + require.NoError(t, db.QueryRow(qConnectionOpenInit, chainID0).Scan(&clientID, &counterpartyClientID)) require.Equal(t, clientID, gaia0ClientID) require.Equal(t, counterpartyClientID, gaia1ClientID) @@ -153,7 +150,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.core.connection.v1.MsgConnectionOpenTry" AND chain_id = ? ` var counterpartyConnID string - require.NoError(t, db.QueryRow(qConnectionOpenTry, gaia1ChainID).Scan(&counterpartyClientID, &counterpartyConnID)) + require.NoError(t, db.QueryRow(qConnectionOpenTry, chainID1).Scan(&counterpartyClientID, &counterpartyConnID)) require.Equal(t, counterpartyClientID, gaia0ClientID) require.Equal(t, counterpartyConnID, gaia0ConnID) @@ -164,7 +161,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.core.connection.v1.MsgConnectionOpenAck" AND chain_id = ? ` var connID string - require.NoError(t, db.QueryRow(qConnectionOpenAck, gaia0ChainID).Scan(&connID, &counterpartyConnID)) + require.NoError(t, db.QueryRow(qConnectionOpenAck, chainID0).Scan(&connID, &counterpartyConnID)) require.Equal(t, connID, gaia0ConnID) require.Equal(t, counterpartyConnID, gaia1ConnID) @@ -174,7 +171,7 @@ conn_id FROM v_cosmos_messages WHERE type = "/ibc.core.connection.v1.MsgConnectionOpenConfirm" AND chain_id = ? ` - require.NoError(t, db.QueryRow(qConnectionOpenConfirm, gaia1ChainID).Scan(&connID)) + require.NoError(t, db.QueryRow(qConnectionOpenConfirm, chainID1).Scan(&connID)) require.Equal(t, connID, gaia0ConnID) // Not sure if this should be connection 0 or 1, as they are typically equal during this test. }) if t.Failed() { @@ -194,7 +191,7 @@ WHERE type = "/ibc.core.connection.v1.MsgConnectionOpenConfirm" AND chain_id = ? // Wait for another block before retrieving the channels and querying for them. require.NoError(t, testutil.WaitForBlocks(ctx, 1, gaia0, gaia1)) - channels, err := r.GetChannels(ctx, eRep, gaia0ChainID) + channels, err := r.GetChannels(ctx, eRep, chainID0) require.NoError(t, err) require.Len(t, channels, 1) @@ -208,7 +205,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.core.channel.v1.MsgChannelOpenInit" AND chain_id = ? ` var portID, counterpartyPortID string - require.NoError(t, db.QueryRow(qChannelOpenInit, gaia0ChainID).Scan(&portID, &counterpartyPortID)) + require.NoError(t, db.QueryRow(qChannelOpenInit, chainID0).Scan(&portID, &counterpartyPortID)) require.Equal(t, gaia0Port, portID) require.Equal(t, gaia1Port, counterpartyPortID) @@ -219,7 +216,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.core.channel.v1.MsgChannelOpenTry" AND chain_id = ? ` var counterpartyChannelID string - require.NoError(t, db.QueryRow(qChannelOpenTry, gaia1ChainID).Scan(&portID, &counterpartyPortID, &counterpartyChannelID)) + require.NoError(t, db.QueryRow(qChannelOpenTry, chainID1).Scan(&portID, &counterpartyPortID, &counterpartyChannelID)) require.Equal(t, gaia1Port, portID) require.Equal(t, gaia0Port, counterpartyPortID) require.Equal(t, counterpartyChannelID, gaia0ChannelID) @@ -231,7 +228,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.core.channel.v1.MsgChannelOpenAck" AND chain_id = ? ` var channelID string - require.NoError(t, db.QueryRow(qChannelOpenAck, gaia0ChainID).Scan(&portID, &channelID, &counterpartyChannelID)) + require.NoError(t, db.QueryRow(qChannelOpenAck, chainID0).Scan(&portID, &channelID, &counterpartyChannelID)) require.Equal(t, gaia0Port, portID) require.Equal(t, channelID, gaia0ChannelID) require.Equal(t, counterpartyChannelID, gaia1ChannelID) @@ -242,7 +239,7 @@ port_id, channel_id FROM v_cosmos_messages WHERE type = "/ibc.core.channel.v1.MsgChannelOpenConfirm" AND chain_id = ? ` - require.NoError(t, db.QueryRow(qChannelOpenConfirm, gaia1ChainID).Scan(&portID, &channelID)) + require.NoError(t, db.QueryRow(qChannelOpenConfirm, chainID1).Scan(&portID, &channelID)) require.Equal(t, gaia1Port, portID) require.Equal(t, channelID, gaia1ChannelID) }) @@ -274,7 +271,7 @@ FROM v_cosmos_messages WHERE type = "/ibc.applications.transfer.v1.MsgTransfer" AND chain_id = ? ` var portID, channelID string - require.NoError(t, db.QueryRow(qMsgTransfer, gaia0ChainID).Scan(&portID, &channelID)) + require.NoError(t, db.QueryRow(qMsgTransfer, chainID0).Scan(&portID, &channelID)) require.Equal(t, gaia0Port, portID) require.Equal(t, channelID, gaia0ChannelID) }) @@ -298,7 +295,7 @@ WHERE type = "/ibc.core.channel.v1.MsgRecvPacket" AND chain_id = ? var portID, channelID, counterpartyPortID, counterpartyChannelID string - require.NoError(t, db.QueryRow(qMsgRecvPacket, gaia1ChainID).Scan(&portID, &channelID, &counterpartyPortID, &counterpartyChannelID)) + require.NoError(t, db.QueryRow(qMsgRecvPacket, chainID1).Scan(&portID, &channelID, &counterpartyPortID, &counterpartyChannelID)) require.Equal(t, gaia0Port, portID) require.Equal(t, channelID, gaia0ChannelID) @@ -310,7 +307,7 @@ port_id, channel_id, counterparty_port_id, counterparty_channel_id FROM v_cosmos_messages WHERE type = "/ibc.core.channel.v1.MsgAcknowledgement" AND chain_id = ? ` - require.NoError(t, db.QueryRow(qMsgAck, gaia0ChainID).Scan(&portID, &channelID, &counterpartyPortID, &counterpartyChannelID)) + require.NoError(t, db.QueryRow(qMsgAck, chainID0).Scan(&portID, &channelID, &counterpartyPortID, &counterpartyChannelID)) require.Equal(t, gaia0Port, portID) require.Equal(t, channelID, gaia0ChannelID) diff --git a/examples/cosmos/chain_genesis_stake_test.go b/examples/cosmos/chain_genesis_stake_test.go index 962213845..8f0c39bda 100644 --- a/examples/cosmos/chain_genesis_stake_test.go +++ b/examples/cosmos/chain_genesis_stake_test.go @@ -34,6 +34,7 @@ func TestChainGenesisUnequalStake(t *testing.T) { ChainName: "gaia", Version: "v15.1.0", NumValidators: &validators, + NumFullNodes: &numFullNodesZero, ChainConfig: ibc.ChainConfig{ Denom: denom, ModifyGenesisAmounts: func(i int) (sdk.Coin, sdk.Coin) { diff --git a/examples/cosmos/chain_param_change_test.go b/examples/cosmos/chain_param_change_test.go index 74851e8dc..881239520 100644 --- a/examples/cosmos/chain_param_change_test.go +++ b/examples/cosmos/chain_param_change_test.go @@ -26,9 +26,6 @@ func CosmosChainParamChangeTest(t *testing.T, name, version string) { t.Parallel() - numVals := 1 - numFullNodes := 1 - // SDK v45 params for Juno genesis shortVoteGenesis := []cosmos.GenesisKV{ cosmos.NewGenesisKV("app_state.gov.voting_params.voting_period", votingPeriod), @@ -41,7 +38,7 @@ func CosmosChainParamChangeTest(t *testing.T, name, version string) { ModifyGenesis: cosmos.ModifyGenesis(shortVoteGenesis), } - chains := interchaintest.CreateChainWithConfig(t, numVals, numFullNodes, name, version, cfg) + chains := interchaintest.CreateChainWithConfig(t, numVals, numFullNodesZero, name, version, cfg) chain := chains[0].(*cosmos.CosmosChain) enableBlockDB := false diff --git a/examples/cosmos/code_coverage_test.go b/examples/cosmos/code_coverage_test.go index 0a1181782..9ca4d0a3c 100644 --- a/examples/cosmos/code_coverage_test.go +++ b/examples/cosmos/code_coverage_test.go @@ -16,14 +16,16 @@ import ( ) func TestCodeCoverage(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + t.Parallel() var ( ctx = context.Background() ExternalGoCoverDir = "/tmp/interchaintest-app-coverage" Denom = "umfx" - vals = 1 - fullNodes = 0 ) cfgA := ibc.ChainConfig{ @@ -53,8 +55,8 @@ func TestCodeCoverage(t *testing.T) { Name: "manifest", Version: cfgA.Images[0].Version, ChainName: cfgA.Name, - NumValidators: &vals, - NumFullNodes: &fullNodes, + NumValidators: &numVals, + NumFullNodes: &numFullNodesZero, ChainConfig: cfgA, }, }) diff --git a/examples/cosmos/cometmock_test.go b/examples/cosmos/cometmock_test.go index 157f5918b..1bdc4995a 100644 --- a/examples/cosmos/cometmock_test.go +++ b/examples/cosmos/cometmock_test.go @@ -24,6 +24,10 @@ var ( ) func TestCometMock(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { Name: "juno", diff --git a/examples/cosmos/ethermint_test.go b/examples/cosmos/ethermint_test.go index 2010c58d5..c822c0d31 100644 --- a/examples/cosmos/ethermint_test.go +++ b/examples/cosmos/ethermint_test.go @@ -111,6 +111,8 @@ func TestEthermintChain(t *testing.T) { ExposeAdditionalPorts: []string{"8545/tcp"}, ConfigFileOverrides: map[string]any{"config/app.toml": appTomlOverrides}, }, + NumValidators: &numValsOne, + NumFullNodes: &numFullNodesZero, }, }) @@ -143,7 +145,7 @@ func TestEthermintChain(t *testing.T) { require.Equal(t, "10000000000", balance.String()) // verify access to port exposed via ExposeAdditionalPorts - evmJsonRpcUrl, err := chain.FullNodes[0].GetHostAddress(ctx, "8545/tcp") + evmJsonRpcUrl, err := chain.GetNode().GetHostAddress(ctx, "8545/tcp") require.NoError(t, err) data := []byte(`{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0x1", null]}`) diff --git a/examples/cosmos/sdk_boundary_test.go b/examples/cosmos/sdk_boundary_test.go index 1d28bea8b..4a66cf01d 100644 --- a/examples/cosmos/sdk_boundary_test.go +++ b/examples/cosmos/sdk_boundary_test.go @@ -33,25 +33,29 @@ func TestSDKBoundaries(t *testing.T) { name: "sdk 45 <-> 50", chainSpecs: []*interchaintest.ChainSpec{ { - Name: "gaia", ChainName: "gaia", Version: "v7.0.3", // sdk 0.45.6 + Name: "gaia", ChainName: "gaia", Version: "v7.0.3", //sdk 0.45.6 + NumValidators: &numValsOne, NumFullNodes: &numFullNodesZero, }, { - Name: "ibc-go-simd", ChainName: "simd-50", Version: "feat-upgrade-sdk-v0.50", // sdk 0.50 alpha + Name: "ibc-go-simd", ChainName: "simd-50", Version: "v8.5.1", // sdk v0.50.10 + NumValidators: &numValsOne, NumFullNodes: &numFullNodesZero, }, }, - relayerVersion: "colin-event-fix", + relayerVersion: rly.DefaultContainerVersion, }, { name: "sdk 47 <-> 50", chainSpecs: []*interchaintest.ChainSpec{ { - Name: "ibc-go-simd", ChainName: "simd-47", Version: "v7.2.0", // sdk 0.47.3 + Name: "ibc-go-simd", ChainName: "simd-47", Version: "v7.2.0", //sdk 0.47.3 + NumValidators: &numValsOne, NumFullNodes: &numFullNodesZero, }, { - Name: "ibc-go-simd", ChainName: "simd-50", Version: "feat-upgrade-sdk-v0.50", // sdk 0.50 alpha + Name: "ibc-go-simd", ChainName: "simd-50", Version: "v8.5.1", // sdk v0.50.10 + NumValidators: &numValsOne, NumFullNodes: &numFullNodesZero, }, }, - relayerVersion: "colin-event-fix", + relayerVersion: rly.DefaultContainerVersion, }, } diff --git a/examples/ibc/ics_test.go b/examples/ibc/ics_test.go index 104f6c387..ca9d59ad5 100644 --- a/examples/ibc/ics_test.go +++ b/examples/ibc/ics_test.go @@ -20,8 +20,6 @@ import ( var ( icsVersions = []string{"v3.1.0", "v3.3.0", "v4.0.0"} - vals = 2 - fNodes = 0 providerChainID = "provider-1" ) @@ -34,13 +32,11 @@ func TestICS(t *testing.T) { icsVersions = []string{ver} } - relayers := []struct { + type relayerTypes struct { rly ibc.RelayerImplementation name string - }{ - {rly: ibc.Hermes, name: "hermes"}, - {rly: ibc.CosmosRly, name: "rly"}, } + relayers := []relayerTypes{{rly: ibc.CosmosRly, name: "rly"}} for _, version := range icsVersions { version := version @@ -63,15 +59,17 @@ func icsTest(t *testing.T, version string, rly ibc.RelayerImplementation) { consumerBechPrefix = "consumer" } + validators := 2 + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { Name: "ics-provider", Version: version, - NumValidators: &vals, NumFullNodes: &fNodes, + NumValidators: &validators, NumFullNodes: &numFullNodes, ChainConfig: ibc.ChainConfig{GasAdjustment: 1.5, ChainID: providerChainID, TrustingPeriod: "336h"}, }, { Name: "ics-consumer", Version: version, - NumValidators: &vals, NumFullNodes: &fNodes, + NumValidators: &validators, NumFullNodes: &numFullNodes, ChainConfig: ibc.ChainConfig{GasAdjustment: 1.5, ChainID: "consumer-1", Bech32Prefix: consumerBechPrefix, InterchainSecurityConfig: ibc.ICSConfig{ ConsumerCopyProviderKey: func(i int) bool { return i == 0 @@ -120,6 +118,8 @@ func icsTest(t *testing.T, version string, rly ibc.RelayerImplementation) { // ------------------ ICS Setup ------------------ + require.GreaterOrEqual(t, len(provider.Validators), 2) + // Finish the ICS provider chain initialization. // - Restarts the relayer to connect ics20-1 transfer channel // - Delegates tokens to the provider to update consensus value diff --git a/examples/thorchain/setup_test.go b/examples/thorchain/setup_test.go index 790fcc145..0cce1a92e 100644 --- a/examples/thorchain/setup_test.go +++ b/examples/thorchain/setup_test.go @@ -94,6 +94,12 @@ func StartExoChains(t *testing.T, ctx context.Context, client *client.Client, ne })) t.Cleanup(func() { _ = ic.Close() + for _, chain := range chains { + utxoChain, ok := chain.(*utxo.UtxoChain) + if ok { + utxoChain.Stop() + } + } }) return exoChains diff --git a/examples/utxo/start_test.go b/examples/utxo/start_test.go index 85788199f..537b92d82 100644 --- a/examples/utxo/start_test.go +++ b/examples/utxo/start_test.go @@ -56,6 +56,12 @@ func TestUtxo(t *testing.T) { })) t.Cleanup(func() { _ = ic.Close() + for _, chain := range chains { + utxoChain, ok := chain.(*utxo.UtxoChain) + if ok { + utxoChain.Stop() + } + } }) // Create and fund a user using GetAndFundTestUsers diff --git a/interchain_test.go b/interchain_test.go index 923ec8b64..b02ea8cd2 100644 --- a/interchain_test.go +++ b/interchain_test.go @@ -30,6 +30,11 @@ import ( "github.com/strangelove-ventures/interchaintest/v8/testutil" ) +var ( + numVals = 1 + numFullNodesZero = 0 +) + func TestInterchain_DuplicateChain_CosmosRly(t *testing.T) { duplicateChainTest(t, ibc.CosmosRly) } @@ -51,26 +56,26 @@ func duplicateChainTest(t *testing.T, relayerImpl ibc.RelayerImplementation) { cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ // Two otherwise identical chains that only differ by ChainID. - {Name: "gaia", ChainName: "g1", Version: "v7.0.1"}, - {Name: "gaia", ChainName: "g2", Version: "v7.0.1"}, + {Name: testutil.TestSimd, ChainName: "c1", Version: testutil.SimdVersion, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, + {Name: testutil.TestSimd, ChainName: "c2", Version: testutil.SimdVersion, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - gaia0, gaia1 := chains[0], chains[1] + chain0, chain1 := chains[0], chains[1] r := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build( t, client, network, ) ic := interchaintest.NewInterchain(). - AddChain(gaia0). - AddChain(gaia1). + AddChain(chain0). + AddChain(chain1). AddRelayer(r, "r"). AddLink(interchaintest.InterchainLink{ - Chain1: gaia0, - Chain2: gaia1, + Chain1: chain0, + Chain2: chain1, Relayer: r, }) @@ -109,26 +114,26 @@ func getRelayerWalletsTest(t *testing.T, relayerImpl ibc.RelayerImplementation) cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ // Two otherwise identical chains that only differ by ChainID. - {Name: "gaia", ChainName: "g1", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, - {Name: "gaia", ChainName: "g2", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-1"}}, + {Name: testutil.TestSimd, ChainName: "c1", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-1"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, + {Name: testutil.TestSimd, ChainName: "c2", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-2"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - gaia0, gaia1 := chains[0], chains[1] + chain0, chain1 := chains[0], chains[1] r := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build( t, client, network, ) ic := interchaintest.NewInterchain(). - AddChain(gaia0). - AddChain(gaia1). + AddChain(chain0). + AddChain(chain1). AddRelayer(r, "r"). AddLink(interchaintest.InterchainLink{ - Chain1: gaia0, - Chain2: gaia1, + Chain1: chain0, + Chain2: chain1, Relayer: r, }) @@ -188,15 +193,15 @@ func TestInterchain_CreateUser(t *testing.T) { cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ // Two otherwise identical chains that only differ by ChainID. - {Name: "gaia", ChainName: "g1", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, + {Name: testutil.TestSimd, ChainName: "c1", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-0"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - gaia0 := chains[0] + chain0 := chains[0] - ic := interchaintest.NewInterchain().AddChain(gaia0) + ic := interchaintest.NewInterchain().AddChain(chain0) defer ic.Close() rep := testreporter.NewNopReporter() @@ -230,26 +235,26 @@ func TestInterchain_CreateUser(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, mnemonic) - user, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, keyName, mnemonic, initBal, gaia0) + user, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, keyName, mnemonic, initBal, chain0) require.NoError(t, err) - require.NoError(t, testutil.WaitForBlocks(ctx, 2, gaia0)) + require.NoError(t, testutil.WaitForBlocks(ctx, 2, chain0)) require.NotEmpty(t, user.Address()) require.NotEmpty(t, user.KeyName()) - actualBalance, err := gaia0.GetBalance(ctx, user.FormattedAddress(), gaia0.Config().Denom) + actualBalance, err := chain0.GetBalance(ctx, user.FormattedAddress(), chain0.Config().Denom) require.NoError(t, err) require.True(t, actualBalance.Equal(initBal)) }) t.Run("without mnemonic", func(t *testing.T) { keyName := "regular-user-name" - users := interchaintest.GetAndFundTestUsers(t, ctx, keyName, initBal, gaia0) - require.NoError(t, testutil.WaitForBlocks(ctx, 2, gaia0)) + users := interchaintest.GetAndFundTestUsers(t, ctx, keyName, initBal, chain0) + require.NoError(t, testutil.WaitForBlocks(ctx, 2, chain0)) require.Len(t, users, 1) require.NotEmpty(t, users[0].Address()) require.NotEmpty(t, users[0].KeyName()) - actualBalance, err := gaia0.GetBalance(ctx, users[0].FormattedAddress(), gaia0.Config().Denom) + actualBalance, err := chain0.GetBalance(ctx, users[0].FormattedAddress(), chain0.Config().Denom) require.NoError(t, err) require.True(t, actualBalance.Equal(initBal)) }) @@ -301,9 +306,9 @@ func TestInterchain_ConcurrentRelayerOps(t *testing.T) { chainSpecs := make([]*interchaintest.ChainSpec, chains) for i := 0; i < chains; i++ { chainSpecs[i] = &interchaintest.ChainSpec{ - Name: "gaia", + Name: testutil.TestSimd, ChainName: fmt.Sprintf("g%d", i+1), - Version: "v7.0.1", + Version: testutil.SimdVersion, NumValidators: &numValidators, NumFullNodes: &numFullNodes, ChainConfig: ibc.ChainConfig{ @@ -365,14 +370,14 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ // Two otherwise identical chains that only differ by ChainID. - {Name: "gaia", ChainName: "g1", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, - {Name: "gaia", ChainName: "g2", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-1"}}, + {Name: testutil.TestSimd, ChainName: "c1", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-1"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, + {Name: testutil.TestSimd, ChainName: "c2", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-2"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - gaia0, gaia1 := chains[0], chains[1] + chain0, chain1 := chains[0], chains[1] r := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build( t, client, network, @@ -380,12 +385,12 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat pathName := "p" ic := interchaintest.NewInterchain(). - AddChain(gaia0). - AddChain(gaia1). + AddChain(chain0). + AddChain(chain1). AddRelayer(r, "r"). AddLink(interchaintest.InterchainLink{ - Chain1: gaia0, - Chain2: gaia1, + Chain1: chain0, + Chain2: chain1, Relayer: r, Path: pathName, }) @@ -400,7 +405,7 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat NetworkID: network, })) - testUser := interchaintest.GetAndFundTestUsers(t, ctx, "gaia-user-1", math.NewInt(10_000_000), gaia0)[0] + testUser := interchaintest.GetAndFundTestUsers(t, ctx, "chain-user-1", math.NewInt(10_000_000), chain0)[0] sendAmount := math.NewInt(10_000) @@ -409,17 +414,20 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat }) t.Run("broadcast success", func(t *testing.T) { - b := cosmos.NewBroadcaster(t, gaia0.(*cosmos.CosmosChain)) - transferAmount := sdk.Coin{Denom: gaia0.Config().Denom, Amount: sendAmount} + b := cosmos.NewBroadcaster(t, chain0.(*cosmos.CosmosChain)) + transferAmount := sdk.Coin{Denom: chain0.Config().Denom, Amount: sendAmount} memo := "" + h, err := chain0.Height(ctx) + require.NoError(t, err) + msg := transfertypes.NewMsgTransfer( "transfer", "channel-0", transferAmount, testUser.FormattedAddress(), - testUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(gaia1.Config().Bech32Prefix), - clienttypes.NewHeight(1, 1000), + testUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(chain1.Config().Bech32Prefix), + clienttypes.NewHeight(2, uint64(h)+100), 0, memo, ) @@ -429,12 +437,12 @@ func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementat }) t.Run("transfer success", func(t *testing.T) { - require.NoError(t, testutil.WaitForBlocks(ctx, 5, gaia0, gaia1)) + require.NoError(t, testutil.WaitForBlocks(ctx, 5, chain0, chain1)) - srcDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", gaia0.Config().Denom)) + srcDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", chain0.Config().Denom)) dstIbcDenom := srcDenomTrace.IBCDenom() - dstFinalBalance, err := gaia1.GetBalance(ctx, testUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(gaia1.Config().Bech32Prefix), dstIbcDenom) + dstFinalBalance, err := chain1.GetBalance(ctx, testUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(chain1.Config().Bech32Prefix), dstIbcDenom) require.NoError(t, err, "failed to get balance from dest chain") require.True(t, dstFinalBalance.Equal(sendAmount)) }) @@ -452,15 +460,15 @@ func TestInterchain_OmitGitSHA(t *testing.T) { client, network := interchaintest.DockerSetup(t) cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ - {Name: "gaia", Version: "v7.0.1"}, + {Name: testutil.TestSimd, Version: testutil.SimdVersion, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - gaia := chains[0] + chain := chains[0] ic := interchaintest.NewInterchain(). - AddChain(gaia) + AddChain(chain) rep := testreporter.NewNopReporter() eRep := rep.RelayerExecReporter(t) @@ -480,7 +488,7 @@ func TestInterchain_OmitGitSHA(t *testing.T) { func TestInterchain_ConflictRejection(t *testing.T) { t.Run("duplicate chain", func(t *testing.T) { cf := interchaintest.NewBuiltinChainFactory(zap.NewNop(), []*interchaintest.ChainSpec{ - {Name: "gaia", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, + {Name: testutil.TestSimd, Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-0"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) @@ -496,14 +504,14 @@ func TestInterchain_ConflictRejection(t *testing.T) { t.Run("chain name", func(t *testing.T) { cf := interchaintest.NewBuiltinChainFactory(zap.NewNop(), []*interchaintest.ChainSpec{ // Different ChainID, but explicit ChainName used twice. - {Name: "gaia", ChainName: "g", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, - {Name: "gaia", ChainName: "g", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-1"}}, + {Name: testutil.TestSimd, ChainName: "c", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-0"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, + {Name: testutil.TestSimd, ChainName: "c", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-1"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - require.PanicsWithError(t, "a chain with name g already exists", func() { + require.PanicsWithError(t, "a chain with name c already exists", func() { _ = interchaintest.NewInterchain().AddChain(chains[0]).AddChain(chains[1]) }) }) @@ -511,14 +519,14 @@ func TestInterchain_ConflictRejection(t *testing.T) { t.Run("chain ID", func(t *testing.T) { cf := interchaintest.NewBuiltinChainFactory(zap.NewNop(), []*interchaintest.ChainSpec{ // Valid ChainName but duplicate ChainID. - {Name: "gaia", ChainName: "g1", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, - {Name: "gaia", ChainName: "g2", Version: "v7.0.1", ChainConfig: ibc.ChainConfig{ChainID: "cosmoshub-0"}}, + {Name: testutil.TestSimd, ChainName: "c1", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-0"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, + {Name: testutil.TestSimd, ChainName: "c2", Version: testutil.SimdVersion, ChainConfig: ibc.ChainConfig{ChainID: "chain-0"}, NumValidators: &numVals, NumFullNodes: &numFullNodesZero}, }) chains, err := cf.Chains(t.Name()) require.NoError(t, err) - require.PanicsWithError(t, "a chain with ID cosmoshub-0 already exists", func() { + require.PanicsWithError(t, "a chain with ID chain-0 already exists", func() { _ = interchaintest.NewInterchain().AddChain(chains[0]).AddChain(chains[1]) }) }) diff --git a/local-interchain/Makefile b/local-interchain/Makefile index 342ef86fd..841ece774 100644 --- a/local-interchain/Makefile +++ b/local-interchain/Makefile @@ -14,7 +14,11 @@ endif # if env IGNORE_STATIC_LINK=true, then only set the main version in ldflags # required so releasing the binary does not set the main directory incorrectly -ldflags = -X main.MakeFileInstallDirectory=$(CWD) -X main.Version=$(VERSION) +# - +# Stripping the debug info with -s -w fixes this so the binary +# is properly signed (MacOS sequoia + XCode 16 causes the binary to instantly die) +# ref: https://github.com/rollchains/spawn/issues/248 +ldflags = -X main.MakeFileInstallDirectory=$(CWD) -X main.Version=$(VERSION) -s -w ifeq ($(IGNORE_STATIC_LINK),true) ldflags = -X main.Version=$(VERSION) endif diff --git a/local-interchain/chains/state/README.md b/local-interchain/chains/state/README.md index 4adec1875..3dd1ffa6a 100644 --- a/local-interchain/chains/state/README.md +++ b/local-interchain/chains/state/README.md @@ -1,3 +1,3 @@ ## avs-and-eigenlayer-deployed-anvil-state.json -- \ No newline at end of file +- \ No newline at end of file diff --git a/local-interchain/cmd/local-ic/start_chain.go b/local-interchain/cmd/local-ic/start_chain.go index 075deb110..dea17c8fd 100644 --- a/local-interchain/cmd/local-ic/start_chain.go +++ b/local-interchain/cmd/local-ic/start_chain.go @@ -36,7 +36,13 @@ local-ic start https://pastebin.com/raw/Ummk4DTM Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { configPath := args[0] - parentDir := GetDirectory() + isURL := strings.HasPrefix(configPath, "http") + + var ( + parentDir string + config *types.Config + err error + ) if path.IsAbs(configPath) { dir, err := filepath.Abs(configPath) @@ -48,10 +54,7 @@ local-ic start https://pastebin.com/raw/Ummk4DTM configPath = filepath.Base(configPath) } - var config *types.Config - var err error - - if strings.HasPrefix(configPath, "http") { + if isURL { config, err = interchain.LoadConfigFromURL(configPath) if err != nil { panic(err) @@ -60,6 +63,8 @@ local-ic start https://pastebin.com/raw/Ummk4DTM // last part of the URL to be the test name configPath = configPath[strings.LastIndex(configPath, "/")+1:] } else { + parentDir = GetDirectory() + configPath, err = GetConfigWithExtension(parentDir, configPath) if err != nil { panic(err) diff --git a/local-interchain/docs/WINDOWS.md b/local-interchain/docs/WINDOWS.md index f981815db..8dbca0a63 100644 --- a/local-interchain/docs/WINDOWS.md +++ b/local-interchain/docs/WINDOWS.md @@ -60,7 +60,7 @@ After installation, open a new cmd or shell, and you will be able to run `go ver ### 4. Downloading Make Make is a tool which controls the generation of executables and other non-source files of a program from the source files. It is necessary for building *`makefiles`*. -Make does not come with Windows, so we need to download the make binary which you can find provided by GNU [here](https://gnuwin32.sourceforge.net/packages/make.htm) and download the Binaries zip, or go to [this link](https://gnuwin32.sourceforge.net/downlinks/make-bin-zip.php) directly and begin downloading. +Make does not come with Windows, so we need to download the make binary which you can find provided by GNU [here](https://www.gnu.org/software/make/) and download the Binaries zip, or go to [this link](https://sourceforge.net/projects/gnuwin32/files/make/3.81/make-3.81-bin.zip/download?use_mirror=kent&download=) directly and begin downloading. 1. Extract the downloaded zip file 2. Go to the *`bin`* folder, copy *`make.exe`* diff --git a/local-interchain/go.mod b/local-interchain/go.mod index 4bb6ce867..8ef29ce5d 100644 --- a/local-interchain/go.mod +++ b/local-interchain/go.mod @@ -20,6 +20,7 @@ require ( github.com/cosmos/cosmos-sdk v0.50.9 github.com/cosmos/go-bip39 v1.0.0 github.com/go-playground/validator v9.31.0+incompatible + github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.2 github.com/gorilla/mux v1.8.1 github.com/spf13/cobra v1.8.1 @@ -139,7 +140,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/gorilla/websocket v1.5.0 // indirect diff --git a/local-interchain/interchain/handlers/container_log_stream.go b/local-interchain/interchain/handlers/container_log_stream.go new file mode 100644 index 000000000..0f629ab44 --- /dev/null +++ b/local-interchain/interchain/handlers/container_log_stream.go @@ -0,0 +1,152 @@ +package handlers + +import ( + "context" + "fmt" + "net/http" + "regexp" + "strconv" + "strings" + "unicode" + + dockertypes "github.com/docker/docker/api/types" + dockerclient "github.com/docker/docker/client" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "go.uber.org/zap" +) + +var removeColorRegex = regexp.MustCompile("\x1b\\[[0-9;]*m") + +type ContainerStream struct { + ctx context.Context + logger *zap.Logger + cli *dockerclient.Client + authKey string + testName string + + nameToID map[string]string +} + +func NewContainerSteam(ctx context.Context, logger *zap.Logger, cli *dockerclient.Client, authKey, testName string, vals map[string][]*cosmos.ChainNode) *ContainerStream { + nameToID := make(map[string]string) + for _, nodes := range vals { + for _, node := range nodes { + nameToID[node.Name()] = node.ContainerID() + } + } + + return &ContainerStream{ + ctx: ctx, + authKey: authKey, + cli: cli, + logger: logger, + testName: testName, + nameToID: nameToID, + } +} + +func (cs *ContainerStream) StreamContainer(w http.ResponseWriter, r *http.Request) { + if err := VerifyAuthKey(cs.authKey, r); err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + + containerID := r.URL.Query().Get("id") + if containerID == "" { + output := "No container ID provided. Available containers:\n" + for name, id := range cs.nameToID { + output += fmt.Sprintf("- %s: %s\n", name, id) + } + + fmt.Fprint(w, output) + fmt.Fprint(w, "Provide a container ID with ?id=") + return + } + + // if container id is in the cs.nameToID map, use the mapped container ID + if id, ok := cs.nameToID[containerID]; ok { + containerID = id + } else { + fmt.Fprintf(w, "Container ID %s not found\n", containerID) + return + } + + // http://127.0.0.1:8080/container_logs?id=&colored=true + isColored := strings.HasPrefix(strings.ToLower(r.URL.Query().Get("colored")), "t") + tailLines := tailLinesParam(r.URL.Query().Get("lines")) + + rr, err := cs.cli.ContainerLogs(cs.ctx, containerID, dockertypes.ContainerLogsOptions{ + ShowStdout: true, + ShowStderr: true, + Follow: true, + Details: true, + Tail: strconv.FormatUint(tailLines, 10), + }) + if err != nil { + http.Error(w, "Unable to get container logs", http.StatusInternalServerError) + return + } + defer rr.Close() + + // Set headers to keep the connection open for SSE (Server-Sent Events) + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + // Flush ensures data is sent to the client immediately + flusher, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Streaming unsupported", http.StatusInternalServerError) + return + } + + for { + buf := make([]byte, 8*1024) + n, err := rr.Read(buf) + if err != nil { + break + } + + text := string(buf[:n]) + if !isColored { + text, err = removeAnsiColorCodesFromText(string(buf[:n])) + if err != nil { + http.Error(w, "Unable to remove ANSI color codes", http.StatusInternalServerError) + return + } + } + + fmt.Fprint(w, cleanSpecialChars(text)) + flusher.Flush() + } +} + +func tailLinesParam(tailInput string) uint64 { + if tailInput == "" { + return defaultTailLines + } + + tailLines, err := strconv.ParseUint(tailInput, 10, 64) + if err != nil { + return defaultTailLines + } + + return tailLines +} + +func removeAnsiColorCodesFromText(text string) (string, error) { + return removeColorRegex.ReplaceAllString(text, ""), nil +} + +func cleanSpecialChars(text string) string { + return strings.Map(func(r rune) rune { + if r == '\n' { + return r + } + + if unicode.IsPrint(r) { + return r + } + return -1 + }, text) +} diff --git a/local-interchain/interchain/handlers/log_stream.go b/local-interchain/interchain/handlers/log_stream.go new file mode 100644 index 000000000..9279a31bb --- /dev/null +++ b/local-interchain/interchain/handlers/log_stream.go @@ -0,0 +1,164 @@ +package handlers + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "net/http" + "os" + "strconv" + "time" + + "go.uber.org/zap" +) + +const defaultTailLines = 50 + +type LogStream struct { + fName string + authKey string + logger *zap.Logger +} + +func NewLogSteam(logger *zap.Logger, file string, authKey string) *LogStream { + return &LogStream{ + fName: file, + authKey: authKey, + logger: logger, + } +} + +func (ls *LogStream) StreamLogs(w http.ResponseWriter, r *http.Request) { + if err := VerifyAuthKey(ls.authKey, r); err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + + // Set headers to keep the connection open for SSE (Server-Sent Events) + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + // Flush ensures data is sent to the client immediately + flusher, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Streaming unsupported", http.StatusInternalServerError) + return + } + + // Open the log file + file, err := os.Open(ls.fName) + if err != nil { + http.Error(w, "Unable to open log file", http.StatusInternalServerError) + return + } + defer file.Close() + + // Seek to the end of the file to read only new log entries + file.Seek(0, io.SeekEnd) + + // Read new lines from the log file + reader := bufio.NewReader(file) + + for { + select { + // In case client closes the connection, break out of loop + case <-r.Context().Done(): + return + default: + // Try to read a line + line, err := reader.ReadString('\n') + if err == nil { + // Send the log line to the client + fmt.Fprintf(w, "data: %s\n\n", line) + flusher.Flush() // Send to client immediately + } else { + // If no new log is available, wait for a short period before retrying + time.Sleep(100 * time.Millisecond) + } + } + } +} + +func (ls *LogStream) TailLogs(w http.ResponseWriter, r *http.Request) { + if err := VerifyAuthKey(ls.authKey, r); err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + + var linesToTail uint64 = defaultTailLines + tailInput := r.URL.Query().Get("lines") + if tailInput != "" { + tailLines, err := strconv.ParseUint(tailInput, 10, 64) + if err != nil { + http.Error(w, "Invalid lines input", http.StatusBadRequest) + return + } + linesToTail = tailLines + } + + logs := TailFile(ls.logger, ls.fName, linesToTail) + for _, log := range logs { + fmt.Fprintf(w, "%s\n", log) + } +} + +func TailFile(logger *zap.Logger, logFile string, lines uint64) []string { + // read the last n lines of a file + file, err := os.Open(logFile) + if err != nil { + log.Fatal(err) + } + defer file.Close() + + totalLines, err := lineCounter(file) + if err != nil { + log.Fatal(err) + } + + if lines > uint64(totalLines) { + lines = uint64(totalLines) + } + + file.Seek(0, io.SeekStart) + reader := bufio.NewReader(file) + + var logs []string + for i := 0; uint64(i) < totalLines-lines; i++ { + _, _, err := reader.ReadLine() + if err != nil { + logger.Fatal("Error reading log file", zap.Error(err)) + } + } + + for { + line, _, err := reader.ReadLine() + if err == io.EOF { + break + } + logs = append(logs, string(line)) + } + + return logs +} + +func lineCounter(r io.Reader) (uint64, error) { + buf := make([]byte, 32*1024) + var count uint64 = 0 + lineSep := []byte{'\n'} + + for { + c, err := r.Read(buf) + count += uint64(bytes.Count(buf[:c], lineSep)) + + switch { + case err == io.EOF: + return count, nil + + case err != nil: + return count, err + } + } +} diff --git a/local-interchain/interchain/handlers/types.go b/local-interchain/interchain/handlers/types.go index 8889b241a..19de04af2 100644 --- a/local-interchain/interchain/handlers/types.go +++ b/local-interchain/interchain/handlers/types.go @@ -2,10 +2,24 @@ package handlers import ( "encoding/json" + "fmt" + "net/http" "github.com/strangelove-ventures/interchaintest/v8/ibc" ) +func VerifyAuthKey(expected string, r *http.Request) error { + if expected == "" { + return nil + } + + if r.URL.Query().Get("auth_key") == expected { + return nil + } + + return fmt.Errorf("unauthorized, incorrect or no ?auth_key= provided") +} + type IbcChainConfigAlias struct { Type string `json:"type" yaml:"type"` Name string `json:"name" yaml:"name"` diff --git a/local-interchain/interchain/logs.go b/local-interchain/interchain/logs.go index 9ec2ba3b3..e750d9ee5 100644 --- a/local-interchain/interchain/logs.go +++ b/local-interchain/interchain/logs.go @@ -73,21 +73,20 @@ func DumpChainsInfoToLogs(configDir string, config *types.Config, chains []ibc.C } // == Zap Logger == -func getLoggerConfig() zap.Config { - config := zap.NewDevelopmentConfig() +func InitLogger(logFile *os.File) (*zap.Logger, error) { + // Production logger that saves logs to file and console. + pe := zap.NewProductionEncoderConfig() + pe.EncodeTime = zapcore.TimeEncoderOfLayout(time.TimeOnly) - config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder - config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + fileEncoder := zapcore.NewJSONEncoder(pe) + consoleEncoder := zapcore.NewConsoleEncoder(pe) - return config -} + level := zap.InfoLevel -func InitLogger() (*zap.Logger, error) { - config := getLoggerConfig() - logger, err := config.Build() - if err != nil { - return nil, err - } + core := zapcore.NewTee( + zapcore.NewCore(fileEncoder, zapcore.AddSync(logFile), level), + zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), level), + ) - return logger, nil + return zap.New(core), nil } diff --git a/local-interchain/interchain/router/router.go b/local-interchain/interchain/router/router.go index 3694ed634..7501a22f0 100644 --- a/local-interchain/interchain/router/router.go +++ b/local-interchain/interchain/router/router.go @@ -8,12 +8,14 @@ import ( "os" "path/filepath" + "github.com/docker/docker/client" "github.com/gorilla/mux" ictypes "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/types" "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/util" "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" + "go.uber.org/zap" "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/handlers" ) @@ -23,22 +25,39 @@ type Route struct { Methods []string `json:"methods" yaml:"methods"` } +type RouterConfig struct { + ibc.RelayerExecReporter + + Config *ictypes.Config + CosmosChains map[string]*cosmos.CosmosChain + Vals map[string][]*cosmos.ChainNode + Relayer ibc.Relayer + AuthKey string + InstallDir string + LogFile string + TestName string + Logger *zap.Logger + DockerClient *client.Client +} + func NewRouter( ctx context.Context, ic *interchaintest.Interchain, - config *ictypes.Config, - cosmosChains map[string]*cosmos.CosmosChain, - vals map[string][]*cosmos.ChainNode, - relayer ibc.Relayer, - authKey string, - eRep ibc.RelayerExecReporter, - installDir string, + rc *RouterConfig, ) *mux.Router { r := mux.NewRouter() - infoH := handlers.NewInfo(config, installDir, ctx, ic, cosmosChains, vals, relayer, eRep) + infoH := handlers.NewInfo(rc.Config, rc.InstallDir, ctx, ic, rc.CosmosChains, rc.Vals, rc.Relayer, rc.RelayerExecReporter) r.HandleFunc("/info", infoH.GetInfo).Methods(http.MethodGet) + // interaction logs + logStream := handlers.NewLogSteam(rc.Logger, rc.LogFile, rc.AuthKey) + r.HandleFunc("/logs", logStream.StreamLogs).Methods(http.MethodGet) + r.HandleFunc("/logs_tail", logStream.TailLogs).Methods(http.MethodGet) // ?lines= + + containerStream := handlers.NewContainerSteam(ctx, rc.Logger, rc.DockerClient, rc.AuthKey, rc.TestName, rc.Vals) + r.HandleFunc("/container_logs", containerStream.StreamContainer).Methods(http.MethodGet) // ?container=&colored=true&lines=10000 + wd, err := os.Getwd() if err != nil { panic(err) @@ -60,10 +79,10 @@ func NewRouter( log.Printf("chain_registry_assets.json not found in %s, not exposing endpoint.", wd) } - actionsH := handlers.NewActions(ctx, ic, cosmosChains, vals, relayer, eRep, authKey) + actionsH := handlers.NewActions(ctx, ic, rc.CosmosChains, rc.Vals, rc.Relayer, rc.RelayerExecReporter, rc.AuthKey) r.HandleFunc("/", actionsH.PostActions).Methods(http.MethodPost) - uploaderH := handlers.NewUploader(ctx, vals, authKey) + uploaderH := handlers.NewUploader(ctx, rc.Vals, rc.AuthKey) r.HandleFunc("/upload", uploaderH.PostUpload).Methods(http.MethodPost) availableRoutes := getAllMethods(*r) diff --git a/local-interchain/interchain/start.go b/local-interchain/interchain/start.go index d76d50d9f..a62f9e12b 100644 --- a/local-interchain/interchain/start.go +++ b/local-interchain/interchain/start.go @@ -3,7 +3,6 @@ package interchain import ( "context" "fmt" - "log" "math" "net/http" "os" @@ -12,7 +11,9 @@ import ( "strings" "sync" "syscall" + "time" + "github.com/google/uuid" "github.com/gorilla/handlers" "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/router" "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/types" @@ -53,11 +54,27 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { } }() + // very unique file to ensure if multiple start at the same time. + logFile, err := interchaintest.CreateLogFile(fmt.Sprintf("%d-%s.json", time.Now().Unix(), uuid.New())) + if err != nil { + panic(err) + } + defer func() { + if err := logFile.Close(); err != nil { + fmt.Println("Error closing log file: ", err) + } + + if err := os.Remove(logFile.Name()); err != nil { + fmt.Println("Error deleting log file: ", err) + } + }() + // Logger for ICTest functions only. - logger, err := InitLogger() + logger, err := InitLogger(logFile) if err != nil { panic(err) } + logger.Debug("Log file created", zap.String("file", logFile.Name())) config := ac.Cfg @@ -88,7 +105,7 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { } if err := VerifyIBCPaths(ibcpaths); err != nil { - log.Fatal("VerifyIBCPaths", err) + logger.Fatal("VerifyIBCPaths", zap.Error(err)) } // Create chain factory for all the chains @@ -98,7 +115,7 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { chains, err := cf.Chains(testName) if err != nil { - log.Fatal("cf.Chains", err) + logger.Fatal("ChainFactory chains", zap.Error(err)) } for _, chain := range chains { @@ -111,7 +128,8 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { } // Base setup - rep := testreporter.NewNopReporter() + + rep := testreporter.NewReporter(logFile) eRep = rep.RelayerExecReporter(&fakeT) client, network := interchaintest.DockerSetup(fakeT) @@ -191,7 +209,7 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { SkipPathCreation: false, }) if err != nil { - log.Fatalf("ic.Build: %v", err) + logger.Fatal("Interchain Build", zap.Error(err)) } if relayer != nil && len(ibcpaths) > 0 { @@ -201,12 +219,12 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { } if err := relayer.StartRelayer(ctx, eRep, paths...); err != nil { - log.Fatal("relayer.StartRelayer", err) + logger.Fatal("Relayer StartRelayer", zap.Error(err)) } defer func() { if err := relayer.StopRelayer(ctx, eRep); err != nil { - log.Fatal("relayer.StopRelayer", err) + logger.Error("Relayer StopRelayer", zap.Error(err)) } }() } @@ -225,7 +243,7 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { for ibcPath, chain := range icsProviderPaths { if provider, ok := chain.(*cosmos.CosmosChain); ok { if err := provider.FinishICSProviderSetup(ctx, relayer, eRep, ibcPath); err != nil { - log.Fatal("FinishICSProviderSetup", err) + logger.Error("FinishICSProviderSetup", zap.Error(err)) } } } @@ -240,7 +258,19 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { } } - r := router.NewRouter(ctx, ic, config, cosmosChains, vals, relayer, ac.AuthKey, eRep, installDir) + r := router.NewRouter(ctx, ic, &router.RouterConfig{ + Logger: logger, + RelayerExecReporter: eRep, + Config: config, + CosmosChains: cosmosChains, + DockerClient: client, + Vals: vals, + Relayer: relayer, + AuthKey: ac.AuthKey, + InstallDir: installDir, + LogFile: logFile.Name(), + TestName: testName, + }) config.Server = types.RestServer{ Host: ac.Address, @@ -259,14 +289,14 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { // Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure) corsHandler := handlers.CORS( handlers.AllowedOrigins([]string{"*"}), - handlers.AllowedHeaders([]string{"*"}), + handlers.AllowedHeaders([]string{"Content-Type", "Authorization", "Accept"}), handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS", "DELETE"}), handlers.AllowCredentials(), handlers.ExposedHeaders([]string{"*"}), ) if err := http.ListenAndServe(serverAddr, corsHandler(r)); err != nil { - log.Default().Println(err) + logger.Error("HTTP ListenAndServe", zap.Error(err)) } }() @@ -280,7 +310,7 @@ func StartChain(installDir, chainCfgFile string, ac *types.AppStartConfig) { // Save to logs.json file for runtime chain information. DumpChainsInfoToLogs(installDir, config, chains, connections) - log.Println("\nLocal-IC API is running on ", fmt.Sprintf("http://%s:%s", config.Server.Host, config.Server.Port)) + logger.Info("Local-IC API is running", zap.String("url", fmt.Sprintf("http://%s:%s", config.Server.Host, config.Server.Port))) if err = testutil.WaitForBlocks(ctx, math.MaxInt, chains[0]); err != nil { // when the network is stopped / killed (ctrl + c), ignore error diff --git a/testutil/doc.go b/testutil/doc.go index f6542ace6..306af54ea 100644 --- a/testutil/doc.go +++ b/testutil/doc.go @@ -1,2 +1,7 @@ // Package testutil includes convenience functions and types to help with testing package testutil + +const ( + TestSimd = "ibc-go-simd" + SimdVersion = "v8.5.1" +)