cosmos-sdk/baseapp/utils_test.go
2025-08-29 15:58:04 -04:00

446 lines
13 KiB
Go

package baseapp_test
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"net/url"
"reflect"
"strconv"
"testing"
"unsafe"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
cmttypes "github.com/cometbft/cometbft/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/stretchr/testify/require"
runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appconfig"
"cosmossdk.io/depinject"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/baseapp/state"
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil/mock"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
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"
_ "github.com/cosmos/cosmos-sdk/x/params"
_ "github.com/cosmos/cosmos-sdk/x/staking"
)
var ParamStoreKey = []byte("paramstore")
// 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 := cmttypes.NewValidator(pubKey, 1)
valSet := cmttypes.NewValidatorSet([]*cmttypes.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, math.NewInt(100000000000000))),
},
}
genesisState := builder.DefaultGenesis()
// sus
genesisState, err = simtestutil.GenesisStateWithValSet(codec, genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...)
require.NoError(t, err)
return genesisState
}
func makeMinimalConfig() depinject.Config {
var (
mempoolOpt = baseapp.SetMempool(mempool.NewSenderNonceMempool())
addressCodec = func() address.Codec { return addresscodec.NewBech32Codec("cosmos") }
validatorAddressCodec = func() runtime.ValidatorAddressCodec { return addresscodec.NewBech32Codec("cosmosvaloper") }
consensusAddressCodec = func() runtime.ConsensusAddressCodec { return addresscodec.NewBech32Codec("cosmosvalcons") }
)
return depinject.Configs(
depinject.Supply(mempoolOpt, addressCodec, validatorAddressCodec, consensusAddressCodec),
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 mockCounterServer struct {
incrementCounterFn func(context.Context, *baseapptestutil.MsgCounter) (*baseapptestutil.MsgCreateCounterResponse, error)
}
func (m mockCounterServer) IncrementCounter(ctx context.Context, req *baseapptestutil.MsgCounter) (*baseapptestutil.MsgCreateCounterResponse, error) {
if m.incrementCounterFn == nil {
panic("not expected to be called")
}
return m.incrementCounterFn(ctx, req)
}
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) {
t.Helper()
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, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "message handler failure")
}
msgCount = m.Counter
case *baseapptestutil.MsgCounter2:
if m.FailOnHandler {
return nil, errorsmod.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 {
t.Helper()
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, errorsmod.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 storetypes.KVStore, counterKey []byte, counter int64) (*sdk.Result, error) {
t.Helper()
storedCounter := getIntFromStore(t, store, counterKey)
require.Equal(t, storedCounter, counter)
setIntOnStore(store, counterKey, counter+1)
return &sdk.Result{}, nil
}
func setIntOnStore(store storetypes.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
}
var _ baseapp.ParamStore = (*paramStore)(nil)
func (ps paramStore) Set(_ context.Context, value cmtproto.ConsensusParams) error {
bz, err := json.Marshal(value)
if err != nil {
return err
}
return ps.db.Set(ParamStoreKey, bz)
}
func (ps paramStore) Has(_ context.Context) (bool, error) {
return ps.db.Has(ParamStoreKey)
}
func (ps paramStore) Get(_ context.Context) (cmtproto.ConsensusParams, error) {
bz, err := ps.db.Get(ParamStoreKey)
if err != nil {
return cmtproto.ConsensusParams{}, err
}
if len(bz) == 0 {
return cmtproto.ConsensusParams{}, errors.New("params not found")
}
var params cmtproto.ConsensusParams
if err := json.Unmarshal(bz, &params); err != nil {
return cmtproto.ConsensusParams{}, err
}
return params, nil
}
func setTxSignature(t *testing.T, builder client.TxBuilder, nonce uint64) {
t.Helper()
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) {
t.Helper()
lastHeight := app.LastBlockHeight()
lastID := app.LastCommitID()
require.Equal(t, expectedHeight, lastHeight)
require.Equal(t, expectedID, lastID)
}
// note: we use reflection in this test because we do not want to make the baseapp state publicly available
func reflectGetStateCtx(app *baseapp.BaseApp, mode sdk.ExecMode) sdk.Context {
v := reflect.ValueOf(app).Elem()
f := v.FieldByName("stateManager")
rf := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
arg := reflect.ValueOf(mode)
st := rf.MethodByName("GetState").Call([]reflect.Value{arg})[0].Interface().(*state.State)
return st.Context()
}
func getCheckStateCtx(app *baseapp.BaseApp) sdk.Context {
return reflectGetStateCtx(app, sdk.ExecModeCheck)
}
func getFinalizeBlockStateCtx(app *baseapp.BaseApp) sdk.Context {
return reflectGetStateCtx(app, sdk.ExecModeFinalize)
}
func parseTxMemo(t *testing.T, tx sdk.Tx) (counter int64, failOnAnte bool) {
t.Helper()
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 {
t.Helper()
_, _, addr := testdata.KeyTestPubAddr()
msgs := make([]sdk.Msg, 0, len(msgCounters))
for _, c := range msgCounters {
msg := &baseapptestutil.MsgCounter{Counter: c, FailOnHandler: false, Signer: addr.String()}
msgs = append(msgs, msg)
}
builder := cfg.NewTxBuilder()
require.NoError(t, 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 storetypes.KVStore, key []byte) int64 {
t.Helper()
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 {
t.Helper()
builder := cfg.NewTxBuilder()
require.NoError(t, 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,
}
}
if err := builder.SetMsgs(msgs...); err != nil {
panic(err)
}
return builder.GetTx()
}
// wonkyMsg is to be used to run a MsgCounter2 message when the MsgCounter2 handler is not registered.
func wonkyMsg(t *testing.T, cfg client.TxConfig, tx signing.Tx) signing.Tx {
t.Helper()
t.Helper()
builder := cfg.NewTxBuilder()
builder.SetMemo(tx.GetMemo())
msgs := tx.GetMsgs()
msgs = append(msgs, &baseapptestutil.MsgCounter2{})
err := builder.SetMsgs(msgs...)
require.NoError(t, err)
return builder.GetTx()
}