Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
parent
bf24d366fd
commit
e292bdab10
@ -130,36 +130,6 @@ var (
|
||||
_ servertypes.Application = (*SimApp)(nil)
|
||||
)
|
||||
|
||||
// stdAccAddressCodec is a temporary address codec that we will use until we
|
||||
// can populate it with the correct bech32 prefixes without depending on the global.
|
||||
type stdAccAddressCodec struct{}
|
||||
|
||||
func (g stdAccAddressCodec) StringToBytes(text string) ([]byte, error) {
|
||||
if text == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return sdk.AccAddressFromBech32(text)
|
||||
}
|
||||
|
||||
func (g stdAccAddressCodec) BytesToString(bz []byte) (string, error) {
|
||||
if bz == nil {
|
||||
return "", nil
|
||||
}
|
||||
return sdk.AccAddress(bz).String(), nil
|
||||
}
|
||||
|
||||
// stdValAddressCodec is a temporary address codec that we will use until we
|
||||
// can populate it with the correct bech32 prefixes without depending on the global.
|
||||
type stdValAddressCodec struct{}
|
||||
|
||||
func (g stdValAddressCodec) StringToBytes(text string) ([]byte, error) {
|
||||
return sdk.ValAddressFromBech32(text)
|
||||
}
|
||||
|
||||
func (g stdValAddressCodec) BytesToString(bz []byte) (string, error) {
|
||||
return sdk.ValAddress(bz).String(), nil
|
||||
}
|
||||
|
||||
// SimApp extends an ABCI application, but with most of its parameters exported.
|
||||
// They are exported for convenience in creating helper functions, as object
|
||||
// capabilities aren't needed for testing.
|
||||
|
||||
@ -1,169 +0,0 @@
|
||||
package testnet_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cmtcfg "github.com/cometbft/cometbft/config"
|
||||
"github.com/cometbft/cometbft/rpc/client/http"
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/simapp"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testnet"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
const memdb = "memdb"
|
||||
|
||||
// A single comet server in a network runs an RPC server successfully.
|
||||
func TestCometRPC_SingleRPCServer(t *testing.T) {
|
||||
const nVals = 2
|
||||
|
||||
valPKs := testnet.NewValidatorPrivKeys(nVals)
|
||||
cmtVals := valPKs.CometGenesisValidators()
|
||||
stakingVals := cmtVals.StakingValidators()
|
||||
|
||||
const chainID = "comet-rpc-singleton"
|
||||
|
||||
b := testnet.DefaultGenesisBuilderOnlyValidators(
|
||||
chainID,
|
||||
stakingVals,
|
||||
sdk.NewCoin(sdk.DefaultBondDenom, sdk.DefaultPowerReduction),
|
||||
)
|
||||
|
||||
jGenesis := b.Encode()
|
||||
|
||||
// Logs shouldn't be necessary here because we are exercising CometStarter,
|
||||
// and only doing a very basic check that the RPC talks to the app.
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
nodes, err := testnet.NewNetwork(nVals, func(idx int) *testnet.CometStarter {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
app := simapp.NewSimApp(
|
||||
logger,
|
||||
dbm.NewMemDB(),
|
||||
nil,
|
||||
true,
|
||||
simtestutil.NewAppOptionsWithFlagHome(rootDir),
|
||||
baseapp.SetChainID(chainID),
|
||||
)
|
||||
|
||||
cfg := cmtcfg.DefaultConfig()
|
||||
cfg.BaseConfig.DBBackend = memdb
|
||||
|
||||
cs := testnet.NewCometStarter(
|
||||
app,
|
||||
cfg,
|
||||
valPKs[idx].Val,
|
||||
jGenesis,
|
||||
rootDir,
|
||||
)
|
||||
|
||||
// Only enable the RPC on the first service.
|
||||
if idx == 0 {
|
||||
cs = cs.RPCListen()
|
||||
}
|
||||
|
||||
return cs
|
||||
})
|
||||
defer nodes.StopAndWait()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Once HTTP client to be shared across the following subtests.
|
||||
c, err := http.New(nodes[0].Config().RPC.ListenAddress, "/websocket")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("status query", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
st, err := c.Status(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Simple assertion to ensure we have a functioning RPC.
|
||||
require.Equal(t, chainID, st.NodeInfo.Network)
|
||||
})
|
||||
|
||||
// Block until reported height is at least 1,
|
||||
// otherwise we can't make transactions.
|
||||
require.NoError(t, testnet.WaitForNodeHeight(nodes[0], 1, 10*time.Second))
|
||||
|
||||
t.Run("simple abci query", func(t *testing.T) {
|
||||
res, err := c.ABCIQuery(
|
||||
context.Background(),
|
||||
"/cosmos.bank.v1beta1.Query/TotalSupply",
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
registry := codectypes.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(registry)
|
||||
|
||||
var tsResp banktypes.QueryTotalSupplyResponse
|
||||
require.NoError(t, cdc.Unmarshal(res.Response.Value, &tsResp))
|
||||
|
||||
// Just check that something is reported in the supply.
|
||||
require.NotEmpty(t, tsResp.Supply)
|
||||
})
|
||||
}
|
||||
|
||||
// Starting two comet instances with an RPC server,
|
||||
// fails with a predictable error.
|
||||
func TestCometRPC_MultipleRPCError(t *testing.T) {
|
||||
const nVals = 2
|
||||
|
||||
valPKs := testnet.NewValidatorPrivKeys(nVals)
|
||||
cmtVals := valPKs.CometGenesisValidators()
|
||||
stakingVals := cmtVals.StakingValidators()
|
||||
|
||||
const chainID = "comet-rpc-multiple"
|
||||
|
||||
b := testnet.DefaultGenesisBuilderOnlyValidators(
|
||||
chainID,
|
||||
stakingVals,
|
||||
sdk.NewCoin(sdk.DefaultBondDenom, sdk.DefaultPowerReduction),
|
||||
)
|
||||
|
||||
jGenesis := b.Encode()
|
||||
|
||||
// Logs shouldn't be necessary here because we are exercising CometStarter.
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
nodes, err := testnet.NewNetwork(nVals, func(idx int) *testnet.CometStarter {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
app := simapp.NewSimApp(
|
||||
logger,
|
||||
dbm.NewMemDB(),
|
||||
nil,
|
||||
true,
|
||||
simtestutil.NewAppOptionsWithFlagHome(rootDir),
|
||||
baseapp.SetChainID(chainID),
|
||||
)
|
||||
|
||||
cfg := cmtcfg.DefaultConfig()
|
||||
cfg.BaseConfig.DBBackend = memdb
|
||||
|
||||
return testnet.NewCometStarter(
|
||||
app,
|
||||
cfg,
|
||||
valPKs[idx].Val,
|
||||
jGenesis,
|
||||
rootDir,
|
||||
).RPCListen() // Every node has RPCListen enabled, which will cause a failure.
|
||||
})
|
||||
defer nodes.StopAndWait()
|
||||
|
||||
// Returned error is convertible to CometRPCInUseError.
|
||||
// We can't test the exact value because it includes a stack trace.
|
||||
require.Error(t, err)
|
||||
require.ErrorAs(t, err, new(testnet.CometRPCInUseError))
|
||||
}
|
||||
@ -1,136 +0,0 @@
|
||||
package testnet_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cmtcfg "github.com/cometbft/cometbft/config"
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/simapp"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testnet"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Use a limited set of available ports to ensure that
|
||||
// retries eventually land on a free port.
|
||||
func TestCometStarter_PortContention(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping long test in short mode")
|
||||
}
|
||||
|
||||
const nVals = 4
|
||||
|
||||
// Find n+1 addresses that should be free.
|
||||
// Ephemeral port range should start at about 49k+
|
||||
// according to `sysctl net.inet.ip.portrange` on macOS,
|
||||
// and at about 32k+ on Linux
|
||||
// according to `sysctl net.ipv4.ip_local_port_range`.
|
||||
//
|
||||
// Because we attempt to find free addresses outside that range,
|
||||
// it is unlikely that another process will claim a port
|
||||
// we discover to be free, during the time this test runs.
|
||||
const portSeekStart = 19000
|
||||
reuseAddrs := make([]string, 0, nVals+1)
|
||||
for i := portSeekStart; i < portSeekStart+1000; i++ {
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", i)
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
// No need to log the failure.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the port was free, append it to our reusable addresses.
|
||||
reuseAddrs = append(reuseAddrs, "tcp://"+addr)
|
||||
_ = ln.Close()
|
||||
|
||||
if len(reuseAddrs) == nVals+1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(reuseAddrs) != nVals+1 {
|
||||
t.Fatalf("needed %d free ports but only found %d", nVals+1, len(reuseAddrs))
|
||||
}
|
||||
|
||||
// Now that we have one more port than the number of validators,
|
||||
// there is a good chance that picking a random port will conflict with a previously chosen one.
|
||||
// But since CometStarter retries several times,
|
||||
// it should eventually land on a free port.
|
||||
|
||||
valPKs := testnet.NewValidatorPrivKeys(nVals)
|
||||
cmtVals := valPKs.CometGenesisValidators()
|
||||
stakingVals := cmtVals.StakingValidators()
|
||||
|
||||
const chainID = "simapp-cometstarter"
|
||||
|
||||
b := testnet.DefaultGenesisBuilderOnlyValidators(
|
||||
chainID,
|
||||
stakingVals,
|
||||
sdk.NewCoin(sdk.DefaultBondDenom, sdk.DefaultPowerReduction),
|
||||
)
|
||||
|
||||
jGenesis := b.Encode()
|
||||
|
||||
// Use an info-level logger, because the debug logs in comet are noisy
|
||||
// and there is a data race in comet debug logs,
|
||||
// due to be fixed in v0.37.1 which is not yet released:
|
||||
// https://github.com/cometbft/cometbft/pull/532
|
||||
logger := log.NewTestLoggerInfo(t)
|
||||
|
||||
const nRuns = 4
|
||||
for i := 0; i < nRuns; i++ {
|
||||
t.Run(fmt.Sprintf("attempt %d", i), func(t *testing.T) {
|
||||
nodes, err := testnet.NewNetwork(nVals, func(idx int) *testnet.CometStarter {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
app := simapp.NewSimApp(
|
||||
logger.With("instance", idx),
|
||||
dbm.NewMemDB(),
|
||||
nil,
|
||||
true,
|
||||
simtestutil.NewAppOptionsWithFlagHome(rootDir),
|
||||
baseapp.SetChainID(chainID),
|
||||
)
|
||||
|
||||
cfg := cmtcfg.DefaultConfig()
|
||||
|
||||
// memdb is sufficient for this test.
|
||||
cfg.BaseConfig.DBBackend = "memdb"
|
||||
|
||||
return testnet.NewCometStarter(
|
||||
app,
|
||||
cfg,
|
||||
valPKs[idx].Val,
|
||||
jGenesis,
|
||||
rootDir,
|
||||
).
|
||||
Logger(logger.With("rootmodule", fmt.Sprintf("comet_node-%d", idx))).
|
||||
TCPAddrChooser(func() string {
|
||||
// This chooser function is the key of this test,
|
||||
// where there is only one more available address than there are nodes.
|
||||
// Therefore it is likely that an address will already be in use,
|
||||
// thereby exercising the address-in-use retry.
|
||||
return reuseAddrs[rand.Intn(len(reuseAddrs))]
|
||||
})
|
||||
})
|
||||
|
||||
// Ensure nodes are stopped completely,
|
||||
// so that we don't get t.Cleanup errors around directories not being empty.
|
||||
defer nodes.StopAndWait()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure that the height advances.
|
||||
// Looking for height 2 seems more meaningful than 1.
|
||||
require.NoError(t, testnet.WaitForNodeHeight(nodes[0], 2, 10*time.Second))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
// Package testnet contains tests for
|
||||
// [github.com/cosmos/cosmos-sdk/testutil/testnet].
|
||||
//
|
||||
// Eventually all of these tests will move into that package,
|
||||
// but that is currently blocked on having a minimal app defined
|
||||
// in the root cosmos-sdk Go module.
|
||||
// Once that app is available, the contents of this package
|
||||
// will be moved to testutil/testnet,
|
||||
// and references to SimApp will be replaced by the minimal app.
|
||||
package testnet
|
||||
@ -1,114 +0,0 @@
|
||||
package testnet_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
cmtcfg "github.com/cometbft/cometbft/config"
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/simapp"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testnet"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func Example_basicUsage() {
|
||||
const nVals = 2
|
||||
|
||||
// Set up new private keys for the set of validators.
|
||||
valPKs := testnet.NewValidatorPrivKeys(nVals)
|
||||
|
||||
// Comet-style validators.
|
||||
cmtVals := valPKs.CometGenesisValidators()
|
||||
|
||||
// Cosmos SDK staking validators for genesis.
|
||||
stakingVals := cmtVals.StakingValidators()
|
||||
|
||||
const chainID = "example-basic"
|
||||
|
||||
// Create a genesis builder that only requires validators,
|
||||
// without any separate delegator accounts.
|
||||
//
|
||||
// If you need further customization, start with testnet.NewGenesisBuilder().
|
||||
b := testnet.DefaultGenesisBuilderOnlyValidators(
|
||||
chainID,
|
||||
stakingVals,
|
||||
// The amount to use in each validator's account during gentx.
|
||||
sdk.NewCoin(sdk.DefaultBondDenom, sdk.DefaultPowerReduction),
|
||||
)
|
||||
|
||||
// JSON-formatted genesis.
|
||||
jGenesis := b.Encode()
|
||||
|
||||
// In this example, we have an outer root directory for the validators.
|
||||
// Use t.TempDir() in tests.
|
||||
rootDir, err := os.MkdirTemp("", "testnet-example-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
// In tests, you probably want to use log.NewTestLoggerInfo(t).
|
||||
logger := log.NewNopLogger()
|
||||
|
||||
// The NewNetwork function creates a network of validators.
|
||||
// We have to provide a callback to return CometStarter instances.
|
||||
// NewNetwork will start all the comet instances concurrently
|
||||
// and join the nodes together.
|
||||
nodes, err := testnet.NewNetwork(nVals, func(idx int) *testnet.CometStarter {
|
||||
// Make a new directory for the validator being created.
|
||||
// In tests, this would be a simpler call to t.TempDir().
|
||||
dir := filepath.Join(rootDir, fmt.Sprintf("val-%d", idx))
|
||||
if err := os.Mkdir(dir, 0o755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: use a different minimal app for this.
|
||||
app := simapp.NewSimApp(
|
||||
logger.With("instance", idx),
|
||||
dbm.NewMemDB(),
|
||||
nil,
|
||||
true,
|
||||
simtestutil.NewAppOptionsWithFlagHome(rootDir),
|
||||
baseapp.SetChainID(chainID),
|
||||
)
|
||||
|
||||
// Each CometStarter instance must be associated with
|
||||
// a distinct comet Config object,
|
||||
// as the CometStarter will automatically modify some fields,
|
||||
// including P2P.ListenAddress.
|
||||
cfg := cmtcfg.DefaultConfig()
|
||||
|
||||
// No need to persist comet's DB to disk in this example.
|
||||
cfg.BaseConfig.DBBackend = "memdb"
|
||||
|
||||
return testnet.NewCometStarter(
|
||||
app,
|
||||
cfg,
|
||||
valPKs[idx].Val, // Validator private key for this comet instance.
|
||||
jGenesis, // Raw bytes of genesis file.
|
||||
dir, // Where to put files on disk.
|
||||
).Logger(logger.With("root_module", fmt.Sprintf("comet_%d", idx)))
|
||||
})
|
||||
// StopAndWait must be deferred before the error check,
|
||||
// as the nodes value may contain some successfully started instances.
|
||||
defer nodes.StopAndWait()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Now you can begin interacting with the nodes.
|
||||
// For the sake of this example, we'll just check
|
||||
// a couple simple properties of one node.
|
||||
fmt.Println(nodes[0].IsListening())
|
||||
fmt.Println(nodes[0].GenesisDoc().ChainID)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// example-basic
|
||||
}
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
circuittypes "cosmossdk.io/x/circuit/types"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
@ -31,7 +32,11 @@ func (app SimApp) RegisterUpgradeHandlers() {
|
||||
}
|
||||
|
||||
if upgradeInfo.Name == UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
|
||||
storeUpgrades := storetypes.StoreUpgrades{}
|
||||
storeUpgrades := storetypes.StoreUpgrades{
|
||||
Added: []string{
|
||||
circuittypes.ModuleName,
|
||||
},
|
||||
}
|
||||
|
||||
// configure store loader that checks if version == upgradeHeight and applies store upgrades
|
||||
app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user