Skip to content

Commit

Permalink
feat: e2e parallel (osmosis-labs#5598)
Browse files Browse the repository at this point in the history
* e2e parallel

* dont add node to config

* go mod

* temp node

* temp node config

* remove mutex

* add mutex to everything

* remove mutex from upgrade.go

* remove pointer

* move node placement

* more mutex

* test

* test

* dont use config for txs

* use go routines and less blocks for vote

* sequence nums amoung other fixes

* remove no longer needed mutex

* remove old sendIBC

* reintroduce secondary sendibc func

* add back in balance check

* mod check for seq num

* more cleaning

* more speed

* parallel 4 to 8 check

* parallel to 3

* inc goroutine procs

* back to 4 procs and reorder tests

* change epoch to day from week

* updates for new upgrade handler

* final speedup

* add todos

* Minor de-duplication of logic in E2E (osmosis-labs#5690)

* minor dedup

* more minor de-duplication continued

* Update tests/e2e/helpers_e2e_test.go

Co-authored-by: Dev Ojha <[email protected]>

* Update tests/e2e/e2e_test.go

Co-authored-by: Roman <[email protected]>

* Update tests/e2e/e2e_test.go

Co-authored-by: Dev Ojha <[email protected]>

* rename

* split ibc tests back up

* add code comment

---------

Co-authored-by: Dev Ojha <[email protected]>
Co-authored-by: Roman <[email protected]>
  • Loading branch information
3 people authored Jul 3, 2023
1 parent faacd4e commit 8e2b5b5
Show file tree
Hide file tree
Showing 11 changed files with 993 additions and 441 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ test-e2e: e2e-setup test-e2e-ci e2e-remove-resources
# does not do any validation about the state of the Docker environment
# As a result, avoid using this locally.
test-e2e-ci:
@VERSION=$(VERSION) OSMOSIS_E2E=True OSMOSIS_E2E_DEBUG_LOG=False OSMOSIS_E2E_UPGRADE_VERSION=$(E2E_UPGRADE_VERSION) go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E)
@VERSION=$(VERSION) OSMOSIS_E2E=True OSMOSIS_E2E_DEBUG_LOG=False OSMOSIS_E2E_UPGRADE_VERSION=$(E2E_UPGRADE_VERSION) go test -mod=readonly -timeout=25m -v $(PACKAGES_E2E) -p 4

# test-e2e-debug runs a full e2e test suite but does
# not attempt to delete Docker resources at the end.
Expand Down
27 changes: 25 additions & 2 deletions tests/e2e/configurer/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,41 @@ func (bc *baseConfigurer) GetChainConfig(chainIndex int) *chain.Config {
}

func (bc *baseConfigurer) RunValidators() error {
errChan := make(chan error, len(bc.chainConfigs))

// Launch goroutines for each chainConfig
for _, chainConfig := range bc.chainConfigs {
if err := bc.runValidators(chainConfig); err != nil {
go func(config *chain.Config) {
err := bc.runValidators(config)
errChan <- err
}(chainConfig)
}

// Collect errors from goroutines
for range bc.chainConfigs {
if err := <-errChan; err != nil {
return err
}
}

return nil
}

func (bc *baseConfigurer) runValidators(chainConfig *chain.Config) error {
bc.t.Logf("starting %s validator containers...", chainConfig.Id)

errCh := make(chan error) // Channel to collect errors

// Iterate over each node
for _, node := range chainConfig.NodeConfigs {
if err := node.Run(); err != nil {
go func(n *chain.NodeConfig) {
errCh <- n.Run() // Run the node and send any error to the channel
}(node)
}

// Wait for goroutines to finish and collect errors
for range chainConfig.NodeConfigs {
if err := <-errCh; err != nil {
return err
}
}
Expand Down
191 changes: 55 additions & 136 deletions tests/e2e/configurer/chain/chain.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
package chain

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
"time"

paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils"

ibcratelimittypes "github.com/osmosis-labs/osmosis/v16/x/ibc-rate-limit/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
coretypes "github.com/tendermint/tendermint/rpc/core/types"

"github.com/osmosis-labs/osmosis/v16/tests/e2e/util"

appparams "github.com/osmosis-labs/osmosis/v16/app/params"
"github.com/osmosis-labs/osmosis/v16/tests/e2e/configurer/config"

Expand All @@ -39,6 +30,7 @@ type Config struct {
LatestProposalNumber int
LatestLockNumber int
NodeConfigs []*NodeConfig
NodeTempConfigs []*NodeConfig

LatestCodeId int

Expand Down Expand Up @@ -85,6 +77,20 @@ func (c *Config) CreateNode(initNode *initialization.Node) *NodeConfig {
return nodeConfig
}

// CreateNodeTemp returns new initialized NodeConfig and appends it to a separate list of temporary nodes.
// This is used for nodes that are intended to only exist for a single test. Without this separation,
// parallel tests will try and use this node and fail.
func (c *Config) CreateNodeTemp(initNode *initialization.Node) *NodeConfig {
nodeConfig := &NodeConfig{
Node: *initNode,
chainId: c.Id,
containerManager: c.containerManager,
t: c.t,
}
c.NodeTempConfigs = append(c.NodeTempConfigs, nodeConfig)
return nodeConfig
}

// RemoveNode removes node and stops it from running.
func (c *Config) RemoveNode(nodeName string) error {
for i, node := range c.NodeConfigs {
Expand All @@ -96,6 +102,17 @@ func (c *Config) RemoveNode(nodeName string) error {
return fmt.Errorf("node %s not found", nodeName)
}

// RemoveTempNode removes a temporary node and stops it from running.
func (c *Config) RemoveTempNode(nodeName string) error {
for i, node := range c.NodeTempConfigs {
if node.Name == nodeName {
c.NodeTempConfigs = append(c.NodeTempConfigs[:i], c.NodeTempConfigs[i+1:]...)
return node.Stop()
}
}
return fmt.Errorf("node %s not found", nodeName)
}

// WaitUntilEpoch waits for the chain to reach the specified epoch.
func (c *Config) WaitUntilEpoch(epoch int64, epochIdentifier string) {
node, err := c.GetDefaultNode()
Expand Down Expand Up @@ -152,9 +169,20 @@ func (c *Config) SendIBC(dstChain *Config, recipient string, token sdk.Coin) {
require.NoError(c.t, err)

// removes the fee token from balances for calculating the difference in other tokens
// before and after the IBC send.
// before and after the IBC send. Since we run tests in parallel now, some tests may
// send uosmo between accounts while this test is running. Since we don't care about
// non ibc denoms, its safe to filter uosmo out.
// TODO: we can probably improve this by specifying the denom we expect to be received
// and just look out for that. This wasn't required prior to parallel tests, but
// would be useful now.
removeFeeTokenFromBalance := func(balance sdk.Coins) sdk.Coins {
feeRewardTokenBalance := balance.FilterDenoms([]string{initialization.E2EFeeToken})
filteredCoinDenoms := []string{}
for _, coin := range balance {
if !strings.HasPrefix(coin.Denom, "ibc/") {
filteredCoinDenoms = append(filteredCoinDenoms, coin.Denom)
}
}
feeRewardTokenBalance := balance.FilterDenoms(filteredCoinDenoms)
return balance.Sub(feeRewardTokenBalance)
}

Expand All @@ -170,7 +198,6 @@ func (c *Config) SendIBC(dstChain *Config, recipient string, token sdk.Coin) {
func() bool {
balancesDstPostWithTxFeeBalance, err := dstNode.QueryBalances(recipient)
require.NoError(c.t, err)

balancesDstPost := removeFeeTokenFromBalance(balancesDstPostWithTxFeeBalance)

ibcCoin := balancesDstPost.Sub(balancesDstPre)
Expand All @@ -192,35 +219,6 @@ func (c *Config) SendIBC(dstChain *Config, recipient string, token sdk.Coin) {
c.t.Log("successfully sent IBC tokens")
}

func (c *Config) EnableSuperfluidAsset(denom string) {
chain, err := c.GetDefaultNode()
require.NoError(c.t, err)
chain.SubmitSuperfluidProposal(denom, sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit)))
c.LatestProposalNumber += 1
chain.DepositProposal(c.LatestProposalNumber, false)
for _, node := range c.NodeConfigs {
node.VoteYesProposal(initialization.ValidatorWalletName, c.LatestProposalNumber)
}
}

func (c *Config) LockAndAddToExistingLock(amount sdk.Int, denom, lockupWalletAddr, lockupWalletSuperfluidAddr string) {
chain, err := c.GetDefaultNode()
require.NoError(c.t, err)

// lock tokens
chain.LockTokens(fmt.Sprintf("%v%s", amount, denom), "240s", lockupWalletAddr)
c.LatestLockNumber += 1
// add to existing lock
chain.AddToExistingLock(amount, denom, "240s", lockupWalletAddr)

// superfluid lock tokens
chain.LockTokens(fmt.Sprintf("%v%s", amount, denom), "240s", lockupWalletSuperfluidAddr)
c.LatestLockNumber += 1
chain.SuperfluidDelegate(c.LatestLockNumber, c.NodeConfigs[1].OperatorAddress, lockupWalletSuperfluidAddr)
// add to existing lock
chain.AddToExistingLock(amount, denom, "240s", lockupWalletSuperfluidAddr)
}

// GetDefaultNode returns the default node of the chain.
// The default node is the first one created. Returns error if no
// ndoes created.
Expand Down Expand Up @@ -250,115 +248,36 @@ func (c *Config) getNodeAtIndex(nodeIndex int) (*NodeConfig, error) {
return c.NodeConfigs[nodeIndex], nil
}

func (c *Config) SubmitParamChangeProposal(subspace, key string, value []byte) error {
func (c *Config) SubmitCreateConcentratedPoolProposal() (uint64, error) {
node, err := c.GetDefaultNode()
if err != nil {
return err
}

proposal := paramsutils.ParamChangeProposalJSON{
Title: "Param Change",
Description: fmt.Sprintf("Changing the %s param", key),
Changes: paramsutils.ParamChangesJSON{
paramsutils.ParamChangeJSON{
Subspace: subspace,
Key: key,
Value: value,
},
},
Deposit: "625000000uosmo",
}
proposalJson, err := json.Marshal(proposal)
if err != nil {
return err
return 0, err
}

node.SubmitParamChangeProposal(string(proposalJson), initialization.ValidatorWalletName)
propNumber := node.SubmitCreateConcentratedPoolProposal(sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit)))
c.LatestProposalNumber += 1

for _, n := range c.NodeConfigs {
n.VoteYesProposal(initialization.ValidatorWalletName, c.LatestProposalNumber)
}
node.DepositProposal(propNumber, false)

require.Eventually(c.t, func() bool {
status, err := node.QueryPropStatus(c.LatestProposalNumber)
if err != nil {
return false
}
return status == proposalStatusPassed
}, time.Second*30, time.Millisecond*500)
return nil
}

func (c *Config) SubmitCreateConcentratedPoolProposal() error {
node, err := c.GetDefaultNode()
if err != nil {
return err
}

node.SubmitCreateConcentratedPoolProposal(sdk.NewCoin(appparams.BaseCoinUnit, sdk.NewInt(config.InitialMinDeposit)))
c.LatestProposalNumber += 1
node.DepositProposal(c.LatestProposalNumber, false)
var wg sync.WaitGroup

for _, n := range c.NodeConfigs {
n.VoteYesProposal(initialization.ValidatorWalletName, c.LatestProposalNumber)
wg.Add(1)
go func(nodeConfig *NodeConfig) {
defer wg.Done()
nodeConfig.VoteYesProposal(initialization.ValidatorWalletName, propNumber)
}(n)
}

wg.Wait()

require.Eventually(c.t, func() bool {
status, err := node.QueryPropStatus(c.LatestProposalNumber)
status, err := node.QueryPropStatus(propNumber)
if err != nil {
return false
}
return status == proposalStatusPassed
}, time.Second*30, time.Millisecond*500)
return nil
}

func (c *Config) SetupRateLimiting(paths, gov_addr string) (string, error) {
node, err := c.GetDefaultNode()
if err != nil {
return "", err
}

// copy the contract from x/rate-limit/testdata/
wd, err := os.Getwd()
if err != nil {
return "", err
}
// go up two levels
projectDir := filepath.Dir(filepath.Dir(wd))
fmt.Println(wd, projectDir)
_, err = util.CopyFile(projectDir+"/x/ibc-rate-limit/bytecode/rate_limiter.wasm", wd+"/scripts/rate_limiter.wasm")
if err != nil {
return "", err
}

node.StoreWasmCode("rate_limiter.wasm", initialization.ValidatorWalletName)
c.LatestCodeId = int(node.QueryLatestWasmCodeID())
node.InstantiateWasmContract(
strconv.Itoa(c.LatestCodeId),
fmt.Sprintf(`{"gov_module": "%s", "ibc_module": "%s", "paths": [%s] }`, gov_addr, node.PublicAddress, paths),
initialization.ValidatorWalletName)

contracts, err := node.QueryContractsFromId(c.LatestCodeId)
if err != nil {
return "", err
}

contract := contracts[len(contracts)-1]

err = c.SubmitParamChangeProposal(
ibcratelimittypes.ModuleName,
string(ibcratelimittypes.KeyContractAddress),
[]byte(fmt.Sprintf(`"%s"`, contract)),
)
if err != nil {
return "", err
}
require.Eventually(c.t, func() bool {
val := node.QueryParams(ibcratelimittypes.ModuleName, string(ibcratelimittypes.KeyContractAddress))
return strings.Contains(val, contract)
}, time.Second*30, time.Millisecond*500)
fmt.Println("contract address set to", contract)
return contract, nil
poolId := node.QueryNumPools()
return poolId, nil
}
Loading

0 comments on commit 8e2b5b5

Please sign in to comment.