chore(simapp): audit (backport #16914) (#16922)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2023-07-11 17:22:30 +02:00 committed by GitHub
parent bf24d366fd
commit e292bdab10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 6 additions and 460 deletions

View File

@ -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.

View File

@ -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))
}

View File

@ -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))
})
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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))