cosmos-sdk/baseapp/utils_test.go

483 lines
13 KiB
Go

package baseapp_test
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
"reflect"
"strconv"
"testing"
"unsafe"
runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
consensusmodulev1 "cosmossdk.io/api/cosmos/consensus/module/v1"
mintmodulev1 "cosmossdk.io/api/cosmos/mint/module/v1"
paramsmodulev1 "cosmossdk.io/api/cosmos/params/module/v1"
stakingmodulev1 "cosmossdk.io/api/cosmos/staking/module/v1"
txconfigv1 "cosmossdk.io/api/cosmos/tx/config/v1"
"cosmossdk.io/core/appconfig"
"cosmossdk.io/depinject"
dbm "github.com/cometbft/cometbft-db"
"github.com/cometbft/cometbft/libs/log"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
tmtypes "github.com/cometbft/cometbft/types"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/baseapp"
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/runtime"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cosmos/cosmos-sdk/testutil/mock"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/mempool"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
_ "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
_ "github.com/cosmos/cosmos-sdk/x/bank"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
_ "github.com/cosmos/cosmos-sdk/x/consensus"
_ "github.com/cosmos/cosmos-sdk/x/mint"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
_ "github.com/cosmos/cosmos-sdk/x/params"
_ "github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
var (
ParamStoreKey = []byte("paramstore")
)
func defaultLogger() log.Logger {
if testing.Verbose() {
return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "baseapp/test")
}
return log.NewNopLogger()
}
// GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts
// that also act as delegators.
func GenesisStateWithSingleValidator(t *testing.T, codec codec.Codec, builder *runtime.AppBuilder) map[string]json.RawMessage {
t.Helper()
privVal := mock.NewPV()
pubKey, err := privVal.GetPubKey()
require.NoError(t, err)
// create validator set with single validator
validator := tmtypes.NewValidator(pubKey, 1)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
// generate genesis account
senderPrivKey := secp256k1.GenPrivKey()
acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0)
balances := []banktypes.Balance{
{
Address: acc.GetAddress().String(),
Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))),
},
}
genesisState := builder.DefaultGenesis()
// sus
genesisState, err = simtestutil.GenesisStateWithValSet(codec, genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...)
require.NoError(t, err)
return genesisState
}
func makeTestConfig() depinject.Config {
return appconfig.Compose(&appv1alpha1.Config{
Modules: []*appv1alpha1.ModuleConfig{
{
Name: "runtime",
Config: appconfig.WrapAny(&runtimev1alpha1.Module{
AppName: "BaseAppApp",
BeginBlockers: []string{
"mint",
"staking",
"auth",
"bank",
"params",
"consensus",
},
EndBlockers: []string{
"staking",
"auth",
"bank",
"mint",
"params",
"consensus",
},
OverrideStoreKeys: []*runtimev1alpha1.StoreKeyConfig{
{
ModuleName: "auth",
KvStoreKey: "acc",
},
},
InitGenesis: []string{
"auth",
"bank",
"staking",
"mint",
"params",
"consensus",
},
}),
},
{
Name: "auth",
Config: appconfig.WrapAny(&authmodulev1.Module{
Bech32Prefix: "cosmos",
ModuleAccountPermissions: []*authmodulev1.ModuleAccountPermission{
{Account: authtypes.FeeCollectorName},
{Account: minttypes.ModuleName, Permissions: []string{authtypes.Minter}},
{Account: stakingtypes.BondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}},
{Account: stakingtypes.NotBondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}},
},
}),
},
{
Name: "bank",
Config: appconfig.WrapAny(&bankmodulev1.Module{}),
},
{
Name: "params",
Config: appconfig.WrapAny(&paramsmodulev1.Module{}),
},
{
Name: "staking",
Config: appconfig.WrapAny(&stakingmodulev1.Module{}),
},
{
Name: "mint",
Config: appconfig.WrapAny(&mintmodulev1.Module{}),
},
{
Name: "consensus",
Config: appconfig.WrapAny(&consensusmodulev1.Module{}),
},
{
Name: "tx",
Config: appconfig.WrapAny(&txconfigv1.Config{}),
},
},
})
}
func makeMinimalConfig() depinject.Config {
var mempoolOpt runtime.BaseAppOption = baseapp.SetMempool(mempool.NewSenderNonceMempool())
return depinject.Configs(
depinject.Supply(mempoolOpt),
appconfig.Compose(&appv1alpha1.Config{
Modules: []*appv1alpha1.ModuleConfig{
{
Name: "runtime",
Config: appconfig.WrapAny(&runtimev1alpha1.Module{
AppName: "BaseAppApp",
}),
},
},
}))
}
type MsgKeyValueImpl struct{}
func (m MsgKeyValueImpl) Set(ctx context.Context, msg *baseapptestutil.MsgKeyValue) (*baseapptestutil.MsgCreateKeyValueResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.KVStore(capKey2).Set(msg.Key, msg.Value)
return &baseapptestutil.MsgCreateKeyValueResponse{}, nil
}
type CounterServerImplGasMeterOnly struct {
gas uint64
}
func (m CounterServerImplGasMeterOnly) IncrementCounter(ctx context.Context, msg *baseapptestutil.MsgCounter) (*baseapptestutil.MsgCreateCounterResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
gas := m.gas
// if no gas is provided, use the counter as gas. This is useful for testing
if gas == 0 {
gas = uint64(msg.Counter)
}
sdkCtx.GasMeter().ConsumeGas(gas, "test")
return &baseapptestutil.MsgCreateCounterResponse{}, nil
}
type NoopCounterServerImpl struct{}
func (m NoopCounterServerImpl) IncrementCounter(
_ context.Context,
_ *baseapptestutil.MsgCounter,
) (*baseapptestutil.MsgCreateCounterResponse, error) {
return &baseapptestutil.MsgCreateCounterResponse{}, nil
}
type CounterServerImpl struct {
t *testing.T
capKey storetypes.StoreKey
deliverKey []byte
}
func (m CounterServerImpl) IncrementCounter(ctx context.Context, msg *baseapptestutil.MsgCounter) (*baseapptestutil.MsgCreateCounterResponse, error) {
return incrementCounter(ctx, m.t, m.capKey, m.deliverKey, msg)
}
type Counter2ServerImpl struct {
t *testing.T
capKey storetypes.StoreKey
deliverKey []byte
}
func (m Counter2ServerImpl) IncrementCounter(ctx context.Context, msg *baseapptestutil.MsgCounter2) (*baseapptestutil.MsgCreateCounterResponse, error) {
return incrementCounter(ctx, m.t, m.capKey, m.deliverKey, msg)
}
func incrementCounter(ctx context.Context,
t *testing.T,
capKey storetypes.StoreKey,
deliverKey []byte,
msg sdk.Msg,
) (*baseapptestutil.MsgCreateCounterResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
store := sdkCtx.KVStore(capKey)
sdkCtx.GasMeter().ConsumeGas(5, "test")
var msgCount int64
switch m := msg.(type) {
case *baseapptestutil.MsgCounter:
if m.FailOnHandler {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "message handler failure")
}
msgCount = m.Counter
case *baseapptestutil.MsgCounter2:
if m.FailOnHandler {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "message handler failure")
}
msgCount = m.Counter
}
sdkCtx.EventManager().EmitEvents(
counterEvent(sdk.EventTypeMessage, msgCount),
)
_, err := incrementingCounter(t, store, deliverKey, msgCount)
if err != nil {
return nil, err
}
return &baseapptestutil.MsgCreateCounterResponse{}, nil
}
func counterEvent(evType string, msgCount int64) sdk.Events {
return sdk.Events{
sdk.NewEvent(
evType,
sdk.NewAttribute("update_counter", fmt.Sprintf("%d", msgCount)),
),
}
}
func anteHandlerTxTest(t *testing.T, capKey storetypes.StoreKey, storeKey []byte) sdk.AnteHandler {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
store := ctx.KVStore(capKey)
counter, failOnAnte := parseTxMemo(t, tx)
if failOnAnte {
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure")
}
_, err := incrementingCounter(t, store, storeKey, counter)
if err != nil {
return ctx, err
}
ctx.EventManager().EmitEvents(
counterEvent("ante_handler", counter),
)
ctx = ctx.WithPriority(testTxPriority)
return ctx, nil
}
}
func incrementingCounter(t *testing.T, store sdk.KVStore, counterKey []byte, counter int64) (*sdk.Result, error) {
storedCounter := getIntFromStore(t, store, counterKey)
require.Equal(t, storedCounter, counter)
setIntOnStore(store, counterKey, counter+1)
return &sdk.Result{}, nil
}
func setIntOnStore(store sdk.KVStore, key []byte, i int64) {
bz := make([]byte, 8)
n := binary.PutVarint(bz, i)
store.Set(key, bz[:n])
}
type paramStore struct {
db *dbm.MemDB
}
func (ps *paramStore) Set(_ sdk.Context, value *tmproto.ConsensusParams) {
bz, err := json.Marshal(value)
if err != nil {
panic(err)
}
ps.db.Set(ParamStoreKey, bz)
}
func (ps *paramStore) Has(_ sdk.Context) bool {
ok, err := ps.db.Has(ParamStoreKey)
if err != nil {
panic(err)
}
return ok
}
func (ps paramStore) Get(ctx sdk.Context) (*tmproto.ConsensusParams, error) {
bz, err := ps.db.Get(ParamStoreKey)
if err != nil {
panic(err)
}
if len(bz) == 0 {
return nil, errors.New("params not found")
}
var params tmproto.ConsensusParams
if err := json.Unmarshal(bz, &params); err != nil {
panic(err)
}
return &params, nil
}
func setTxSignature(t *testing.T, builder client.TxBuilder, nonce uint64) {
privKey := secp256k1.GenPrivKeyFromSecret([]byte("test"))
pubKey := privKey.PubKey()
err := builder.SetSignatures(
signingtypes.SignatureV2{
PubKey: pubKey,
Sequence: nonce,
Data: &signingtypes.SingleSignatureData{},
},
)
require.NoError(t, err)
}
func testLoadVersionHelper(t *testing.T, app *baseapp.BaseApp, expectedHeight int64, expectedID storetypes.CommitID) {
lastHeight := app.LastBlockHeight()
lastID := app.LastCommitID()
require.Equal(t, expectedHeight, lastHeight)
require.Equal(t, expectedID, lastID)
}
func getCheckStateCtx(app *baseapp.BaseApp) sdk.Context {
v := reflect.ValueOf(app).Elem()
f := v.FieldByName("checkState")
rf := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
return rf.MethodByName("Context").Call(nil)[0].Interface().(sdk.Context)
}
func getDeliverStateCtx(app *baseapp.BaseApp) sdk.Context {
v := reflect.ValueOf(app).Elem()
f := v.FieldByName("deliverState")
rf := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
return rf.MethodByName("Context").Call(nil)[0].Interface().(sdk.Context)
}
func parseTxMemo(t *testing.T, tx sdk.Tx) (counter int64, failOnAnte bool) {
txWithMemo, ok := tx.(sdk.TxWithMemo)
require.True(t, ok)
memo := txWithMemo.GetMemo()
vals, err := url.ParseQuery(memo)
require.NoError(t, err)
counter, err = strconv.ParseInt(vals.Get("counter"), 10, 64)
require.NoError(t, err)
failOnAnte = vals.Get("failOnAnte") == "true"
return counter, failOnAnte
}
func newTxCounter(t *testing.T, cfg client.TxConfig, counter int64, msgCounters ...int64) signing.Tx {
msgs := make([]sdk.Msg, 0, len(msgCounters))
for _, c := range msgCounters {
msg := &baseapptestutil.MsgCounter{Counter: c, FailOnHandler: false}
msgs = append(msgs, msg)
}
builder := cfg.NewTxBuilder()
builder.SetMsgs(msgs...)
builder.SetMemo("counter=" + strconv.FormatInt(counter, 10) + "&failOnAnte=false")
setTxSignature(t, builder, uint64(counter))
return builder.GetTx()
}
func getIntFromStore(t *testing.T, store sdk.KVStore, key []byte) int64 {
bz := store.Get(key)
if len(bz) == 0 {
return 0
}
i, err := binary.ReadVarint(bytes.NewBuffer(bz))
require.NoError(t, err)
return i
}
func setFailOnAnte(t *testing.T, cfg client.TxConfig, tx signing.Tx, failOnAnte bool) signing.Tx {
builder := cfg.NewTxBuilder()
builder.SetMsgs(tx.GetMsgs()...)
memo := tx.GetMemo()
vals, err := url.ParseQuery(memo)
require.NoError(t, err)
vals.Set("failOnAnte", strconv.FormatBool(failOnAnte))
memo = vals.Encode()
builder.SetMemo(memo)
setTxSignature(t, builder, 1)
return builder.GetTx()
}
func setFailOnHandler(cfg client.TxConfig, tx signing.Tx, fail bool) signing.Tx {
builder := cfg.NewTxBuilder()
builder.SetMemo(tx.GetMemo())
msgs := tx.GetMsgs()
for i, msg := range msgs {
msgs[i] = &baseapptestutil.MsgCounter{
Counter: msg.(*baseapptestutil.MsgCounter).Counter,
FailOnHandler: fail,
}
}
builder.SetMsgs(msgs...)
return builder.GetTx()
}