Skip to content

Commit

Permalink
Add wasm e2e test (babylonchain#380)
Browse files Browse the repository at this point in the history
* Add wasm e2e test
  • Loading branch information
KonradStaniec authored May 11, 2023
1 parent a19d78a commit 60a2be5
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 2 deletions.
8 changes: 6 additions & 2 deletions test/e2e/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# End-to-end Tests

## Structure

### `e2e` Package

The `e2e` package defines an integration testing suite used for full
end-to-end testing functionality. The package is copy of Osmosis e2e testing
approach.


### Wasm contract used for e2e testing

Wasm contract located in `bytecode/storage_contract.wasm` is compiled from most recent commit `main` branch - https://github.com/babylonchain/storage-contract

This contract uses feature specific to Babylon, through Babylon bindings library.

### Common Problems

Please note that if the tests are stopped mid-way, the e2e framework might fail to start again due to duplicated containers. Make sure that
Expand Down
Binary file added test/e2e/bytecode/storage_contract.wasm
Binary file not shown.
28 changes: 28 additions & 0 deletions test/e2e/configurer/chain/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"math/rand"
"strings"
"time"

"github.com/cosmos/cosmos-sdk/types/bech32"
Expand Down Expand Up @@ -165,3 +166,30 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) {
}
}
}

func (n *NodeConfig) StoreWasmCode(wasmFile, from string) {
n.LogActionF("storing wasm code from file %s", wasmFile)
cmd := []string{"babylond", "tx", "wasm", "store", wasmFile, fmt.Sprintf("--from=%s", from), "--gas=auto", "--gas-prices=1ubbn", "--gas-adjustment=1.3"}
n.LogActionF(strings.Join(cmd, " "))
_, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd)
require.NoError(n.t, err)
n.LogActionF("successfully stored")
}

func (n *NodeConfig) InstantiateWasmContract(codeId, initMsg, from string) {
n.LogActionF("instantiating wasm contract %s with %s", codeId, initMsg)
cmd := []string{"babylond", "tx", "wasm", "instantiate", codeId, initMsg, fmt.Sprintf("--from=%s", from), "--no-admin", "--label=contract", "--gas=auto", "--gas-prices=1ubbn", "--gas-adjustment=1.3"}
n.LogActionF(strings.Join(cmd, " "))
_, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd)
require.NoError(n.t, err)
n.LogActionF("successfully initialized")
}

func (n *NodeConfig) WasmExecute(contract, execMsg, from string) {
n.LogActionF("executing %s on wasm contract %s from %s", execMsg, contract, from)
cmd := []string{"babylond", "tx", "wasm", "execute", contract, execMsg, fmt.Sprintf("--from=%s", from)}
n.LogActionF(strings.Join(cmd, " "))
_, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd)
require.NoError(n.t, err)
n.LogActionF("successfully executed")
}
8 changes: 8 additions & 0 deletions test/e2e/configurer/chain/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ func (n *NodeConfig) WaitUntilBtcHeight(height uint64) {
}, fmt.Sprintf("Timed out waiting for btc height %d", height))
}

func (n *NodeConfig) WaitForNextBlock() {
latest := n.LatestBlockNumber()
n.WaitForCondition(func() bool {
newLatest := n.LatestBlockNumber()
return newLatest > latest
}, fmt.Sprintf("Timed out waiting for next block. Current height is: %d", latest))
}

func (n *NodeConfig) extractOperatorAddressIfValidator() error {
if !n.IsValidator {
n.t.Logf("node (%s) is not a validator, skipping", n.Name)
Expand Down
61 changes: 61 additions & 0 deletions test/e2e/configurer/chain/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/stretchr/testify/require"

wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/babylonchain/babylon/test/e2e/util"
blc "github.com/babylonchain/babylon/x/btclightclient/types"
ct "github.com/babylonchain/babylon/x/checkpointing/types"
Expand Down Expand Up @@ -291,3 +292,63 @@ func (n *NodeConfig) QueryLightClientHeightCheckpointReported(ckptHash []byte) (
}
return mResponse.BtcLightClientHeight, nil
}

func (n *NodeConfig) QueryLatestWasmCodeID() uint64 {
path := "/cosmwasm/wasm/v1/code"

bz, err := n.QueryGRPCGateway(path, url.Values{})
require.NoError(n.t, err)

var response wasmtypes.QueryCodesResponse
err = util.Cdc.UnmarshalJSON(bz, &response)
require.NoError(n.t, err)
if len(response.CodeInfos) == 0 {
return 0
}
return response.CodeInfos[len(response.CodeInfos)-1].CodeID
}

func (n *NodeConfig) QueryContractsFromId(codeId int) ([]string, error) {
path := fmt.Sprintf("/cosmwasm/wasm/v1/code/%d/contracts", codeId)
bz, err := n.QueryGRPCGateway(path, url.Values{})

require.NoError(n.t, err)

var contractsResponse wasmtypes.QueryContractsByCodeResponse
if err := util.Cdc.UnmarshalJSON(bz, &contractsResponse); err != nil {
return nil, err
}

return contractsResponse.Contracts, nil
}

func (n *NodeConfig) QueryWasmSmart(contract string, msg string, result any) error {
// base64-encode the msg
encodedMsg := base64.StdEncoding.EncodeToString([]byte(msg))
path := fmt.Sprintf("/cosmwasm/wasm/v1/contract/%s/smart/%s", contract, encodedMsg)

bz, err := n.QueryGRPCGateway(path, url.Values{})
if err != nil {
return err
}

var response wasmtypes.QuerySmartContractStateResponse
err = util.Cdc.UnmarshalJSON(bz, &response)
if err != nil {
return err
}

err = json.Unmarshal(response.Data, &result)
if err != nil {
return err
}
return nil
}

func (n *NodeConfig) QueryWasmSmartObject(contract string, msg string) (resultObject map[string]interface{}, err error) {
err = n.QueryWasmSmart(contract, msg, &resultObject)
if err != nil {
return nil, err
}
return resultObject, nil
}
7 changes: 7 additions & 0 deletions test/e2e/containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"os"
"regexp"
"strings"
"testing"
Expand Down Expand Up @@ -201,6 +202,11 @@ func (m *Manager) RunHermesResource(chainAID, osmoARelayerNodeName, osmoAValMnem
// RunNodeResource runs a node container. Assings containerName to the container.
// Mounts the container on valConfigDir volume on the running host. Returns the container resource and error if any.
func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir string) (*dockertest.Resource, error) {
pwd, err := os.Getwd()
if err != nil {
return nil, err
}

runOpts := &dockertest.RunOptions{
Name: containerName,
Repository: BabylonContainerName,
Expand All @@ -215,6 +221,7 @@ func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir st
Platform: "linux/x86_64",
Mounts: []string{
fmt.Sprintf("%s/:/home/babylon/babylondata", valCondifDir),
fmt.Sprintf("%s/bytecode:/bytecode", pwd),
},
}

Expand Down
45 changes: 45 additions & 0 deletions test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
package e2e

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"

"github.com/babylonchain/babylon/test/e2e/initialization"
ct "github.com/babylonchain/babylon/x/checkpointing/types"
"github.com/stretchr/testify/require"
)

// Most simple test, just checking that two chains are up and connected through
Expand Down Expand Up @@ -74,3 +78,44 @@ func (s *IntegrationTestSuite) TestIbcCheckpointing() {
_, err = chainB.GetDefaultNode()
s.NoError(err)
}

func (s *IntegrationTestSuite) TestWasm() {
contractPath := "/bytecode/storage_contract.wasm"
chainA := s.configurer.GetChainConfig(0)
nonValidatorNode, err := chainA.GetNodeAtIndex(2)
require.NoError(s.T(), err)
nonValidatorNode.StoreWasmCode(contractPath, initialization.ValidatorWalletName)
nonValidatorNode.WaitForNextBlock()
latestWasmId := int(nonValidatorNode.QueryLatestWasmCodeID())
nonValidatorNode.InstantiateWasmContract(
strconv.Itoa(latestWasmId),
`{}`,
initialization.ValidatorWalletName,
)
nonValidatorNode.WaitForNextBlock()
contracts, err := nonValidatorNode.QueryContractsFromId(1)
s.NoError(err)
s.Require().Len(contracts, 1, "Wrong number of contracts for the counter")
contractAddr := contracts[0]

data := []byte{1, 2, 3, 4, 5}
dataHex := hex.EncodeToString(data)
dataHash := sha256.Sum256(data)
dataHashHex := hex.EncodeToString(dataHash[:])

storeMsg := fmt.Sprintf(`{"save_data":{"data":"%s"}}`, dataHex)
nonValidatorNode.WasmExecute(contractAddr, storeMsg, initialization.ValidatorWalletName)
nonValidatorNode.WaitForNextBlock()
queryMsg := fmt.Sprintf(`{"check_data": {"data_hash":"%s"}}`, dataHashHex)
queryResult, err := nonValidatorNode.QueryWasmSmartObject(contractAddr, queryMsg)
require.NoError(s.T(), err)
finalized := queryResult["finalized"].(bool)
latestFinalizedEpoch := int(queryResult["latest_finalized_epoch"].(float64))
saveEpoch := int(queryResult["save_epoch"].(float64))

require.False(s.T(), finalized)
// in previous test we already finalized epoch 3
require.Equal(s.T(), 3, latestFinalizedEpoch)
// data is not finalized yet, so save epoch should be strictly greater than latest finalized epoch
require.Greater(s.T(), saveEpoch, latestFinalizedEpoch)
}

0 comments on commit 60a2be5

Please sign in to comment.