From c9d5b34276cd00514603fc8f9371edc7133030c6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 10:15:58 -0400 Subject: [PATCH 01/29] Init commit --- go.mod | 1 + go.sum | 1 + server/start.go | 16 ++- testutil/network.go | 281 +++++++++++++++++++++++++++++++++++++++ testutil/network_test.go | 34 +++++ testutil/util.go | 188 ++++++++++++++++++++++++++ 6 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 testutil/network.go create mode 100644 testutil/network_test.go create mode 100644 testutil/util.go diff --git a/go.mod b/go.mod index 6d4c3881c48a..9a5bc48b7dfa 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/tendermint/tendermint v0.33.5 github.com/tendermint/tm-db v0.5.1 golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e google.golang.org/grpc v1.29.1 google.golang.org/protobuf v1.24.0 // indirect gopkg.in/yaml.v2 v2.3.0 diff --git a/go.sum b/go.sum index 57c691acec6c..d2d9421953fd 100644 --- a/go.sum +++ b/go.sum @@ -607,6 +607,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/server/start.go b/server/start.go index d4eb9f76ded3..e8a3e2f0ef7c 100644 --- a/server/start.go +++ b/server/start.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "runtime/pprof" + "time" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -192,8 +193,9 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator return err } - config := config.GetConfig() var apiSrv *api.Server + + config := config.GetConfig() if config.API.Enable { genDoc, err := genDocProvider() if err != nil { @@ -213,8 +215,18 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator apiSrv = api.New(ctx) app.RegisterAPIRoutes(apiSrv) - if err := apiSrv.Start(config); err != nil { + errCh := make(chan error) + + go func() { + if err := apiSrv.Start(config); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: return err + case <-time.After(5 * time.Second): // assume server started successfully } } diff --git a/testutil/network.go b/testutil/network.go new file mode 100644 index 000000000000..6db01f781fc0 --- /dev/null +++ b/testutil/network.go @@ -0,0 +1,281 @@ +package testutil + +import ( + "bufio" + "encoding/json" + "fmt" + "net/url" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + tmcfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + tmflags "github.com/tendermint/tendermint/libs/cli/flags" + "github.com/tendermint/tendermint/libs/log" + tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/node" + tmclient "github.com/tendermint/tendermint/rpc/client" + "golang.org/x/sync/errgroup" + + "github.com/cosmos/cosmos-sdk/client" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/simapp" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + _, cdc = simapp.MakeCodecs() +) + +// Config defines the necessary configuration used to bootstrap and start an +// in-process local testing network. +type Config struct { + GenesisState map[string]json.RawMessage + TimeoutCommit time.Duration + ChainID string + NumValidators int + BondDenom string + MinGasPrices string + Passphrase string + AccountTokens sdk.Int + StakingTokens sdk.Int + BondedTokens sdk.Int + EnableLogging bool +} + +// DefaultConfig returns a sane default configuration suitable for nearly all +// testing requirements. +func DefaultConfig() Config { + return Config{ + GenesisState: simapp.ModuleBasics.DefaultGenesis(cdc), + TimeoutCommit: 2 * time.Second, + ChainID: "chain-" + tmrand.NewRand().Str(6), + NumValidators: 4, + BondDenom: sdk.DefaultBondDenom, + MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), + Passphrase: clientkeys.DefaultKeyPass, + AccountTokens: sdk.TokensFromConsensusPower(1000), + StakingTokens: sdk.TokensFromConsensusPower(500), + BondedTokens: sdk.TokensFromConsensusPower(100), + } +} + +type ( + // Network defines a local in-process testing network using SimApp. It can be + // configured to start any number of validators, each with its own RPC and API + // clients. Typically, this test network would be used in client and integration + // testing where user input is expected. + Network struct { + T *testing.T + BaseDir string + Validators []*Validator + } + + // Validator defines an in-process Tendermint validator node. Through this object, + // a client can make RPC and API calls and interact with any client command + // or handler. + Validator struct { + AppConfig *srvconfig.Config + ClientCtx client.Context + Ctx *server.Context + Dir string + NodeID string + PubKey crypto.PubKey + Moniker string + RPCAddress string + P2PAddress string + Address sdk.AccAddress + ValAddress sdk.ValAddress + RPCClient tmclient.Client + + tmNode *node.Node + api *api.Server + } +) + +func NewTestNetwork(t *testing.T, cfg Config) *Network { + network := &Network{ + T: t, + BaseDir: os.TempDir(), + Validators: make([]*Validator, cfg.NumValidators), + } + + t.Log("preparing test network...") + + monikers := make([]string, cfg.NumValidators) + nodeIDs := make([]string, cfg.NumValidators) + valPubKeys := make([]crypto.PubKey, cfg.NumValidators) + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + buf := bufio.NewReader(os.Stdin) + + // generate private keys, node IDs, and initial transactions + for i := 0; i < cfg.NumValidators; i++ { + appCfg := srvconfig.DefaultConfig() + appCfg.Pruning = storetypes.PruningOptionNothing + appCfg.MinGasPrices = cfg.MinGasPrices + appCfg.API.Enable = true + appCfg.API.Swagger = false + appCfg.Telemetry.Enabled = false + + apiAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + appCfg.API.Address = apiAddr + + ctx := server.NewDefaultContext() + tmCfg := ctx.Config + tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit + + logger := log.NewNopLogger() + if cfg.EnableLogging { + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel()) + } + + ctx.Logger = logger + + nodeDirName := fmt.Sprintf("node%d", i) + nodeDir := filepath.Join(network.BaseDir, nodeDirName, "simd") + clientDir := filepath.Join(network.BaseDir, nodeDirName, "simcli") + gentxsDir := filepath.Join(network.BaseDir, "gentxs") + + require.NoError(t, os.MkdirAll(filepath.Join(nodeDir, "config"), 0755)) + require.NoError(t, os.MkdirAll(clientDir, 0755)) + + tmCfg.SetRoot(nodeDir) + tmCfg.Moniker = nodeDirName + monikers[i] = nodeDirName + + proxyAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.ProxyApp = proxyAddr + + rpcAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.RPC.ListenAddress = rpcAddr + + p2pAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.P2P.ListenAddress = p2pAddr + tmCfg.P2P.AddrBookStrict = false + tmCfg.P2P.AllowDuplicateIP = true + + nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) + require.NoError(t, err) + nodeIDs[i] = nodeID + valPubKeys[i] = pubKey + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf) + require.NoError(t, err) + + addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, cfg.Passphrase, true) + require.NoError(t, err) + + info := map[string]string{"secret": secret} + infoBz, err := json.Marshal(info) + require.NoError(t, err) + + // save private key seed words + require.NoError(t, writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz)) + + balances := sdk.Coins{ + sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), cfg.AccountTokens), + sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens), + } + + genFiles = append(genFiles, tmCfg.GenesisFile()) + genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: balances.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + createValMsg := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(sdk.DefaultBondDenom, cfg.BondedTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + + p2pURL, err := url.Parse(p2pAddr) + require.NoError(t, err) + + memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) + tx := authtypes.NewStdTx([]sdk.Msg{createValMsg}, authtypes.StdFee{}, []authtypes.StdSignature{}, memo) //nolint:staticcheck // SA1019: authtypes.StdFee is deprecated + txBldr := authtypes.TxBuilder{}.WithChainID(cfg.ChainID).WithMemo(memo).WithKeybase(kb) + + signedTx, err := txBldr.SignStdTx(nodeDirName, tx, false) + require.NoError(t, err) + + txBz, err := cdc.MarshalJSON(signedTx) + require.NoError(t, err) + require.NoError(t, writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz)) + + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appCfg) + + network.Validators[i] = &Validator{ + AppConfig: appCfg, + Ctx: ctx, + Dir: filepath.Join(network.BaseDir, nodeDirName), + NodeID: nodeID, + PubKey: pubKey, + Moniker: nodeDirName, + RPCAddress: rpcAddr, + P2PAddress: p2pAddr, + Address: addr, + ValAddress: sdk.ValAddress(addr), + } + } + + require.NoError(t, initGenFiles(cfg, genAccounts, genBalances, genFiles)) + require.NoError(t, collectGenFiles(cfg, network.Validators, network.BaseDir)) + + t.Log("starting test network...") + var eg errgroup.Group + for _, v := range network.Validators { + val := v + eg.Go(func() error { + return startInProcess(cfg, val) + }) + } + + require.NoError(t, eg.Wait()) + t.Log("started test network") + + // TODO: Do we need to trap signal + + return network +} + +func (n *Network) Cleanup() { + n.T.Log("cleaning up test network...") + + for _, v := range n.Validators { + if v.tmNode != nil && v.tmNode.IsRunning() { + _ = v.tmNode.Stop() + } + + if v.api != nil { + _ = v.api.Close() + } + } + + _ = os.RemoveAll(n.BaseDir) + n.T.Log("finished cleaning up test network") +} diff --git a/testutil/network_test.go b/testutil/network_test.go new file mode 100644 index 000000000000..a1a69bcf1512 --- /dev/null +++ b/testutil/network_test.go @@ -0,0 +1,34 @@ +package testutil + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestNetwork_Liveness(t *testing.T) { + n := NewTestNetwork(t, DefaultConfig()) + defer n.Cleanup() + + require.NotNil(t, n) + require.NotEmpty(t, n.Validators) + client := n.Validators[0].RPCClient + require.NotNil(t, client) + + ticker := time.NewTicker(5 * time.Second) + timeout := time.After(time.Minute) + + for { + select { + case <-ticker.C: + s, _ := client.Status() + if s != nil && s.SyncInfo.LatestBlockHeight >= 10 { + t.Logf("successfully process %d blocks", s.SyncInfo.LatestBlockHeight) + return + } + case <-timeout: + t.Fatal("timeout exceeded waiting for enough committed blocks") + } + } +} diff --git a/testutil/util.go b/testutil/util.go new file mode 100644 index 000000000000..caedc2198a96 --- /dev/null +++ b/testutil/util.go @@ -0,0 +1,188 @@ +package testutil + +import ( + "path/filepath" + "time" + + tmos "github.com/tendermint/tendermint/libs/os" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/p2p" + pvm "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/rpc/client/local" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/simapp" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +func startInProcess(cfg Config, val *Validator) error { + logger := val.Ctx.Logger + tmCfg := val.Ctx.Config + tmCfg.Instrumentation.Prometheus = false + + db := dbm.NewMemDB() + genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) + + app := simapp.NewSimApp( + logger, db, nil, true, make(map[int64]bool), + tmCfg.RootDir, 0, + baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), + baseapp.SetMinGasPrices(cfg.MinGasPrices), + ) + + nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) + if err != nil { + return err + } + + tmNode, err := node.NewNode( + tmCfg, + pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + genDocProvider, + node.DefaultDBProvider, + node.DefaultMetricsProvider(tmCfg.Instrumentation), + logger.With("module", val.Moniker), + ) + if err != nil { + return err + } + + if err := tmNode.Start(); err != nil { + return err + } + + rpcClient := local.New(tmNode) + + val.ClientCtx = client.Context{}. + WithHomeDir(tmCfg.RootDir). + WithChainID(cfg.ChainID). + WithJSONMarshaler(cdc). + WithClient(rpcClient). + WithTrustNode(true) + + apiSrv := api.New(val.ClientCtx) + app.RegisterAPIRoutes(apiSrv) + + errCh := make(chan error) + + go func() { + if err := apiSrv.Start(*val.AppConfig); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(5 * time.Second): // assume server started successfully + } + + val.tmNode = tmNode + val.api = apiSrv + val.RPCClient = rpcClient + + return nil +} + +func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { + genTime := tmtime.Now() + + for i := 0; i < cfg.NumValidators; i++ { + tmCfg := vals[i].Ctx.Config + + nodeDir := filepath.Join(outputDir, vals[i].Moniker, "simd") + gentxsDir := filepath.Join(outputDir, "gentxs") + + tmCfg.Moniker = vals[i].Moniker + tmCfg.SetRoot(nodeDir) + + initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].Moniker, vals[i].NodeID, vals[i].PubKey) + + genFile := tmCfg.GenesisFile() + genDoc, err := types.GenesisDocFromFile(genFile) + if err != nil { + return err + } + + appState, err := genutil.GenAppStateFromConfig(cdc, tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}) + if err != nil { + return err + } + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func initGenFiles( + cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string, +) error { + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + cdc.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState) + + authGenState.Accounts = genAccounts + cfg.GenesisState[authtypes.ModuleName] = cdc.MustMarshalJSON(authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + cdc.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = genBalances + cfg.GenesisState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankGenState) + + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, cfg.GenesisState) + if err != nil { + return err + } + + genDoc := types.GenesisDoc{ + ChainID: cfg.ChainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < cfg.NumValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + + return nil +} + +func writeFile(name string, dir string, contents []byte) error { + writePath := filepath.Join(dir) + file := filepath.Join(writePath, name) + + err := tmos.EnsureDir(writePath, 0755) + if err != nil { + return err + } + + err = tmos.WriteFile(file, contents, 0644) + if err != nil { + return err + } + + return nil +} From 1e651e8682feb84d01284d2cc7b2dccfb9e128dc Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 10:16:33 -0400 Subject: [PATCH 02/29] Remove script --- .github/workflows/test.yml | 24 ---------------- contrib/localnet_liveness.sh | 55 ------------------------------------ 2 files changed, 79 deletions(-) delete mode 100755 contrib/localnet_liveness.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2c819617e763..85040ee3ddf4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -213,27 +213,3 @@ jobs: run: | make test-integration if: "env.GIT_DIFF != ''" - - liveness-test: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v1 - id: git_diff - with: - SUFFIX_FILTER: | - .go - .mod - .sum - - name: build image - run: | - make build-docker-local-simapp - - name: start localnet - run: | - make clean build-sim-linux localnet-start - if: "env.GIT_DIFF != ''" - - name: test liveness - run: | - ./contrib/localnet_liveness.sh 100 5 50 localhost - if: "env.GIT_DIFF != ''" diff --git a/contrib/localnet_liveness.sh b/contrib/localnet_liveness.sh deleted file mode 100755 index 1dac38fa72a8..000000000000 --- a/contrib/localnet_liveness.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -CNT=0 -ITER=$1 -SLEEP=$2 -NUMBLOCKS=$3 -NODEADDR=$4 - -if [ -z "$1" ]; then - echo "Need to input number of iterations to run..." - exit 1 -fi - -if [ -z "$2" ]; then - echo "Need to input number of seconds to sleep between iterations" - exit 1 -fi - -if [ -z "$3" ]; then - echo "Need to input block height to declare completion..." - exit 1 -fi - -if [ -z "$4" ]; then - echo "Need to input node address to poll..." - exit 1 -fi - -docker_containers=( $(docker ps -q -f name=simdnode --format='{{.Names}}') ) - -while [ ${CNT} -lt $ITER ]; do - curr_block=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height') - - if [ ! -z ${curr_block} ] ; then - echo "Number of Blocks: ${curr_block}" - fi - - if [ ! -z ${curr_block} ] && [ ${curr_block} -gt ${NUMBLOCKS} ]; then - echo "Number of blocks reached. Success!" - exit 0 - fi - - # Emulate network chaos: - # - # Every 10 blocks, pick a random container and restart it. - if ! ((${CNT} % 10)); then - rand_container=${docker_containers["$[RANDOM % ${#docker_containers[@]}]"]}; - echo "Restarting random docker container ${rand_container}" - docker restart ${rand_container} &>/dev/null & - fi - let CNT=CNT+1 - sleep $SLEEP -done -echo "Timeout reached. Failure!" -exit 1 From cd2ec2da3a963fe8f7c9d3f757eea20f59fca8bd Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 12:16:35 -0400 Subject: [PATCH 03/29] Fix IBC codec bug --- x/ibc/02-client/alias.go | 1 - x/ibc/02-client/types/codec.go | 11 +++++------ x/ibc/07-tendermint/types/codec.go | 12 +++++------- x/ibc/09-localhost/types/codec.go | 11 +++++------ 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index 2536d6601182..885b0ab701e8 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -24,7 +24,6 @@ const ( var ( // functions aliases RegisterCodec = types.RegisterCodec - SetSubModuleCodec = types.SetSubModuleCodec NewClientConsensusStates = types.NewClientConsensusStates NewGenesisState = types.NewGenesisState DefaultGenesisState = types.DefaultGenesisState diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index b8c0eed6ee61..e4fe3cec68e1 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -8,6 +8,11 @@ import ( // SubModuleCdc defines the IBC client codec. var SubModuleCdc *codec.Codec +func init() { + SubModuleCdc = codec.New() + RegisterCodec(SubModuleCdc) +} + // RegisterCodec registers the IBC client interfaces and types func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.ClientState)(nil), nil) @@ -16,10 +21,4 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.ConsensusState)(nil), nil) cdc.RegisterInterface((*exported.Header)(nil), nil) cdc.RegisterInterface((*exported.Misbehaviour)(nil), nil) - - SetSubModuleCodec(cdc) -} - -func SetSubModuleCodec(cdc *codec.Codec) { - SubModuleCdc = cdc } diff --git a/x/ibc/07-tendermint/types/codec.go b/x/ibc/07-tendermint/types/codec.go index 2ac179d62c63..7e18d1594a44 100644 --- a/x/ibc/07-tendermint/types/codec.go +++ b/x/ibc/07-tendermint/types/codec.go @@ -7,6 +7,11 @@ import ( // SubModuleCdc defines the IBC tendermint client codec. var SubModuleCdc *codec.Codec +func init() { + SubModuleCdc = codec.New() + RegisterCodec(SubModuleCdc) +} + // RegisterCodec registers the Tendermint types func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(ClientState{}, "ibc/client/tendermint/ClientState", nil) @@ -16,11 +21,4 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(&MsgCreateClient{}, "ibc/client/tendermint/MsgCreateClient", nil) cdc.RegisterConcrete(&MsgUpdateClient{}, "ibc/client/tendermint/MsgUpdateClient", nil) cdc.RegisterConcrete(&MsgSubmitClientMisbehaviour{}, "ibc/client/tendermint/MsgSubmitClientMisbehaviour", nil) - - SetSubModuleCodec(cdc) -} - -// SetSubModuleCodec sets the ibc tendermint client codec -func SetSubModuleCodec(cdc *codec.Codec) { - SubModuleCdc = cdc } diff --git a/x/ibc/09-localhost/types/codec.go b/x/ibc/09-localhost/types/codec.go index a0dff8718cfd..bbe58cffce6f 100644 --- a/x/ibc/09-localhost/types/codec.go +++ b/x/ibc/09-localhost/types/codec.go @@ -12,14 +12,13 @@ const ( // SubModuleCdc defines the IBC localhost client codec. var SubModuleCdc *codec.Codec +func init() { + SubModuleCdc = codec.New() + RegisterCodec(SubModuleCdc) +} + // RegisterCodec registers the localhost types func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(ClientState{}, "ibc/client/localhost/ClientState", nil) cdc.RegisterConcrete(&MsgCreateClient{}, "ibc/client/localhost/MsgCreateClient", nil) - SetSubModuleCodec(cdc) -} - -// SetSubModuleCodec sets the ibc localhost client codec -func SetSubModuleCodec(cdc *codec.Codec) { - SubModuleCdc = cdc } From ecfa3c81de504cfca1faff8e58fc0a78b40fccac Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 12:30:28 -0400 Subject: [PATCH 04/29] go mod tidy + codec fixes attempt --- go.mod | 2 +- go.sum | 4 ++-- x/ibc/02-client/types/codec.go | 2 ++ x/ibc/07-tendermint/types/codec.go | 2 ++ x/ibc/09-localhost/types/codec.go | 2 ++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9486476840e6..c61e28b91ec6 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/tendermint/btcd v0.1.1 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 github.com/tendermint/go-amino v0.15.1 - github.com/tendermint/iavl v0.13.4-0.20200621145059-83c3470ad61d + github.com/tendermint/iavl v0.13.4-0.20200622140716-a41a83e2415f github.com/tendermint/tendermint v0.33.5 github.com/tendermint/tm-db v0.5.1 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e diff --git a/go.sum b/go.sum index b115857f45cd..adb8ec77e734 100644 --- a/go.sum +++ b/go.sum @@ -510,8 +510,8 @@ github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYM github.com/tendermint/go-amino v0.15.1 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ= github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1TuoljBcsQoGA= -github.com/tendermint/iavl v0.13.4-0.20200621145059-83c3470ad61d h1:CeGkAbISdVP2LtmxWUv69KAzvrFjndehTtKTCd/kn9E= -github.com/tendermint/iavl v0.13.4-0.20200621145059-83c3470ad61d/go.mod h1:ZftLrZ/r+rapraBmqypoFAiw8YbvTglawkmZu2grdkg= +github.com/tendermint/iavl v0.13.4-0.20200622140716-a41a83e2415f h1:p8pR6oVbImPMiqy9jgHwBaf7keQ7LYYXbN0xgY3wy7s= +github.com/tendermint/iavl v0.13.4-0.20200622140716-a41a83e2415f/go.mod h1:ZftLrZ/r+rapraBmqypoFAiw8YbvTglawkmZu2grdkg= github.com/tendermint/tendermint v0.33.2 h1:NzvRMTuXJxqSsFed2J7uHmMU5N1CVzSpfi3nCc882KY= github.com/tendermint/tendermint v0.33.2/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk= github.com/tendermint/tendermint v0.33.5 h1:jYgRd9ImkzA9iOyhpmgreYsqSB6tpDa6/rXYPb8HKE8= diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index e4fe3cec68e1..792a05a8dec5 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -2,6 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" ) @@ -10,6 +11,7 @@ var SubModuleCdc *codec.Codec func init() { SubModuleCdc = codec.New() + cryptocodec.RegisterCrypto(SubModuleCdc) RegisterCodec(SubModuleCdc) } diff --git a/x/ibc/07-tendermint/types/codec.go b/x/ibc/07-tendermint/types/codec.go index 7e18d1594a44..b4e0c4325a21 100644 --- a/x/ibc/07-tendermint/types/codec.go +++ b/x/ibc/07-tendermint/types/codec.go @@ -2,6 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" ) // SubModuleCdc defines the IBC tendermint client codec. @@ -9,6 +10,7 @@ var SubModuleCdc *codec.Codec func init() { SubModuleCdc = codec.New() + cryptocodec.RegisterCrypto(SubModuleCdc) RegisterCodec(SubModuleCdc) } diff --git a/x/ibc/09-localhost/types/codec.go b/x/ibc/09-localhost/types/codec.go index bbe58cffce6f..b6ccf0ebb4cb 100644 --- a/x/ibc/09-localhost/types/codec.go +++ b/x/ibc/09-localhost/types/codec.go @@ -2,6 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" ) const ( @@ -14,6 +15,7 @@ var SubModuleCdc *codec.Codec func init() { SubModuleCdc = codec.New() + cryptocodec.RegisterCrypto(SubModuleCdc) RegisterCodec(SubModuleCdc) } From c7e9b60aeda5595d5127682ebb6e1351971f973c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 12:51:35 -0400 Subject: [PATCH 05/29] Change base dir to use chain-id --- testutil/network.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testutil/network.go b/testutil/network.go index 6db01f781fc0..e1a3a1216986 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/json" "fmt" + "io/ioutil" "net/url" "os" "path/filepath" @@ -106,9 +107,12 @@ type ( ) func NewTestNetwork(t *testing.T, cfg Config) *Network { + baseDir, err := ioutil.TempDir(os.TempDir(), cfg.ChainID) + require.NoError(t, err) + network := &Network{ T: t, - BaseDir: os.TempDir(), + BaseDir: baseDir, Validators: make([]*Validator, cfg.NumValidators), } From f5deb8dcbe62061da0fa0de6cd12f81a3a908f91 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 14:18:59 -0400 Subject: [PATCH 06/29] Add trap signal call --- testutil/network.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testutil/network.go b/testutil/network.go index e1a3a1216986..dd2ddc59668a 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -262,7 +262,9 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { require.NoError(t, eg.Wait()) t.Log("started test network") - // TODO: Do we need to trap signal + // Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any + // defer in a test would not be called. + server.TrapSignal(network.Cleanup) return network } From 7f51e12973f2179661157faa95e2aed22f1d0493 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 14:21:33 -0400 Subject: [PATCH 07/29] Add godoc --- testutil/network.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testutil/network.go b/testutil/network.go index dd2ddc59668a..31e15129930f 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -269,6 +269,8 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { return network } +// Cleanup removes the root testing (temporary) directory and stops both the +// Tendermint and API services. func (n *Network) Cleanup() { n.T.Log("cleaning up test network...") From ec88212a96d17f69cac2dda8c2e8d80da9b026b8 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 15:29:38 -0400 Subject: [PATCH 08/29] Updates --- server/api/server.go | 12 +++-- server/start.go | 4 +- testutil/network.go | 90 +++++++++++++++++++++++--------- testutil/network_test.go | 7 ++- testutil/util.go | 2 +- types/rest/rest.go | 11 ++++ x/bank/client/rest/query_test.go | 90 ++++++++++++++++++++++++++++++++ 7 files changed, 179 insertions(+), 37 deletions(-) create mode 100644 x/bank/client/rest/query_test.go diff --git a/server/api/server.go b/server/api/server.go index 588614f2f64b..eb884425adbd 100644 --- a/server/api/server.go +++ b/server/api/server.go @@ -4,7 +4,6 @@ import ( "fmt" "net" "net/http" - "os" "strings" "time" @@ -33,11 +32,11 @@ type Server struct { listener net.Listener } -func New(clientCtx client.Context) *Server { +func New(clientCtx client.Context, logger log.Logger) *Server { return &Server{ Router: mux.NewRouter(), ClientCtx: clientCtx, - logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "api-server"), + logger: logger, } } @@ -60,13 +59,18 @@ func (s *Server) Start(cfg config.Config) error { s.registerMetrics() } + addr := cfg.API.Address + if !strings.Contains(addr, "tcp://") { + addr = "tcp://" + addr + } + tmCfg := tmrpcserver.DefaultConfig() tmCfg.MaxOpenConnections = int(cfg.API.MaxOpenConnections) tmCfg.ReadTimeout = time.Duration(cfg.API.RPCReadTimeout) * time.Second tmCfg.WriteTimeout = time.Duration(cfg.API.RPCWriteTimeout) * time.Second tmCfg.MaxBodyBytes = int64(cfg.API.RPCMaxBodyBytes) - listener, err := tmrpcserver.Listen(cfg.API.Address, tmCfg) + listener, err := tmrpcserver.Listen(addr, tmCfg) if err != nil { return err } diff --git a/server/start.go b/server/start.go index 1f0b26dc8e31..a0f6d263abdd 100644 --- a/server/start.go +++ b/server/start.go @@ -212,14 +212,14 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator // TODO: Since this is running in process, do we need to provide a verifier // and set TrustNode=false? If so, we need to add additional logic that // waits for a block to be committed first before starting the API server. - ctx := client.Context{}. + clientCtx := client.Context{}. WithHomeDir(home). WithChainID(genDoc.ChainID). WithJSONMarshaler(cdc). WithClient(local.New(tmNode)). WithTrustNode(true) - apiSrv = api.New(ctx) + apiSrv = api.New(clientCtx, ctx.Logger.With("module", "api-server")) app.RegisterAPIRoutes(apiSrv) errCh := make(chan error) diff --git a/testutil/network.go b/testutil/network.go index 31e15129930f..7f41fd282e39 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -3,6 +3,7 @@ package testutil import ( "bufio" "encoding/json" + "errors" "fmt" "io/ioutil" "net/url" @@ -43,33 +44,35 @@ var ( // Config defines the necessary configuration used to bootstrap and start an // in-process local testing network. type Config struct { - GenesisState map[string]json.RawMessage - TimeoutCommit time.Duration - ChainID string - NumValidators int - BondDenom string - MinGasPrices string - Passphrase string - AccountTokens sdk.Int - StakingTokens sdk.Int - BondedTokens sdk.Int - EnableLogging bool + GenesisState map[string]json.RawMessage + TimeoutCommit time.Duration + ChainID string + NumValidators int + BondDenom string + MinGasPrices string + Passphrase string + AccountTokens sdk.Int + StakingTokens sdk.Int + BondedTokens sdk.Int + PruningStrategy string + EnableLogging bool } // DefaultConfig returns a sane default configuration suitable for nearly all // testing requirements. func DefaultConfig() Config { return Config{ - GenesisState: simapp.ModuleBasics.DefaultGenesis(cdc), - TimeoutCommit: 2 * time.Second, - ChainID: "chain-" + tmrand.NewRand().Str(6), - NumValidators: 4, - BondDenom: sdk.DefaultBondDenom, - MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), - Passphrase: clientkeys.DefaultKeyPass, - AccountTokens: sdk.TokensFromConsensusPower(1000), - StakingTokens: sdk.TokensFromConsensusPower(500), - BondedTokens: sdk.TokensFromConsensusPower(100), + GenesisState: simapp.ModuleBasics.DefaultGenesis(cdc), + TimeoutCommit: 2 * time.Second, + ChainID: "chain-" + tmrand.NewRand().Str(6), + NumValidators: 4, + BondDenom: sdk.DefaultBondDenom, + MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), + Passphrase: clientkeys.DefaultKeyPass, + AccountTokens: sdk.TokensFromConsensusPower(1000), + StakingTokens: sdk.TokensFromConsensusPower(500), + BondedTokens: sdk.TokensFromConsensusPower(100), + PruningStrategy: storetypes.PruningOptionNothing, } } @@ -133,7 +136,7 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { // generate private keys, node IDs, and initial transactions for i := 0; i < cfg.NumValidators; i++ { appCfg := srvconfig.DefaultConfig() - appCfg.Pruning = storetypes.PruningOptionNothing + appCfg.Pruning = cfg.PruningStrategy appCfg.MinGasPrices = cfg.MinGasPrices appCfg.API.Enable = true appCfg.API.Swagger = false @@ -141,7 +144,10 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { apiAddr, _, err := server.FreeTCPAddr() require.NoError(t, err) - appCfg.API.Address = apiAddr + + apiURL, err := url.Parse(apiAddr) + require.NoError(t, err) + appCfg.API.Address = fmt.Sprintf("%s:%s", apiURL.Hostname(), apiURL.Port()) ctx := server.NewDefaultContext() tmCfg := ctx.Config @@ -199,10 +205,10 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { // save private key seed words require.NoError(t, writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz)) - balances := sdk.Coins{ + balances := sdk.NewCoins( sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), cfg.AccountTokens), sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens), - } + ) genFiles = append(genFiles, tmCfg.GenesisFile()) genBalances = append(genBalances, banktypes.Balance{Address: addr, Coins: balances.Sort()}) @@ -222,7 +228,10 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) tx := authtypes.NewStdTx([]sdk.Msg{createValMsg}, authtypes.StdFee{}, []authtypes.StdSignature{}, memo) //nolint:staticcheck // SA1019: authtypes.StdFee is deprecated - txBldr := authtypes.TxBuilder{}.WithChainID(cfg.ChainID).WithMemo(memo).WithKeybase(kb) + txBldr := authtypes.TxBuilder{}. + WithChainID(cfg.ChainID). + WithMemo(memo). + WithKeybase(kb) signedTx, err := txBldr.SignStdTx(nodeDirName, tx, false) require.NoError(t, err) @@ -269,6 +278,35 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { return network } +// WaitForHeight performs a blocking check where it waits for a block to be +// committed after a given block. If that height is not reached within a timeout, +// an error is returned. +func (n *Network) WaitForHeight(h int64) error { + ticker := time.NewTicker(time.Second) + timeout := time.After(10 * time.Second) + + if len(n.Validators) == 0 { + return errors.New("no validators available") + } + + val := n.Validators[0] + + for { + select { + case <-timeout: + ticker.Stop() + return errors.New("timeout exceeded waiting for block") + case <-ticker.C: + status, err := val.RPCClient.Status() + if err == nil && status != nil { + if status.SyncInfo.LatestBlockHeight > h { + return nil + } + } + } + } +} + // Cleanup removes the root testing (temporary) directory and stops both the // Tendermint and API services. func (n *Network) Cleanup() { diff --git a/testutil/network_test.go b/testutil/network_test.go index a1a69bcf1512..e64f8f3312e5 100644 --- a/testutil/network_test.go +++ b/testutil/network_test.go @@ -10,12 +10,11 @@ import ( func TestNetwork_Liveness(t *testing.T) { n := NewTestNetwork(t, DefaultConfig()) defer n.Cleanup() - require.NotNil(t, n) - require.NotEmpty(t, n.Validators) - client := n.Validators[0].RPCClient - require.NotNil(t, client) + require.NoError(t, n.WaitForHeight(1)) + + client := n.Validators[0].RPCClient ticker := time.NewTicker(5 * time.Second) timeout := time.After(time.Minute) diff --git a/testutil/util.go b/testutil/util.go index caedc2198a96..0030dbe81372 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -73,7 +73,7 @@ func startInProcess(cfg Config, val *Validator) error { WithClient(rpcClient). WithTrustNode(true) - apiSrv := api.New(val.ClientCtx) + apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) app.RegisterAPIRoutes(apiSrv) errCh := make(chan error) diff --git a/types/rest/rest.go b/types/rest/rest.go index bee3bbbeaa55..ef7b9b6c7c9d 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -42,6 +42,17 @@ func NewResponseWithHeight(height int64, result json.RawMessage) ResponseWithHei } } +// ParseResponseWithHeight returns the raw result from a JSON-encoded +// ResponseWithHeight object. +func ParseResponseWithHeight(cdc codec.JSONMarshaler, bz []byte) ([]byte, error) { + r := ResponseWithHeight{} + if err := cdc.UnmarshalJSON(bz, &r); err != nil { + return nil, err + } + + return r.Result, nil +} + // GasEstimateResponse defines a response definition for tx gas estimation. type GasEstimateResponse struct { GasEstimate uint64 `json:"gas_estimate"` diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go new file mode 100644 index 000000000000..feb380f251a3 --- /dev/null +++ b/x/bank/client/rest/query_test.go @@ -0,0 +1,90 @@ +package rest_test + +import ( + "fmt" + "io/ioutil" + "net/http" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" +) + +type IntegrationTestSuite struct { + suite.Suite + cfg testutil.Config + network *testutil.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := testutil.DefaultConfig() + cfg.NumValidators = 1 + + s.cfg = cfg + s.network = testutil.NewTestNetwork(s.T(), cfg) + s.Require().NoError(s.network.WaitForHeight(1)) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestSendRequestHandler() { + val := s.network.Validators[0] + apiAddr := val.AppConfig.API.Address + + url := fmt.Sprintf("http://%s/bank/balances/%s?height=1", apiAddr, val.Address) + resp, err := getRequest(url) + s.Require().NoError(err) + + bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp) + s.Require().NoError(err) + + var balances sdk.Coins + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, &balances)) + + expected := sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + ) + s.Require().Equal(expected.String(), balances.String()) + + url = fmt.Sprintf("http://%s/bank/balances/%s?height=1&denom=%s", apiAddr, val.Address, s.cfg.BondDenom) + resp, err = getRequest(url) + s.Require().NoError(err) + + bz, err = rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp) + s.Require().NoError(err) + + var balance sdk.Coin + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, &balance)) + s.Require().Equal(expected[1].String(), balance.String()) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func getRequest(url string) ([]byte, error) { + res, err := http.Get(url) // nolint:gosec + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + if err = res.Body.Close(); err != nil { + return nil, err + } + + return body, nil +} From 4f43424767dfdb1d6032dabc9d615a4f19044de3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 15:37:00 -0400 Subject: [PATCH 09/29] Tweaks --- testutil/network.go | 22 +++++++++++++++------- testutil/network_test.go | 20 ++------------------ x/bank/client/rest/query_test.go | 4 +++- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index 7f41fd282e39..64bb198ab5c9 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -280,27 +280,35 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { // WaitForHeight performs a blocking check where it waits for a block to be // committed after a given block. If that height is not reached within a timeout, -// an error is returned. -func (n *Network) WaitForHeight(h int64) error { +// an error is returned. Regardless, the latest height queried is returned. +func (n *Network) WaitForHeight(h int64) (int64, error) { + return n.WaitForHeightWithTimeout(h, 10*time.Second) +} + +// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can +// provide a custom timeout. +func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { ticker := time.NewTicker(time.Second) - timeout := time.After(10 * time.Second) + timeout := time.After(t) if len(n.Validators) == 0 { - return errors.New("no validators available") + return 0, errors.New("no validators available") } + var latestHeight int64 val := n.Validators[0] for { select { case <-timeout: ticker.Stop() - return errors.New("timeout exceeded waiting for block") + return latestHeight, errors.New("timeout exceeded waiting for block") case <-ticker.C: status, err := val.RPCClient.Status() if err == nil && status != nil { - if status.SyncInfo.LatestBlockHeight > h { - return nil + latestHeight = status.SyncInfo.LatestBlockHeight + if latestHeight >= h { + return latestHeight, nil } } } diff --git a/testutil/network_test.go b/testutil/network_test.go index e64f8f3312e5..019c848364a9 100644 --- a/testutil/network_test.go +++ b/testutil/network_test.go @@ -12,22 +12,6 @@ func TestNetwork_Liveness(t *testing.T) { defer n.Cleanup() require.NotNil(t, n) - require.NoError(t, n.WaitForHeight(1)) - - client := n.Validators[0].RPCClient - ticker := time.NewTicker(5 * time.Second) - timeout := time.After(time.Minute) - - for { - select { - case <-ticker.C: - s, _ := client.Status() - if s != nil && s.SyncInfo.LatestBlockHeight >= 10 { - t.Logf("successfully process %d blocks", s.SyncInfo.LatestBlockHeight) - return - } - case <-timeout: - t.Fatal("timeout exceeded waiting for enough committed blocks") - } - } + h, err := n.WaitForHeightWithTimeout(10, time.Minute) + require.NoError(t, err, "expected to reach 10 blocks; got %d", h) } diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index feb380f251a3..ea31e6c0f8e8 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -27,7 +27,9 @@ func (s *IntegrationTestSuite) SetupSuite() { s.cfg = cfg s.network = testutil.NewTestNetwork(s.T(), cfg) - s.Require().NoError(s.network.WaitForHeight(1)) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) } func (s *IntegrationTestSuite) TearDownSuite() { From e8f5176ef0965f8525733da0cdd43cf8dd7a3e6a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 15:38:05 -0400 Subject: [PATCH 10/29] Fix fn name --- x/bank/client/rest/query_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index ea31e6c0f8e8..c3c82a6781a4 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -37,7 +37,7 @@ func (s *IntegrationTestSuite) TearDownSuite() { s.network.Cleanup() } -func (s *IntegrationTestSuite) TestSendRequestHandler() { +func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() { val := s.network.Validators[0] apiAddr := val.AppConfig.API.Address From 433b67eb6b0d2b5e437cb363e5d00f231e1e485e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 17:30:57 -0400 Subject: [PATCH 11/29] Cleanup --- server/api/server.go | 7 +- testutil/network.go | 4 +- types/rest/rest.go | 20 +++++ x/bank/client/rest/query_test.go | 131 ++++++++++++++++++++----------- x/bank/keeper/querier.go | 7 +- x/bank/keeper/querier_test.go | 4 +- 6 files changed, 116 insertions(+), 57 deletions(-) diff --git a/server/api/server.go b/server/api/server.go index eb884425adbd..b54f5ad68c8c 100644 --- a/server/api/server.go +++ b/server/api/server.go @@ -59,18 +59,13 @@ func (s *Server) Start(cfg config.Config) error { s.registerMetrics() } - addr := cfg.API.Address - if !strings.Contains(addr, "tcp://") { - addr = "tcp://" + addr - } - tmCfg := tmrpcserver.DefaultConfig() tmCfg.MaxOpenConnections = int(cfg.API.MaxOpenConnections) tmCfg.ReadTimeout = time.Duration(cfg.API.RPCReadTimeout) * time.Second tmCfg.WriteTimeout = time.Duration(cfg.API.RPCWriteTimeout) * time.Second tmCfg.MaxBodyBytes = int64(cfg.API.RPCMaxBodyBytes) - listener, err := tmrpcserver.Listen(addr, tmCfg) + listener, err := tmrpcserver.Listen(cfg.API.Address, tmCfg) if err != nil { return err } diff --git a/testutil/network.go b/testutil/network.go index 64bb198ab5c9..f1ee42517f0d 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -98,6 +98,7 @@ type ( NodeID string PubKey crypto.PubKey Moniker string + APIAddress string RPCAddress string P2PAddress string Address sdk.AccAddress @@ -144,10 +145,10 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { apiAddr, _, err := server.FreeTCPAddr() require.NoError(t, err) + appCfg.API.Address = apiAddr apiURL, err := url.Parse(apiAddr) require.NoError(t, err) - appCfg.API.Address = fmt.Sprintf("%s:%s", apiURL.Hostname(), apiURL.Port()) ctx := server.NewDefaultContext() tmCfg := ctx.Config @@ -251,6 +252,7 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { Moniker: nodeDirName, RPCAddress: rpcAddr, P2PAddress: p2pAddr, + APIAddress: fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()), Address: addr, ValAddress: sdk.ValAddress(addr), } diff --git a/types/rest/rest.go b/types/rest/rest.go index ef7b9b6c7c9d..3825c95049e2 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -436,3 +436,23 @@ func ParseQueryParamBool(r *http.Request, param string) bool { return false } + +// GetRequest defines a wrapper around an HTTP GET request with a provided URL. +// An error is returned if the request or reading the body fails. +func GetRequest(url string) ([]byte, error) { + res, err := http.Get(url) // nolint:gosec + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + if err = res.Body.Close(); err != nil { + return nil, err + } + + return body, nil +} diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index c3c82a6781a4..5adb00c7bb5a 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -2,8 +2,6 @@ package rest_test import ( "fmt" - "io/ioutil" - "net/http" "testing" "github.com/stretchr/testify/suite" @@ -39,54 +37,97 @@ func (s *IntegrationTestSuite) TearDownSuite() { func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() { val := s.network.Validators[0] - apiAddr := val.AppConfig.API.Address - - url := fmt.Sprintf("http://%s/bank/balances/%s?height=1", apiAddr, val.Address) - resp, err := getRequest(url) - s.Require().NoError(err) - - bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp) - s.Require().NoError(err) - - var balances sdk.Coins - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, &balances)) - - expected := sdk.NewCoins( - sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), - sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), - ) - s.Require().Equal(expected.String(), balances.String()) - - url = fmt.Sprintf("http://%s/bank/balances/%s?height=1&denom=%s", apiAddr, val.Address, s.cfg.BondDenom) - resp, err = getRequest(url) - s.Require().NoError(err) - - bz, err = rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp) - s.Require().NoError(err) - - var balance sdk.Coin - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, &balance)) - s.Require().Equal(expected[1].String(), balance.String()) -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + respType fmt.Stringer + expected fmt.Stringer + }{ + { + "total account balance", + fmt.Sprintf("%s/bank/balances/%s?height=1", baseURL, val.Address), + &sdk.Coins{}, + sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + ), + }, + { + "total account balance of a specific denom", + fmt.Sprintf("%s/bank/balances/%s?height=1&denom=%s", baseURL, val.Address, s.cfg.BondDenom), + &sdk.Coin{}, + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + }, + { + "total account balance of a bogus denom", + fmt.Sprintf("%s/bank/balances/%s?height=1&denom=foobar", baseURL, val.Address), + &sdk.Coin{}, + sdk.NewCoin("foobar", sdk.ZeroInt()), + }, + } -func getRequest(url string) ([]byte, error) { - res, err := http.Get(url) // nolint:gosec - if err != nil { - return nil, err + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) } +} - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, err +func (s *IntegrationTestSuite) TestTotalSupplyHandlerFn() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + respType fmt.Stringer + expected fmt.Stringer + }{ + { + "total supply", + fmt.Sprintf("%s/bank/total?height=1", baseURL), + &sdk.Coins{}, sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))), + ), + }, + { + "total supply of a specific denom", + fmt.Sprintf("%s/bank/total/%s?height=1", baseURL, s.cfg.BondDenom), + &sdk.Coin{}, + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(10))), + }, + { + "total supply of a bogus denom", + fmt.Sprintf("%s/bank/total/foobar?height=1", baseURL), + &sdk.Coin{}, + sdk.NewCoin("foobar", sdk.ZeroInt()), + }, } - if err = res.Body.Close(); err != nil { - return nil, err + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + bz, err := rest.ParseResponseWithHeight(val.ClientCtx.JSONMarshaler, resp) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(bz, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) } +} - return body, nil +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) } diff --git a/x/bank/keeper/querier.go b/x/bank/keeper/querier.go index c73db3935675..4d5f838df09b 100644 --- a/x/bank/keeper/querier.go +++ b/x/bank/keeper/querier.go @@ -99,12 +99,13 @@ func querySupplyOf(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, er return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - supply := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom) + amount := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom) + supply := sdk.NewCoin(params.Denom, amount) - res, err := supply.MarshalJSON() + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, supply) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } - return res, nil + return bz, nil } diff --git a/x/bank/keeper/querier_test.go b/x/bank/keeper/querier_test.go index 5fb7122c6205..1c52079cad82 100644 --- a/x/bank/keeper/querier_test.go +++ b/x/bank/keeper/querier_test.go @@ -133,9 +133,9 @@ func (suite *IntegrationTestSuite) TestQuerier_QueryTotalSupplyOf() { suite.Require().NoError(err) suite.Require().NotNil(res) - var resp sdk.Int + var resp sdk.Coin suite.Require().NoError(app.Codec().UnmarshalJSON(res, &resp)) - suite.Require().Equal(test1Supply.Amount, resp) + suite.Require().Equal(test1Supply, resp) } func (suite *IntegrationTestSuite) TestQuerierRouteNotFound() { From f7758f71ac68cfc6991abc7c29f73b1c67f25fef Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 23 Jun 2020 22:54:02 -0400 Subject: [PATCH 12/29] Only allow 1st val to serve rpc/api --- testutil/network.go | 35 ++++++++++++++++++++------------- testutil/util.go | 48 ++++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index f1ee42517f0d..d2536c2840f6 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -143,17 +143,28 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { appCfg.API.Swagger = false appCfg.Telemetry.Enabled = false - apiAddr, _, err := server.FreeTCPAddr() - require.NoError(t, err) - appCfg.API.Address = apiAddr - - apiURL, err := url.Parse(apiAddr) - require.NoError(t, err) - ctx := server.NewDefaultContext() tmCfg := ctx.Config tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit + // Only allow the first validator to expose an RPC and API server/client + // due to Tendermint in-process constraints. + apiAddr := "" + tmCfg.RPC.ListenAddress = "" + if i == 0 { + apiListenAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + appCfg.API.Address = apiListenAddr + + apiURL, err := url.Parse(apiListenAddr) + require.NoError(t, err) + apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) + + rpcAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.RPC.ListenAddress = rpcAddr + } + logger := log.NewNopLogger() if cfg.EnableLogging { logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) @@ -178,10 +189,6 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { require.NoError(t, err) tmCfg.ProxyApp = proxyAddr - rpcAddr, _, err := server.FreeTCPAddr() - require.NoError(t, err) - tmCfg.RPC.ListenAddress = rpcAddr - p2pAddr, _, err := server.FreeTCPAddr() require.NoError(t, err) tmCfg.P2P.ListenAddress = p2pAddr @@ -250,9 +257,9 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { NodeID: nodeID, PubKey: pubKey, Moniker: nodeDirName, - RPCAddress: rpcAddr, - P2PAddress: p2pAddr, - APIAddress: fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()), + RPCAddress: tmCfg.RPC.ListenAddress, + P2PAddress: tmCfg.P2P.ListenAddress, + APIAddress: apiAddr, Address: addr, ValAddress: sdk.ValAddress(addr), } diff --git a/testutil/util.go b/testutil/util.go index 0030dbe81372..646a274253df 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -64,36 +64,40 @@ func startInProcess(cfg Config, val *Validator) error { return err } - rpcClient := local.New(tmNode) + val.tmNode = tmNode + + if val.RPCAddress != "" { + val.RPCClient = local.New(tmNode) + } + + if val.APIAddress != "" { + val.ClientCtx = client.Context{}. + WithHomeDir(tmCfg.RootDir). + WithChainID(cfg.ChainID). + WithJSONMarshaler(cdc). + WithClient(val.RPCClient). + WithTrustNode(true) - val.ClientCtx = client.Context{}. - WithHomeDir(tmCfg.RootDir). - WithChainID(cfg.ChainID). - WithJSONMarshaler(cdc). - WithClient(rpcClient). - WithTrustNode(true) + apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) + app.RegisterAPIRoutes(apiSrv) - apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) - app.RegisterAPIRoutes(apiSrv) + errCh := make(chan error) - errCh := make(chan error) + go func() { + if err := apiSrv.Start(*val.AppConfig); err != nil { + errCh <- err + } + }() - go func() { - if err := apiSrv.Start(*val.AppConfig); err != nil { - errCh <- err + select { + case err := <-errCh: + return err + case <-time.After(5 * time.Second): // assume server started successfully } - }() - select { - case err := <-errCh: - return err - case <-time.After(5 * time.Second): // assume server started successfully + val.api = apiSrv } - val.tmNode = tmNode - val.api = apiSrv - val.RPCClient = rpcClient - return nil } From 4bc56bdac330ceba88270d931db2138e4445401a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Jun 2020 09:16:08 -0400 Subject: [PATCH 13/29] use pkg lock --- testutil/network.go | 22 +++++++++++++++++++++- testutil/network_test.go | 36 +++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index d2536c2840f6..87d9caf945d6 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -9,6 +9,7 @@ import ( "net/url" "os" "path/filepath" + "sync" "testing" "time" @@ -39,6 +40,9 @@ import ( var ( _, cdc = simapp.MakeCodecs() + + // package-wide network lock to only allow one test network at a time + lock = new(sync.Mutex) ) // Config defines the necessary configuration used to bootstrap and start an @@ -81,6 +85,11 @@ type ( // configured to start any number of validators, each with its own RPC and API // clients. Typically, this test network would be used in client and integration // testing where user input is expected. + // + // Note, due to Tendermint constraints in regards to RPC functionality, there + // may only be one test network running at a time. Thus, any caller must be + // sure to Cleanup after testing is finished in order to allow other tests + // to create networks. Network struct { T *testing.T BaseDir string @@ -111,6 +120,10 @@ type ( ) func NewTestNetwork(t *testing.T, cfg Config) *Network { + // only one caller/test can create and use a network at a time + t.Log("acquiring test network lock") + lock.Lock() + baseDir, err := ioutil.TempDir(os.TempDir(), cfg.ChainID) require.NoError(t, err) @@ -325,8 +338,15 @@ func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, err } // Cleanup removes the root testing (temporary) directory and stops both the -// Tendermint and API services. +// Tendermint and API services. It allows other callers to create and start +// test networks. This method must be called when a test is finished, typically +// in a defer. func (n *Network) Cleanup() { + defer func() { + lock.Unlock() + n.T.Log("released test network lock") + }() + n.T.Log("cleaning up test network...") for _, v := range n.Validators { diff --git a/testutil/network_test.go b/testutil/network_test.go index 019c848364a9..c8add5fcaf02 100644 --- a/testutil/network_test.go +++ b/testutil/network_test.go @@ -4,14 +4,36 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" ) -func TestNetwork_Liveness(t *testing.T) { - n := NewTestNetwork(t, DefaultConfig()) - defer n.Cleanup() - require.NotNil(t, n) +type IntegrationTestSuite struct { + suite.Suite - h, err := n.WaitForHeightWithTimeout(10, time.Minute) - require.NoError(t, err, "expected to reach 10 blocks; got %d", h) + // cfg eConfig + network *Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.network = NewTestNetwork(s.T(), DefaultConfig()) + s.Require().NotNil(s.network) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestNetwork_Liveness() { + h, err := s.network.WaitForHeightWithTimeout(10, time.Minute) + s.Require().NoError(err, "expected to reach 10 blocks; got %d", h) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) } From c389c8164e8f8b459b06653ce39bac36b496c35c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Jun 2020 09:25:58 -0400 Subject: [PATCH 14/29] lint++ --- testutil/network_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/testutil/network_test.go b/testutil/network_test.go index c8add5fcaf02..12f2338cd61c 100644 --- a/testutil/network_test.go +++ b/testutil/network_test.go @@ -10,7 +10,6 @@ import ( type IntegrationTestSuite struct { suite.Suite - // cfg eConfig network *Network } From a6f8fe35b8ea23717c184c461d7489af70984c8d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Jun 2020 09:26:42 -0400 Subject: [PATCH 15/29] lint++ --- testutil/util.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testutil/util.go b/testutil/util.go index 646a274253df..3ca1047b1282 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -135,9 +135,7 @@ func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { return nil } -func initGenFiles( - cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string, -) error { +func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error { // set the accounts in the genesis state var authGenState authtypes.GenesisState From 9442a8ba540b4d8d9a6b3dc422433784d7962429 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Jun 2020 09:46:48 -0400 Subject: [PATCH 16/29] Remove use of errgroup --- testutil/network.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index 87d9caf945d6..7f330b30c650 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -21,7 +21,6 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/node" tmclient "github.com/tendermint/tendermint/rpc/client" - "golang.org/x/sync/errgroup" "github.com/cosmos/cosmos-sdk/client" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" @@ -282,15 +281,10 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { require.NoError(t, collectGenFiles(cfg, network.Validators, network.BaseDir)) t.Log("starting test network...") - var eg errgroup.Group for _, v := range network.Validators { - val := v - eg.Go(func() error { - return startInProcess(cfg, val) - }) + require.NoError(t, startInProcess(cfg, v)) } - require.NoError(t, eg.Wait()) t.Log("started test network") // Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any From 0aff4e40e0b7812799a88ef5ae248b532b0de459 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 24 Jun 2020 16:53:28 -0400 Subject: [PATCH 17/29] ci: remove -race flag from tests --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 85040ee3ddf4..f8cb95c69195 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,7 +60,7 @@ jobs: if: "env.GIT_DIFF != ''" - name: test & coverage report creation run: | - cat xaa.txt | xargs go test -mod=readonly -timeout 8m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' + cat xaa.txt | xargs go test -mod=readonly -timeout 8m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' if: "env.GIT_DIFF != ''" - name: filter out DONTCOVER run: | @@ -98,7 +98,7 @@ jobs: if: "env.GIT_DIFF != ''" - name: test & coverage report creation run: | - cat xab.txt | xargs go test -mod=readonly -timeout 6m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' + cat xab.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' if: "env.GIT_DIFF != ''" - name: filter out DONTCOVER run: | @@ -136,7 +136,7 @@ jobs: if: "env.GIT_DIFF != ''" - name: test & coverage report creation run: | - cat xac.txt | xargs go test -mod=readonly -timeout 6m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' + cat xac.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' if: "env.GIT_DIFF != ''" - name: filter out DONTCOVER run: | @@ -174,7 +174,7 @@ jobs: if: "env.GIT_DIFF != ''" - name: test & coverage report creation run: | - cat xad.txt | xargs go test -mod=readonly -timeout 6m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' + cat xad.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' if: "env.GIT_DIFF != ''" - name: filter out DONTCOVER run: | From 6d1719fc37cd1fefad817f27c036b0ca0727f6f9 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 10:47:54 -0400 Subject: [PATCH 18/29] Add doc --- testutil/doc.go | 65 ++++++++++++++++++++++++++++++++ x/bank/client/rest/query_test.go | 1 + 2 files changed, 66 insertions(+) create mode 100644 testutil/doc.go diff --git a/testutil/doc.go b/testutil/doc.go new file mode 100644 index 000000000000..94280c689cad --- /dev/null +++ b/testutil/doc.go @@ -0,0 +1,65 @@ +/* +Package testutil implements and exposes a fully operational in-process Tendermint +test network that consists of at least one or potentially many validators. This +test network can be used primarily for integration tests or unit test suites. + +The testnetwork utilizes SimApp as the ABCI application and uses all the modules +defined in the Cosmos SDK. An in-process test network can be configured with any +number of validators as well as account funds and even custom genesis state. + +When creating a test network, a series of Validator objects are returned. Each +Validator objects has useful information such as their address and pubkey. A +Validator will also provide its RPC, P2P, and API addresses that can be useful +for integration testing. In addition, a Tendermint local RPC client also provided +which can be handy for making direct RPC calls to Tendermint. + +Note, due to limitations in concurrency and the design of the RPC layer in +Tendermint, only the first Validator objects will have an RPC and API client +exposed. Due to this exact same limitation, only a single test network can exist +at a time. A caller must be certain it calls Cleanup after it no longer needs +the network. + +A typical testing flow might look like the following: + + type IntegrationTestSuite struct { + suite.Suite + + cfg testutil.Config + network *testutil.Network + } + + func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := testutil.DefaultConfig() + cfg.NumValidators = 1 + + s.cfg = cfg + s.network = testutil.NewTestNetwork(s.T(), cfg) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + } + + func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + + // This is important and must be called to ensure other tests can create + // a network! + s.network.Cleanup() + } + + func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + // Use baseURL to make API HTTP requests or use val.RPCClient to make direct + // Tendermint RPC calls. + // ... + } + + func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) + } +*/ +package testutil diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index 5adb00c7bb5a..d682c868503a 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -13,6 +13,7 @@ import ( type IntegrationTestSuite struct { suite.Suite + cfg testutil.Config network *testutil.Network } From 93454e53eab723ffee68a385d141a8b2811462c3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 13:57:53 -0400 Subject: [PATCH 19/29] cl++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fae116339612..c31e10073f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -146,6 +146,7 @@ be used to retrieve the actual proposal `Content`. Also the `NewMsgSubmitProposa ### Features +* (tests) [\#6489](https://github.com/cosmos/cosmos-sdk/pull/6489) Introduce package `testutil`, new in-process testing network framework for use in integration and unit tests. * (crypto/multisig) [\#6241](https://github.com/cosmos/cosmos-sdk/pull/6241) Add Multisig type directly to the repo. Previously this was in tendermint. * (rest) [\#6167](https://github.com/cosmos/cosmos-sdk/pull/6167) Support `max-body-bytes` CLI flag for the REST service. * (x/ibc) [\#5588](https://github.com/cosmos/cosmos-sdk/pull/5588) Add [ICS 024 - Host State Machine Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements) subpackage to `x/ibc` module. From 52aa585b79e6de959601656cfdd92c2207a0fd8a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 15:06:50 -0400 Subject: [PATCH 20/29] Add section to godoc --- testutil/network.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testutil/network.go b/testutil/network.go index 7f330b30c650..899e6e0378f5 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -88,7 +88,8 @@ type ( // Note, due to Tendermint constraints in regards to RPC functionality, there // may only be one test network running at a time. Thus, any caller must be // sure to Cleanup after testing is finished in order to allow other tests - // to create networks. + // to create networks. In addition, only the first validator will have a valid + // RPC and API server/client. Network struct { T *testing.T BaseDir string From f889a0bce210909892861ad504165aa62f494955 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 15:14:28 -0400 Subject: [PATCH 21/29] testutil: address comments --- testutil/network.go | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index 899e6e0378f5..8d6d9fe27bf7 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -47,18 +47,19 @@ var ( // Config defines the necessary configuration used to bootstrap and start an // in-process local testing network. type Config struct { - GenesisState map[string]json.RawMessage - TimeoutCommit time.Duration - ChainID string - NumValidators int - BondDenom string - MinGasPrices string - Passphrase string - AccountTokens sdk.Int - StakingTokens sdk.Int - BondedTokens sdk.Int - PruningStrategy string - EnableLogging bool + GenesisState map[string]json.RawMessage // custom gensis state to provide + TimeoutCommit time.Duration // the consensus commitment timeout + ChainID string // the network chain-id + NumValidators int // the total number of validators to create and bond + BondDenom string // the staking bond denomination + MinGasPrices string // the minimum gas prices each validator will accept + Passphrase string // the passphrase provided to the test keyring + AccountTokens sdk.Int // the amount of unique validator tokens (e.g. 1000node0) + StakingTokens sdk.Int // the amount of tokens each validator has available to stake + BondedTokens sdk.Int // the amount of tokens each validator stakes + PruningStrategy string // the pruning strategy each validator will have + EnableLogging bool // enable Tendermint logging to STDOUT + CleanupDir bool // remove base temporary directory during cleanup } // DefaultConfig returns a sane default configuration suitable for nearly all @@ -76,6 +77,7 @@ func DefaultConfig() Config { StakingTokens: sdk.TokensFromConsensusPower(500), BondedTokens: sdk.TokensFromConsensusPower(100), PruningStrategy: storetypes.PruningOptionNothing, + CleanupDir: true, } } @@ -94,6 +96,8 @@ type ( T *testing.T BaseDir string Validators []*Validator + + config Config } // Validator defines an in-process Tendermint validator node. Through this object, @@ -126,11 +130,13 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { baseDir, err := ioutil.TempDir(os.TempDir(), cfg.ChainID) require.NoError(t, err) + t.Logf("created temporary directory: %s", baseDir) network := &Network{ T: t, BaseDir: baseDir, Validators: make([]*Validator, cfg.NumValidators), + config: cfg, } t.Log("preparing test network...") @@ -354,6 +360,9 @@ func (n *Network) Cleanup() { } } - _ = os.RemoveAll(n.BaseDir) + if n.config.CleanupDir { + _ = os.RemoveAll(n.BaseDir) + } + n.T.Log("finished cleaning up test network") } From cdc9db6e56668657858b7a697412b751c32f0aca Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Thu, 25 Jun 2020 19:49:56 -0400 Subject: [PATCH 22/29] Update testutil/doc.go Co-authored-by: Aaron Craelius --- testutil/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/doc.go b/testutil/doc.go index 94280c689cad..ef1b5a101453 100644 --- a/testutil/doc.go +++ b/testutil/doc.go @@ -14,7 +14,7 @@ for integration testing. In addition, a Tendermint local RPC client also provide which can be handy for making direct RPC calls to Tendermint. Note, due to limitations in concurrency and the design of the RPC layer in -Tendermint, only the first Validator objects will have an RPC and API client +Tendermint, only the first Validator object will have an RPC and API client exposed. Due to this exact same limitation, only a single test network can exist at a time. A caller must be certain it calls Cleanup after it no longer needs the network. From 8aa5c714de7c7dbf7b381c0d689ffcadcd88bcd4 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Thu, 25 Jun 2020 19:50:05 -0400 Subject: [PATCH 23/29] Update testutil/doc.go Co-authored-by: Aaron Craelius --- testutil/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/doc.go b/testutil/doc.go index ef1b5a101453..60430d9a8664 100644 --- a/testutil/doc.go +++ b/testutil/doc.go @@ -10,7 +10,7 @@ number of validators as well as account funds and even custom genesis state. When creating a test network, a series of Validator objects are returned. Each Validator objects has useful information such as their address and pubkey. A Validator will also provide its RPC, P2P, and API addresses that can be useful -for integration testing. In addition, a Tendermint local RPC client also provided +for integration testing. In addition, a Tendermint local RPC client is also provided which can be handy for making direct RPC calls to Tendermint. Note, due to limitations in concurrency and the design of the RPC layer in From 426505e34780867629e8ed16fa86e7774ef2caa4 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Thu, 25 Jun 2020 19:50:16 -0400 Subject: [PATCH 24/29] Update testutil/doc.go Co-authored-by: Aaron Craelius --- testutil/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/doc.go b/testutil/doc.go index 60430d9a8664..3035d4e1a2d8 100644 --- a/testutil/doc.go +++ b/testutil/doc.go @@ -8,7 +8,7 @@ defined in the Cosmos SDK. An in-process test network can be configured with any number of validators as well as account funds and even custom genesis state. When creating a test network, a series of Validator objects are returned. Each -Validator objects has useful information such as their address and pubkey. A +Validator object has useful information such as their address and pubkey. A Validator will also provide its RPC, P2P, and API addresses that can be useful for integration testing. In addition, a Tendermint local RPC client is also provided which can be handy for making direct RPC calls to Tendermint. From c6a208aa90c95bcb0558c9af34b86aa618e1e28f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 20:20:27 -0400 Subject: [PATCH 25/29] testutil: make app confiruable --- testutil/network.go | 18 ++++++++++++++++-- testutil/network_test.go | 2 +- testutil/util.go | 19 ++++--------------- x/bank/client/rest/query_test.go | 2 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index 8d6d9fe27bf7..3c625a9eb566 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -21,7 +21,9 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/node" tmclient "github.com/tendermint/tendermint/rpc/client" + dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -44,6 +46,18 @@ var ( lock = new(sync.Mutex) ) +// AppConstructor defines a function which accepts a network configuration and +// creates an ABCI Application to provide to Tendermint. +type AppConstructor = func(cfg Config, val Validator) server.Application + +func NewSimApp(cfg Config, val Validator) server.Application { + return simapp.NewSimApp( + val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, + baseapp.SetPruning(storetypes.NewPruningOptionsFromString(cfg.PruningStrategy)), + baseapp.SetMinGasPrices(cfg.MinGasPrices), + ) +} + // Config defines the necessary configuration used to bootstrap and start an // in-process local testing network. type Config struct { @@ -123,7 +137,7 @@ type ( } ) -func NewTestNetwork(t *testing.T, cfg Config) *Network { +func NewTestNetwork(t *testing.T, cfg Config, appConstructor AppConstructor) *Network { // only one caller/test can create and use a network at a time t.Log("acquiring test network lock") lock.Lock() @@ -289,7 +303,7 @@ func NewTestNetwork(t *testing.T, cfg Config) *Network { t.Log("starting test network...") for _, v := range network.Validators { - require.NoError(t, startInProcess(cfg, v)) + require.NoError(t, startInProcess(cfg, v, appConstructor)) } t.Log("started test network") diff --git a/testutil/network_test.go b/testutil/network_test.go index 12f2338cd61c..9b12959c6b7e 100644 --- a/testutil/network_test.go +++ b/testutil/network_test.go @@ -16,7 +16,7 @@ type IntegrationTestSuite struct { func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") - s.network = NewTestNetwork(s.T(), DefaultConfig()) + s.network = NewTestNetwork(s.T(), DefaultConfig(), NewSimApp) s.Require().NotNil(s.network) _, err := s.network.WaitForHeight(1) diff --git a/testutil/util.go b/testutil/util.go index 3ca1047b1282..b1c51597838d 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -12,40 +12,29 @@ import ( "github.com/tendermint/tendermint/rpc/client/local" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server/api" - "github.com/cosmos/cosmos-sdk/simapp" - storetypes "github.com/cosmos/cosmos-sdk/store/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) -func startInProcess(cfg Config, val *Validator) error { +func startInProcess(cfg Config, val *Validator, appConstructor AppConstructor) error { logger := val.Ctx.Logger tmCfg := val.Ctx.Config tmCfg.Instrumentation.Prometheus = false - db := dbm.NewMemDB() - genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) - - app := simapp.NewSimApp( - logger, db, nil, true, make(map[int64]bool), - tmCfg.RootDir, 0, - baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), - baseapp.SetMinGasPrices(cfg.MinGasPrices), - ) - nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) if err != nil { return err } + app := appConstructor(cfg, *val) + + genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) tmNode, err := node.NewNode( tmCfg, pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index d682c868503a..1a23b4b9b318 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -25,7 +25,7 @@ func (s *IntegrationTestSuite) SetupSuite() { cfg.NumValidators = 1 s.cfg = cfg - s.network = testutil.NewTestNetwork(s.T(), cfg) + s.network = testutil.NewTestNetwork(s.T(), cfg, testutil.NewSimApp) _, err := s.network.WaitForHeight(1) s.Require().NoError(err) From 05fd4a722c217abcfacc3fd670772892103f7320 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 20:22:09 -0400 Subject: [PATCH 26/29] testutil: update go doc --- testutil/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/doc.go b/testutil/doc.go index 94280c689cad..46fb1ba4bb88 100644 --- a/testutil/doc.go +++ b/testutil/doc.go @@ -35,7 +35,7 @@ A typical testing flow might look like the following: cfg.NumValidators = 1 s.cfg = cfg - s.network = testutil.NewTestNetwork(s.T(), cfg) + s.network = testutil.NewTestNetwork(s.T(), cfg, testutil.NewSimApp) _, err := s.network.WaitForHeight(1) s.Require().NoError(err) From 195447eff21c6bbb9828ef4c0f0f6f16464f5bca Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 20:29:00 -0400 Subject: [PATCH 27/29] testutil: revise app con --- testutil/network.go | 10 ++++++---- testutil/util.go | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index 3c625a9eb566..7b511548d9d4 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -48,19 +48,20 @@ var ( // AppConstructor defines a function which accepts a network configuration and // creates an ABCI Application to provide to Tendermint. -type AppConstructor = func(cfg Config, val Validator) server.Application +type AppConstructor = func(val Validator) server.Application -func NewSimApp(cfg Config, val Validator) server.Application { +func NewSimApp(val Validator) server.Application { return simapp.NewSimApp( val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, - baseapp.SetPruning(storetypes.NewPruningOptionsFromString(cfg.PruningStrategy)), - baseapp.SetMinGasPrices(cfg.MinGasPrices), + baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), + baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), ) } // Config defines the necessary configuration used to bootstrap and start an // in-process local testing network. type Config struct { + AppConstructor AppConstructor // the ABCI application constructor GenesisState map[string]json.RawMessage // custom gensis state to provide TimeoutCommit time.Duration // the consensus commitment timeout ChainID string // the network chain-id @@ -80,6 +81,7 @@ type Config struct { // testing requirements. func DefaultConfig() Config { return Config{ + AppConstructor: NewSimApp, GenesisState: simapp.ModuleBasics.DefaultGenesis(cdc), TimeoutCommit: 2 * time.Second, ChainID: "chain-" + tmrand.NewRand().Str(6), diff --git a/testutil/util.go b/testutil/util.go index b1c51597838d..a5e856cbd7e0 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -32,7 +32,7 @@ func startInProcess(cfg Config, val *Validator, appConstructor AppConstructor) e return err } - app := appConstructor(cfg, *val) + app := appConstructor(*val) genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) tmNode, err := node.NewNode( From 939b6c3d45187b22509c74121ed1590eccc8068d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 20:29:26 -0400 Subject: [PATCH 28/29] testutil: update go doc --- testutil/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutil/doc.go b/testutil/doc.go index dc1c0c1b5af2..3035d4e1a2d8 100644 --- a/testutil/doc.go +++ b/testutil/doc.go @@ -35,7 +35,7 @@ A typical testing flow might look like the following: cfg.NumValidators = 1 s.cfg = cfg - s.network = testutil.NewTestNetwork(s.T(), cfg, testutil.NewSimApp) + s.network = testutil.NewTestNetwork(s.T(), cfg) _, err := s.network.WaitForHeight(1) s.Require().NoError(err) From 9c77941e3b325a635f6530658a37fc5974b3111a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 25 Jun 2020 20:31:05 -0400 Subject: [PATCH 29/29] Fixes --- testutil/network.go | 4 ++-- testutil/network_test.go | 2 +- testutil/util.go | 4 ++-- x/bank/client/rest/query_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/testutil/network.go b/testutil/network.go index 7b511548d9d4..b4f3c46a2a5b 100644 --- a/testutil/network.go +++ b/testutil/network.go @@ -139,7 +139,7 @@ type ( } ) -func NewTestNetwork(t *testing.T, cfg Config, appConstructor AppConstructor) *Network { +func NewTestNetwork(t *testing.T, cfg Config) *Network { // only one caller/test can create and use a network at a time t.Log("acquiring test network lock") lock.Lock() @@ -305,7 +305,7 @@ func NewTestNetwork(t *testing.T, cfg Config, appConstructor AppConstructor) *Ne t.Log("starting test network...") for _, v := range network.Validators { - require.NoError(t, startInProcess(cfg, v, appConstructor)) + require.NoError(t, startInProcess(cfg, v)) } t.Log("started test network") diff --git a/testutil/network_test.go b/testutil/network_test.go index 9b12959c6b7e..12f2338cd61c 100644 --- a/testutil/network_test.go +++ b/testutil/network_test.go @@ -16,7 +16,7 @@ type IntegrationTestSuite struct { func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") - s.network = NewTestNetwork(s.T(), DefaultConfig(), NewSimApp) + s.network = NewTestNetwork(s.T(), DefaultConfig()) s.Require().NotNil(s.network) _, err := s.network.WaitForHeight(1) diff --git a/testutil/util.go b/testutil/util.go index a5e856cbd7e0..eb89e2272b92 100644 --- a/testutil/util.go +++ b/testutil/util.go @@ -22,7 +22,7 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) -func startInProcess(cfg Config, val *Validator, appConstructor AppConstructor) error { +func startInProcess(cfg Config, val *Validator) error { logger := val.Ctx.Logger tmCfg := val.Ctx.Config tmCfg.Instrumentation.Prometheus = false @@ -32,7 +32,7 @@ func startInProcess(cfg Config, val *Validator, appConstructor AppConstructor) e return err } - app := appConstructor(*val) + app := cfg.AppConstructor(*val) genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) tmNode, err := node.NewNode( diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go index 1a23b4b9b318..d682c868503a 100644 --- a/x/bank/client/rest/query_test.go +++ b/x/bank/client/rest/query_test.go @@ -25,7 +25,7 @@ func (s *IntegrationTestSuite) SetupSuite() { cfg.NumValidators = 1 s.cfg = cfg - s.network = testutil.NewTestNetwork(s.T(), cfg, testutil.NewSimApp) + s.network = testutil.NewTestNetwork(s.T(), cfg) _, err := s.network.WaitForHeight(1) s.Require().NoError(err)