wasmd/x/wasm/keeper/ante_test.go

314 lines
9.4 KiB
Go

package keeper_test
import (
"testing"
"time"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"cosmossdk.io/log"
"cosmossdk.io/store"
storemetrics "cosmossdk.io/store/metrics"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/CosmWasm/wasmd/x/wasm/keeper"
"github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
func TestCountTxDecorator(t *testing.T) {
keyWasm := storetypes.NewKVStoreKey(types.StoreKey)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics())
ms.MountStoreWithDB(keyWasm, storetypes.StoreTypeIAVL, db)
require.NoError(t, ms.LoadLatestVersion())
const myCurrentBlockHeight = 100
specs := map[string]struct {
setupDB func(t *testing.T, ctx sdk.Context)
simulate bool
nextAssertAnte func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error)
}{
"no initial counter set": {
setupDB: func(t *testing.T, ctx sdk.Context) {},
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
gotCounter, ok := types.TXCounter(ctx)
require.True(t, ok)
assert.Equal(t, uint32(0), gotCounter)
// and stored +1
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix)
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 0, 0, 0, 1}, bz)
return ctx, nil
},
},
"persistent counter incremented - big endian": {
setupDB: func(t *testing.T, ctx sdk.Context) {
bz := []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 1, 0, 0, 2}
ctx.MultiStore().GetKVStore(keyWasm).Set(types.TXCounterPrefix, bz)
},
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
gotCounter, ok := types.TXCounter(ctx)
require.True(t, ok)
assert.Equal(t, uint32(1<<24+2), gotCounter)
// and stored +1
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix)
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 1, 0, 0, 3}, bz)
return ctx, nil
},
},
"old height counter replaced": {
setupDB: func(t *testing.T, ctx sdk.Context) {
previousHeight := byte(myCurrentBlockHeight - 1)
bz := []byte{0, 0, 0, 0, 0, 0, 0, previousHeight, 0, 0, 0, 1}
ctx.MultiStore().GetKVStore(keyWasm).Set(types.TXCounterPrefix, bz)
},
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
gotCounter, ok := types.TXCounter(ctx)
require.True(t, ok)
assert.Equal(t, uint32(0), gotCounter)
// and stored +1
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix)
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 0, 0, 0, 1}, bz)
return ctx, nil
},
},
"simulation not persisted": {
setupDB: func(t *testing.T, ctx sdk.Context) {
},
simulate: true,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
_, ok := types.TXCounter(ctx)
assert.False(t, ok)
require.True(t, simulate)
// and not stored
assert.False(t, ctx.MultiStore().GetKVStore(keyWasm).Has(types.TXCounterPrefix))
return ctx, nil
},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
ctx := sdk.NewContext(ms.CacheMultiStore(), cmtproto.Header{
Height: myCurrentBlockHeight,
Time: time.Date(2021, time.September, 27, 12, 0, 0, 0, time.UTC),
}, false, log.NewNopLogger())
spec.setupDB(t, ctx)
var anyTx sdk.Tx
// when
ante := keeper.NewCountTXDecorator(runtime.NewKVStoreService(keyWasm))
_, gotErr := ante.AnteHandle(ctx, anyTx, spec.simulate, spec.nextAssertAnte)
require.NoError(t, gotErr)
})
}
}
func TestLimitSimulationGasDecorator(t *testing.T) {
var (
hundred storetypes.Gas = 100
zero storetypes.Gas = 0
)
specs := map[string]struct {
customLimit *storetypes.Gas
consumeGas storetypes.Gas
maxBlockGas int64
simulation bool
expErr interface{}
}{
"custom limit set": {
customLimit: &hundred,
consumeGas: hundred + 1,
maxBlockGas: -1,
simulation: true,
expErr: storetypes.ErrorOutOfGas{Descriptor: "testing"},
},
"block limit set": {
maxBlockGas: 100,
consumeGas: hundred + 1,
simulation: true,
expErr: storetypes.ErrorOutOfGas{Descriptor: "testing"},
},
"no limits set": {
maxBlockGas: -1,
consumeGas: hundred + 1,
simulation: true,
},
"both limits set, custom applies": {
customLimit: &hundred,
consumeGas: hundred - 1,
maxBlockGas: 10,
simulation: true,
},
"not a simulation": {
customLimit: &hundred,
consumeGas: hundred + 1,
simulation: false,
},
"zero custom limit": {
customLimit: &zero,
simulation: true,
expErr: "gas limit must not be zero",
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
nextAnte := consumeGasAnteHandler(spec.consumeGas)
ctx := sdk.Context{}.
WithGasMeter(storetypes.NewInfiniteGasMeter()).
WithConsensusParams(cmtproto.ConsensusParams{
Block: &cmtproto.BlockParams{MaxGas: spec.maxBlockGas},
})
// when
if spec.expErr != nil {
require.PanicsWithValue(t, spec.expErr, func() {
ante := keeper.NewLimitSimulationGasDecorator(spec.customLimit)
_, err := ante.AnteHandle(ctx, nil, spec.simulation, nextAnte)
require.NoError(t, err)
})
return
}
ante := keeper.NewLimitSimulationGasDecorator(spec.customLimit)
_, err := ante.AnteHandle(ctx, nil, spec.simulation, nextAnte)
require.NoError(t, err)
})
}
}
func consumeGasAnteHandler(gasToConsume storetypes.Gas) sdk.AnteHandler {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
ctx.GasMeter().ConsumeGas(gasToConsume, "testing")
return ctx, nil
}
}
func TestGasRegisterDecorator(t *testing.T) {
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics())
specs := map[string]struct {
simulate bool
nextAssertAnte func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error)
}{
"simulation": {
simulate: true,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
_, ok := types.GasRegisterFromContext(ctx)
assert.True(t, ok)
require.True(t, simulate)
return ctx, nil
},
},
"not simulation": {
simulate: false,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
_, ok := types.GasRegisterFromContext(ctx)
assert.True(t, ok)
require.False(t, simulate)
return ctx, nil
},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
ctx := sdk.NewContext(ms, cmtproto.Header{
Height: 100,
Time: time.Now(),
}, false, log.NewNopLogger())
var anyTx sdk.Tx
// when
ante := keeper.NewGasRegisterDecorator(&wasmtesting.MockGasRegister{})
_, gotErr := ante.AnteHandle(ctx, anyTx, spec.simulate, spec.nextAssertAnte)
// then
require.NoError(t, gotErr)
})
}
}
func TestTxContractsDecorator(t *testing.T) {
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db, log.NewTestLogger(t), storemetrics.NewNoOpMetrics())
specs := map[string]struct {
empty bool
simulate bool
nextAssertAnte func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error)
}{
"simulation - empty tx contracts": {
empty: true,
simulate: true,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
txContracts, ok := types.TxContractsFromContext(ctx)
assert.True(t, ok)
require.True(t, simulate)
require.Empty(t, txContracts.GetContracts())
return ctx, nil
},
},
"not simulation - empty tx contracts": {
empty: true,
simulate: false,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
txContracts, ok := types.TxContractsFromContext(ctx)
assert.True(t, ok)
require.False(t, simulate)
require.Empty(t, txContracts.GetContracts())
return ctx, nil
},
},
"simulation - not empty tx contracts": {
empty: false,
simulate: true,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
txContracts, ok := types.TxContractsFromContext(ctx)
assert.True(t, ok)
require.True(t, simulate)
require.Empty(t, txContracts.GetContracts())
return ctx, nil
},
},
"not simulation - not empty tx contracts": {
empty: false,
simulate: false,
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
txContracts, ok := types.TxContractsFromContext(ctx)
assert.True(t, ok)
require.False(t, simulate)
require.Empty(t, txContracts.GetContracts())
return ctx, nil
},
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
ctx := sdk.NewContext(ms, cmtproto.Header{
Height: 100,
Time: time.Now(),
}, false, log.NewNopLogger())
if !spec.empty {
contracts := types.NewTxContracts()
contracts.AddContract([]byte("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5"))
ctx = types.WithTxContracts(ctx, contracts)
}
var anyTx sdk.Tx
// when
ante := keeper.NewTxContractsDecorator()
_, gotErr := ante.AnteHandle(ctx, anyTx, spec.simulate, spec.nextAssertAnte)
// then
require.NoError(t, gotErr)
})
}
}