Skip to content

Commit

Permalink
Cosmos Gas Multiplier Params (#1565)
Browse files Browse the repository at this point in the history
* Cosmos Gas Multiplier Params

* Update ante dep decorators to remove unnecessary dep

* bump sei-cosmos

* Bump sei-wasmd

* Update sei-cosmos to v0.2.83

---------

Co-authored-by: Uday Patil <[email protected]>
  • Loading branch information
Kbhat1 and udpatil authored Apr 19, 2024
1 parent 7648e26 commit fd7f6bc
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 300 deletions.
7 changes: 5 additions & 2 deletions app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ func NewAnteHandlerAndDepGenerator(options HandlerOptions) (sdk.AnteHandler, sdk
if options.AccessControlKeeper == nil {
return nil, nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "accesscontrol keeper is required for ante builder")
}
if options.ParamsKeeper == nil {
return nil, nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "params keeper is required for ante builder")
}
if options.TracingInfo == nil {
return nil, nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "tracing info is required for ante builder")
}
Expand All @@ -75,9 +78,9 @@ func NewAnteHandlerAndDepGenerator(options HandlerOptions) (sdk.AnteHandler, sdk
sequentialVerifyDecorator := ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler)

anteDecorators := []sdk.AnteFullDecorator{
sdk.CustomDepWrappedAnteDecorator(ante.NewSetUpContextDecorator(antedecorators.GetGasMeterSetter(*options.AccessControlKeeper)), depdecorators.GasMeterSetterDecorator{}), // outermost AnteDecorator. SetUpContext must be called first
sdk.DefaultWrappedAnteDecorator(ante.NewSetUpContextDecorator(antedecorators.GetGasMeterSetter(options.ParamsKeeper.(paramskeeper.Keeper)))), // outermost AnteDecorator. SetUpContext must be called first
antedecorators.NewGaslessDecorator([]sdk.AnteFullDecorator{ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.ParamsKeeper.(paramskeeper.Keeper), options.TxFeeChecker)}, *options.OracleKeeper),
sdk.DefaultWrappedAnteDecorator(wasmkeeper.NewLimitSimulationGasDecorator(options.WasmConfig.SimulationGasLimit)), // after setup context to enforce limits early
sdk.DefaultWrappedAnteDecorator(wasmkeeper.NewLimitSimulationGasDecorator(options.WasmConfig.SimulationGasLimit, antedecorators.GetGasMeterSetter(options.ParamsKeeper.(paramskeeper.Keeper)))), // after setup context to enforce limits early
sdk.DefaultWrappedAnteDecorator(ante.NewRejectExtensionOptionsDecorator()),
oracle.NewSpammingPreventionDecorator(*options.OracleKeeper),
oracle.NewOracleVoteAloneDecorator(),
Expand Down
24 changes: 0 additions & 24 deletions app/antedecorators/depdecorators/gas.go

This file was deleted.

74 changes: 8 additions & 66 deletions app/antedecorators/gas.go
Original file line number Diff line number Diff line change
@@ -1,82 +1,24 @@
package antedecorators

import (
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol"
aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper"
acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
)

const (
GasMultiplierNumerator uint64 = 1
DefaultGasMultiplierDenominator uint64 = 1
WasmCorrectDependencyDiscountDenominator uint64 = 2
)

func GetGasMeterSetter(aclkeeper aclkeeper.Keeper) func(bool, sdk.Context, uint64, sdk.Tx) sdk.Context {
func GetGasMeterSetter(pk paramskeeper.Keeper) func(bool, sdk.Context, uint64, sdk.Tx) sdk.Context {
return func(simulate bool, ctx sdk.Context, gasLimit uint64, tx sdk.Tx) sdk.Context {
if simulate || ctx.BlockHeight() == 0 {
if ctx.BlockHeight() == 0 {
return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
}

denominator := uint64(1)
updatedGasDenominator := false
for _, msg := range tx.GetMsgs() {
candidateDenominator := getMessageMultiplierDenominator(ctx, msg, aclkeeper)
if !updatedGasDenominator || candidateDenominator < denominator {
updatedGasDenominator = true
denominator = candidateDenominator
}
}
return ctx.WithGasMeter(types.NewMultiplierGasMeter(gasLimit, DefaultGasMultiplierDenominator, denominator))
}
}
cosmosGasParams := pk.GetCosmosGasParams(ctx)

func getMessageMultiplierDenominator(ctx sdk.Context, msg sdk.Msg, aclKeeper aclkeeper.Keeper) uint64 {
// TODO: reason through whether it's reasonable to require non-* identifier for all operations
// under the context of inter-contract changes
// only give gas discount if none of the dependency (except COMMIT) has id "*"
if wasmExecuteMsg, ok := msg.(*wasmtypes.MsgExecuteContract); ok {
msgInfo, err := acltypes.NewExecuteMessageInfo(wasmExecuteMsg.Msg)
if err != nil {
return DefaultGasMultiplierDenominator
}
if messageContainsNoWildcardDependencies(
ctx,
aclKeeper,
wasmExecuteMsg.Contract,
msgInfo,
wasmExecuteMsg.Sender,
) {
return WasmCorrectDependencyDiscountDenominator
// In simulation, still use multiplier but with infinite gas limit
if simulate {
return ctx.WithGasMeter(types.NewInfiniteMultiplierGasMeter(cosmosGasParams.CosmosGasMultiplierNumerator, cosmosGasParams.CosmosGasMultiplierDenominator))
}
}
return DefaultGasMultiplierDenominator
}

// TODO: add tracing to measure latency
func messageContainsNoWildcardDependencies(
ctx sdk.Context,
aclKeeper aclkeeper.Keeper,
contractAddrStr string,
msgInfo *acltypes.WasmMessageInfo,
sender string,
) bool {
addr, err := sdk.AccAddressFromBech32(contractAddrStr)
if err != nil {
return false
}
accessOps, err := aclKeeper.GetWasmDependencyAccessOps(ctx, addr, sender, msgInfo, make(aclkeeper.ContractReferenceLookupMap))
if err != nil {
return false
return ctx.WithGasMeter(types.NewMultiplierGasMeter(gasLimit, cosmosGasParams.CosmosGasMultiplierNumerator, cosmosGasParams.CosmosGasMultiplierDenominator))
}
for _, op := range accessOps {
if op.AccessType != sdkacltypes.AccessType_COMMIT && op.IdentifierTemplate == "*" {
return false
}
}

return true
}
225 changes: 23 additions & 202 deletions app/antedecorators/gas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/accesscontrol"
acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/sei-protocol/sei-chain/app"
"github.com/sei-protocol/sei-chain/app/antedecorators"
"github.com/stretchr/testify/require"
Expand All @@ -17,15 +18,15 @@ func TestMultiplierGasSetter(t *testing.T) {
testApp := app.Setup(false)
contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw")
require.NoError(t, err)
otherContractAddr, err := sdk.AccAddressFromBech32("sei14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sh9m79m")
require.NoError(t, err)
ctx := testApp.NewContext(false, types.Header{}).WithBlockHeight(2)
testApp.ParamsKeeper.SetCosmosGasParams(ctx, *paramtypes.DefaultCosmosGasParams())
testApp.ParamsKeeper.SetFeesParams(ctx, paramtypes.DefaultGenesis().GetFeesParams())
testMsg := wasmtypes.MsgExecuteContract{
Contract: "sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw",
Msg: []byte("{\"xyz\":{}}"),
}
testTx := app.NewTestTx([]sdk.Msg{&testMsg})
// discounted mapping

testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: contractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
Expand All @@ -41,212 +42,32 @@ func TestMultiplierGasSetter(t *testing.T) {
},
},
})
// other contract not discounted
testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: otherContractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_READ,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "*",
},
},
{
Operation: acltypes.CommitAccessOp(),
},
},
})

gasMeterSetter := antedecorators.GetGasMeterSetter(testApp.AccessControlKeeper)
// Test with 1/2 cosmos gas multiplier
testApp.ParamsKeeper.SetCosmosGasParams(ctx, paramtypes.CosmosGasParams{CosmosGasMultiplierNumerator: 1, CosmosGasMultiplierDenominator: 2})
gasMeterSetter := antedecorators.GetGasMeterSetter(testApp.ParamsKeeper)
ctxWithGasMeter := gasMeterSetter(false, ctx, 1000, testTx)
ctxWithGasMeter.GasMeter().ConsumeGas(2, "")
require.Equal(t, uint64(1), ctxWithGasMeter.GasMeter().GasConsumed())

otherTestMsg := wasmtypes.MsgExecuteContract{
Contract: "sei14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sh9m79m",
Msg: []byte("{\"xyz\":{}}"),
}
testTx2 := app.NewTestTx([]sdk.Msg{&testMsg, &otherTestMsg})
ctxWithGasMeter = gasMeterSetter(false, ctx, 1000, testTx2)
ctxWithGasMeter.GasMeter().ConsumeGas(2, "")
// should still not give discount because of other contract being non-discounted
require.Equal(t, uint64(2), ctxWithGasMeter.GasMeter().GasConsumed())

// not discounted mapping
testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: contractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_READ,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "*",
},
},
{
Operation: acltypes.CommitAccessOp(),
},
},
})
// Test with 1/4 cosmos gas multiplier
testApp.ParamsKeeper.SetCosmosGasParams(ctx, paramtypes.CosmosGasParams{CosmosGasMultiplierNumerator: 1, CosmosGasMultiplierDenominator: 4})
ctxWithGasMeter = gasMeterSetter(false, ctx, 1000, testTx)
ctxWithGasMeter.GasMeter().ConsumeGas(2, "")
require.Equal(t, uint64(2), ctxWithGasMeter.GasMeter().GasConsumed())
}
ctxWithGasMeter.GasMeter().ConsumeGas(100, "")
require.Equal(t, uint64(25), ctxWithGasMeter.GasMeter().GasConsumed())

func TestMultiplierGasSetterWithWasmReference(t *testing.T) {
testApp := app.Setup(false)
contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw")
referredContractAddr, err := sdk.AccAddressFromBech32("sei14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sh9m79m")
require.NoError(t, err)
ctx := testApp.NewContext(false, types.Header{}).WithBlockHeight(2)
testMsg := wasmtypes.MsgExecuteContract{
Contract: "sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw",
Msg: []byte("{\"xyz\":{}}"),
}
testTx := app.NewTestTx([]sdk.Msg{&testMsg})
// discounted mapping
testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: contractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_READ,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "something",
},
},
{
Operation: acltypes.CommitAccessOp(),
},
},
BaseContractReferences: []*accesscontrol.WasmContractReference{
{
ContractAddress: referredContractAddr.String(),
MessageType: accesscontrol.WasmMessageSubtype_EXECUTE,
MessageName: "abc",
},
},
})
testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: referredContractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: acltypes.CommitAccessOp(),
},
},
ExecuteAccessOps: []*accesscontrol.WasmAccessOperations{
{
MessageName: "abc",
WasmOperations: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_WRITE,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "something else",
},
},
},
},
},
})
gasMeterSetter := antedecorators.GetGasMeterSetter(testApp.AccessControlKeeper)
ctxWithGasMeter := gasMeterSetter(false, ctx, 1000, testTx)
ctxWithGasMeter.GasMeter().ConsumeGas(2, "")
require.Equal(t, uint64(1), ctxWithGasMeter.GasMeter().GasConsumed())
// not discounted mapping
testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: referredContractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: acltypes.CommitAccessOp(),
},
},
ExecuteAccessOps: []*accesscontrol.WasmAccessOperations{
{
MessageName: "abc",
WasmOperations: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_WRITE,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "*",
},
},
},
},
},
})
ctxWithGasMeter = gasMeterSetter(false, ctx, 1000, testTx)
ctxWithGasMeter.GasMeter().ConsumeGas(2, "")
require.Equal(t, uint64(2), ctxWithGasMeter.GasMeter().GasConsumed())
}
// Test over gas limit even with 1/4 gas multiplier
testApp.ParamsKeeper.SetCosmosGasParams(ctx, paramtypes.CosmosGasParams{CosmosGasMultiplierNumerator: 1, CosmosGasMultiplierDenominator: 4})
ctxWithGasMeter = gasMeterSetter(false, ctx, 20, testTx)
require.Panics(t, func() { ctxWithGasMeter.GasMeter().ConsumeGas(100, "") })
require.Equal(t, true, ctxWithGasMeter.GasMeter().IsOutOfGas())

func TestMultiplierGasSetterWithWasmReferenceCycle(t *testing.T) {
testApp := app.Setup(false)
contractAddr, err := sdk.AccAddressFromBech32("sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw")
referredContractAddr, err := sdk.AccAddressFromBech32("sei14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sh9m79m")
require.NoError(t, err)
ctx := testApp.NewContext(false, types.Header{}).WithBlockHeight(2)
testMsg := wasmtypes.MsgExecuteContract{
Contract: "sei1y3pxq5dp900czh0mkudhjdqjq5m8cpmmps8yjw",
Msg: []byte("{\"xyz\":{}}"),
}
testTx := app.NewTestTx([]sdk.Msg{&testMsg})
// Simulation mode has infinite gas meter with multiplier
testApp.ParamsKeeper.SetCosmosGasParams(ctx, paramtypes.CosmosGasParams{CosmosGasMultiplierNumerator: 1, CosmosGasMultiplierDenominator: 4})
// Gas limit is effectively ignored in simulation
ctxWithGasMeter = gasMeterSetter(true, ctx, 20, testTx)
require.NotPanics(t, func() { ctxWithGasMeter.GasMeter().ConsumeGas(100, "") })
require.Equal(t, uint64(25), ctxWithGasMeter.GasMeter().GasConsumed())
require.Equal(t, false, ctxWithGasMeter.GasMeter().IsOutOfGas())

testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: contractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_READ,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "something",
},
},
{
Operation: acltypes.CommitAccessOp(),
},
},
BaseContractReferences: []*accesscontrol.WasmContractReference{
{
ContractAddress: referredContractAddr.String(),
MessageType: accesscontrol.WasmMessageSubtype_EXECUTE,
MessageName: "abc",
},
},
})
testApp.AccessControlKeeper.SetWasmDependencyMapping(ctx, accesscontrol.WasmDependencyMapping{
ContractAddress: referredContractAddr.String(),
BaseAccessOps: []*accesscontrol.WasmAccessOperation{
{
Operation: acltypes.CommitAccessOp(),
},
},
ExecuteAccessOps: []*accesscontrol.WasmAccessOperations{
{
MessageName: "abc",
WasmOperations: []*accesscontrol.WasmAccessOperation{
{
Operation: &accesscontrol.AccessOperation{
AccessType: accesscontrol.AccessType_WRITE,
ResourceType: accesscontrol.ResourceType_KV,
IdentifierTemplate: "something else",
},
},
},
},
},
BaseContractReferences: []*accesscontrol.WasmContractReference{
{
ContractAddress: contractAddr.String(),
MessageType: accesscontrol.WasmMessageSubtype_EXECUTE,
MessageName: "xyz",
},
},
})
gasMeterSetter := antedecorators.GetGasMeterSetter(testApp.AccessControlKeeper)
ctxWithGasMeter := gasMeterSetter(false, ctx, 1000, testTx)
ctxWithGasMeter.GasMeter().ConsumeGas(2, "")
require.Equal(t, uint64(2), ctxWithGasMeter.GasMeter().GasConsumed())
}
Loading

0 comments on commit fd7f6bc

Please sign in to comment.