483 lines
13 KiB
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(¶msmodulev1.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, ¶ms); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return ¶ms, 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()
|
|
}
|