373 lines
13 KiB
Go
373 lines
13 KiB
Go
package simapp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
|
|
abci "github.com/cometbft/cometbft/abci/types"
|
|
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
|
dbm "github.com/cosmos/cosmos-db"
|
|
"github.com/cosmos/gogoproto/proto"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"cosmossdk.io/core/address"
|
|
"cosmossdk.io/core/appmodule"
|
|
"cosmossdk.io/depinject"
|
|
"cosmossdk.io/log"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
"github.com/cosmos/cosmos-sdk/runtime"
|
|
"github.com/cosmos/cosmos-sdk/testutil/mock"
|
|
"github.com/cosmos/cosmos-sdk/testutil/network"
|
|
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/types/module"
|
|
"github.com/cosmos/cosmos-sdk/types/msgservice"
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
|
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
|
"github.com/cosmos/cosmos-sdk/x/authz"
|
|
authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module"
|
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
"github.com/cosmos/cosmos-sdk/x/distribution"
|
|
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
|
"github.com/cosmos/cosmos-sdk/x/epochs"
|
|
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
|
|
"github.com/cosmos/cosmos-sdk/x/evidence"
|
|
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
|
"github.com/cosmos/cosmos-sdk/x/feegrant"
|
|
feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module"
|
|
"github.com/cosmos/cosmos-sdk/x/genutil"
|
|
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
|
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
|
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
|
"github.com/cosmos/cosmos-sdk/x/protocolpool"
|
|
protocolpooltypes "github.com/cosmos/cosmos-sdk/x/protocolpool/types"
|
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
|
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
|
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
"github.com/cosmos/cosmos-sdk/x/upgrade"
|
|
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
|
|
)
|
|
|
|
func TestSimAppExportAndBlockedAddrs(t *testing.T) {
|
|
db := dbm.NewMemDB()
|
|
logger := log.NewTestLogger(t)
|
|
app := NewSimappWithCustomOptions(t, false, SetupOptions{
|
|
Logger: logger.With("instance", "first"),
|
|
DB: db,
|
|
AppOpts: simtestutil.NewAppOptionsWithFlagHome(t.TempDir()),
|
|
})
|
|
|
|
// BlockedAddresses returns a map of addresses in app v1 and a map of modules name in app di.
|
|
for acc := range BlockedAddresses() {
|
|
var addr sdk.AccAddress
|
|
if modAddr, err := sdk.AccAddressFromBech32(acc); err == nil {
|
|
addr = modAddr
|
|
} else {
|
|
addr = app.AccountKeeper.GetModuleAddress(acc)
|
|
}
|
|
|
|
require.True(
|
|
t,
|
|
app.BankKeeper.BlockedAddr(addr),
|
|
fmt.Sprintf("ensure that blocked addresses are properly set in bank keeper: %s should be blocked", acc),
|
|
)
|
|
}
|
|
|
|
// finalize block so we have CheckTx state set
|
|
_, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{
|
|
Height: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = app.Commit()
|
|
require.NoError(t, err)
|
|
|
|
// Making a new app object with the db, so that initchain hasn't been called
|
|
app2 := NewSimApp(logger.With("instance", "second"), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()))
|
|
_, err = app2.ExportAppStateAndValidators(false, []string{}, []string{})
|
|
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
|
|
}
|
|
|
|
func TestRunMigrations(t *testing.T) {
|
|
db := dbm.NewMemDB()
|
|
logger := log.NewTestLogger(t)
|
|
app := NewSimApp(logger.With("instance", "simapp"), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()))
|
|
|
|
// Create a new baseapp and configurator for the purpose of this test.
|
|
bApp := baseapp.NewBaseApp(app.Name(), logger.With("instance", "baseapp"), db, app.TxConfig().TxDecoder())
|
|
bApp.SetCommitMultiStoreTracer(nil)
|
|
bApp.SetInterfaceRegistry(app.InterfaceRegistry())
|
|
app.BaseApp = bApp
|
|
configurator := module.NewConfigurator(app.appCodec, bApp.MsgServiceRouter(), app.GRPCQueryRouter())
|
|
|
|
// We register all modules on the Configurator, except x/bank. x/bank will
|
|
// serve as the test subject on which we run the migration tests.
|
|
//
|
|
// The loop below is the same as calling `RegisterServices` on
|
|
// ModuleManager, except that we skip x/bank.
|
|
for name, mod := range app.ModuleManager.Modules {
|
|
if name == banktypes.ModuleName {
|
|
continue
|
|
}
|
|
|
|
if mod, ok := mod.(module.HasServices); ok {
|
|
mod.RegisterServices(configurator)
|
|
}
|
|
|
|
if mod, ok := mod.(appmodule.HasServices); ok {
|
|
err := mod.RegisterServices(configurator)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
require.NoError(t, configurator.Error())
|
|
}
|
|
|
|
// Initialize the chain
|
|
_, err := app.InitChain(&abci.RequestInitChain{})
|
|
require.NoError(t, err)
|
|
_, err = app.Commit()
|
|
require.NoError(t, err)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
moduleName string
|
|
fromVersion uint64
|
|
toVersion uint64
|
|
expRegErr bool // errors while registering migration
|
|
expRegErrMsg string
|
|
expRunErr bool // errors while running migration
|
|
expRunErrMsg string
|
|
expCalled int
|
|
}{
|
|
{
|
|
"cannot register migration for version 0",
|
|
"bank", 0, 1,
|
|
true, "module migration versions should start at 1: invalid version", false, "", 0,
|
|
},
|
|
{
|
|
"throws error on RunMigrations if no migration registered for bank",
|
|
"", 1, 2,
|
|
false, "", true, "no migrations found for module bank: not found", 0,
|
|
},
|
|
{
|
|
"can register 1->2 migration handler for x/bank, cannot run migration",
|
|
"bank", 1, 2,
|
|
false, "", true, "no migration found for module bank from version 2 to version 3: not found", 0,
|
|
},
|
|
{
|
|
"can register 2->3 migration handler for x/bank, can run migration",
|
|
"bank", 2, bank.AppModule{}.ConsensusVersion(),
|
|
false, "", false, "", int(bank.AppModule{}.ConsensusVersion() - 2), // minus 2 because 1-2 is run in the previous test case.
|
|
},
|
|
{
|
|
"cannot register migration handler for same module & fromVersion",
|
|
"bank", 1, 2,
|
|
true, "another migration for module bank and version 1 already exists: internal logic error", false, "", 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(tt *testing.T) {
|
|
var err error
|
|
|
|
// Since it's very hard to test actual in-place store migrations in
|
|
// tests (due to the difficulty of maintaining multiple versions of a
|
|
// module), we're just testing here that the migration logic is
|
|
// called.
|
|
called := 0
|
|
|
|
if tc.moduleName != "" {
|
|
for i := tc.fromVersion; i < tc.toVersion; i++ {
|
|
// Register migration for module from version `fromVersion` to `fromVersion+1`.
|
|
tt.Logf("Registering migration for %q v%d", tc.moduleName, i)
|
|
err = configurator.RegisterMigration(tc.moduleName, i, func(sdk.Context) error {
|
|
called++
|
|
|
|
return nil
|
|
})
|
|
|
|
if tc.expRegErr {
|
|
require.EqualError(tt, err, tc.expRegErrMsg)
|
|
|
|
return
|
|
}
|
|
require.NoError(tt, err, "registering migration")
|
|
}
|
|
}
|
|
|
|
// Run migrations only for bank. That's why we put the initial
|
|
// version for bank as 1, and for all other modules, we put as
|
|
// their latest ConsensusVersion.
|
|
_, err = app.ModuleManager.RunMigrations(
|
|
app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()}), configurator,
|
|
module.VersionMap{
|
|
banktypes.ModuleName: 1,
|
|
authtypes.ModuleName: auth.AppModule{}.ConsensusVersion(),
|
|
authz.ModuleName: authzmodule.AppModule{}.ConsensusVersion(),
|
|
stakingtypes.ModuleName: staking.AppModule{}.ConsensusVersion(),
|
|
minttypes.ModuleName: mint.AppModule{}.ConsensusVersion(),
|
|
distrtypes.ModuleName: distribution.AppModule{}.ConsensusVersion(),
|
|
slashingtypes.ModuleName: slashing.AppModule{}.ConsensusVersion(),
|
|
govtypes.ModuleName: gov.AppModule{}.ConsensusVersion(),
|
|
upgradetypes.ModuleName: upgrade.AppModule{}.ConsensusVersion(),
|
|
vestingtypes.ModuleName: vesting.AppModule{}.ConsensusVersion(),
|
|
feegrant.ModuleName: feegrantmodule.AppModule{}.ConsensusVersion(),
|
|
evidencetypes.ModuleName: evidence.AppModule{}.ConsensusVersion(),
|
|
genutiltypes.ModuleName: genutil.AppModule{}.ConsensusVersion(),
|
|
epochstypes.ModuleName: epochs.AppModule{}.ConsensusVersion(),
|
|
protocolpooltypes.ModuleName: protocolpool.AppModule{}.ConsensusVersion(),
|
|
},
|
|
)
|
|
if tc.expRunErr {
|
|
require.EqualError(tt, err, tc.expRunErrMsg, "running migration")
|
|
} else {
|
|
require.NoError(tt, err, "running migration")
|
|
// Make sure bank's migration is called.
|
|
require.Equal(tt, tc.expCalled, called)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInitGenesisOnMigration(t *testing.T) {
|
|
db := dbm.NewMemDB()
|
|
app := NewSimApp(log.NewTestLogger(t), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()))
|
|
ctx := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
|
|
|
|
// Create a mock module. This module will serve as the new module we're
|
|
// adding during a migration.
|
|
mockCtrl := gomock.NewController(t)
|
|
t.Cleanup(mockCtrl.Finish)
|
|
mockModule := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
|
|
mockDefaultGenesis := json.RawMessage(`{"key": "value"}`)
|
|
mockModule.EXPECT().DefaultGenesis(gomock.Eq(app.appCodec)).Times(1).Return(mockDefaultGenesis)
|
|
mockModule.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(app.appCodec), gomock.Eq(mockDefaultGenesis)).Times(1)
|
|
mockModule.EXPECT().ConsensusVersion().Times(1).Return(uint64(0))
|
|
|
|
app.ModuleManager.Modules["mock"] = mockModule
|
|
|
|
// Run migrations only for "mock" module. We exclude it from
|
|
// the VersionMap to simulate upgrading with a new module.
|
|
_, err := app.ModuleManager.RunMigrations(ctx, app.Configurator(),
|
|
module.VersionMap{
|
|
"bank": bank.AppModule{}.ConsensusVersion(),
|
|
"auth": auth.AppModule{}.ConsensusVersion(),
|
|
"authz": authzmodule.AppModule{}.ConsensusVersion(),
|
|
"staking": staking.AppModule{}.ConsensusVersion(),
|
|
"mint": mint.AppModule{}.ConsensusVersion(),
|
|
"distribution": distribution.AppModule{}.ConsensusVersion(),
|
|
"slashing": slashing.AppModule{}.ConsensusVersion(),
|
|
"gov": gov.AppModule{}.ConsensusVersion(),
|
|
"upgrade": upgrade.AppModule{}.ConsensusVersion(),
|
|
"vesting": vesting.AppModule{}.ConsensusVersion(),
|
|
"feegrant": feegrantmodule.AppModule{}.ConsensusVersion(),
|
|
"evidence": evidence.AppModule{}.ConsensusVersion(),
|
|
"genutil": genutil.AppModule{}.ConsensusVersion(),
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestUpgradeStateOnGenesis(t *testing.T) {
|
|
db := dbm.NewMemDB()
|
|
app := NewSimappWithCustomOptions(t, false, SetupOptions{
|
|
Logger: log.NewTestLogger(t),
|
|
DB: db,
|
|
AppOpts: simtestutil.NewAppOptionsWithFlagHome(t.TempDir()),
|
|
})
|
|
|
|
// make sure the upgrade keeper has version map in state
|
|
ctx := app.NewContext(false)
|
|
vm, err := app.UpgradeKeeper.GetModuleVersionMap(ctx)
|
|
require.NoError(t, err)
|
|
for v, i := range app.ModuleManager.Modules {
|
|
if i, ok := i.(module.HasConsensusVersion); ok {
|
|
require.Equal(t, vm[v], i.ConsensusVersion())
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, app.UpgradeKeeper.GetVersionSetter())
|
|
}
|
|
|
|
// TestMergedRegistry tests that fetching the gogo/protov2 merged registry
|
|
// doesn't fail after loading all file descriptors.
|
|
func TestMergedRegistry(t *testing.T) {
|
|
r, err := proto.MergedRegistry()
|
|
require.NoError(t, err)
|
|
require.Greater(t, r.NumFiles(), 0)
|
|
}
|
|
|
|
func TestProtoAnnotations(t *testing.T) {
|
|
r, err := proto.MergedRegistry()
|
|
require.NoError(t, err)
|
|
err = msgservice.ValidateProtoAnnotations(r)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
var _ address.Codec = (*customAddressCodec)(nil)
|
|
|
|
type customAddressCodec struct{}
|
|
|
|
func (c customAddressCodec) StringToBytes(text string) ([]byte, error) {
|
|
return []byte(text), nil
|
|
}
|
|
|
|
func (c customAddressCodec) BytesToString(bz []byte) (string, error) {
|
|
return string(bz), nil
|
|
}
|
|
|
|
func TestAddressCodecFactory(t *testing.T) {
|
|
var addrCodec address.Codec
|
|
var valAddressCodec runtime.ValidatorAddressCodec
|
|
var consAddressCodec runtime.ConsensusAddressCodec
|
|
|
|
err := depinject.Inject(
|
|
depinject.Configs(
|
|
network.MinimumAppConfig(),
|
|
depinject.Supply(log.NewNopLogger()),
|
|
),
|
|
&addrCodec, &valAddressCodec, &consAddressCodec)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, addrCodec)
|
|
_, ok := addrCodec.(customAddressCodec)
|
|
require.False(t, ok)
|
|
require.NotNil(t, valAddressCodec)
|
|
_, ok = valAddressCodec.(customAddressCodec)
|
|
require.False(t, ok)
|
|
require.NotNil(t, consAddressCodec)
|
|
_, ok = consAddressCodec.(customAddressCodec)
|
|
require.False(t, ok)
|
|
|
|
// Set the address codec to the custom one
|
|
err = depinject.Inject(
|
|
depinject.Configs(
|
|
network.MinimumAppConfig(),
|
|
depinject.Supply(
|
|
log.NewNopLogger(),
|
|
func() address.Codec { return customAddressCodec{} },
|
|
func() runtime.ValidatorAddressCodec { return customAddressCodec{} },
|
|
func() runtime.ConsensusAddressCodec { return customAddressCodec{} },
|
|
),
|
|
),
|
|
&addrCodec, &valAddressCodec, &consAddressCodec)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, addrCodec)
|
|
_, ok = addrCodec.(customAddressCodec)
|
|
require.True(t, ok)
|
|
require.NotNil(t, valAddressCodec)
|
|
_, ok = valAddressCodec.(customAddressCodec)
|
|
require.True(t, ok)
|
|
require.NotNil(t, consAddressCodec)
|
|
_, ok = consAddressCodec.(customAddressCodec)
|
|
require.True(t, ok)
|
|
}
|