diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index 7fc35033ae5..6856601eaff 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -28,6 +28,13 @@ const GasMultiplier = 100 // MaxGas for a contract is 900 million (enforced in rust) const MaxGas = 900_000_000 +// InstanceCost is how much SDK gas we charge each time we load a WASM instance. +// Creating a new instance is costly, and this helps put a recursion limit to contracts calling contracts. +const InstanceCost uint64 = 40_000 + +// CompileCost is how much SDK gas we charge *per byte* for compiling WASM code. +const CompileCost uint64 = 2 + // Keeper will have a reference to Wasmer with it's own data directory. type Keeper struct { storeKey sdk.StoreKey @@ -73,6 +80,8 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, if err != nil { return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) } + ctx.GasMeter().ConsumeGas(CompileCost*uint64(len(wasmCode)), "Compiling WASM Bytecode") + codeHash, err := k.wasmer.Create(wasmCode) if err != nil { // return 0, sdkerrors.Wrap(err, "cosmwasm create") @@ -89,6 +98,8 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, // Instantiate creates an instance of a WASM contract func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins) (sdk.AccAddress, error) { + ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: init") + // create contract address contractAddress := k.generateContractAddress(ctx, codeID) existingAcct := k.accountKeeper.GetAccount(ctx, contractAddress) @@ -159,6 +170,8 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A // Execute executes the contract instance func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, msg []byte, coins sdk.Coins) (*sdk.Result, error) { + ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: execute") + codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) if err != nil { return nil, err @@ -201,6 +214,8 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller // Migrate allows to upgrade a contract to a new code with data migration. func (k Keeper) Migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newCodeID uint64, msg []byte) (*sdk.Result, error) { + ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: migrate") + contractInfo := k.GetContractInfo(ctx, contractAddress) if contractInfo == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "unknown contract") @@ -267,6 +282,8 @@ func (k Keeper) UpdateContractAdmin(ctx sdk.Context, contractAddress sdk.AccAddr // QuerySmart queries the smart contract itself. func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: query") + ctx = ctx.WithGasMeter(sdk.NewGasMeter(k.queryGasLimit)) codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) diff --git a/x/wasm/internal/keeper/keeper_test.go b/x/wasm/internal/keeper/keeper_test.go index f668a5dad5c..b2c19cc6aae 100644 --- a/x/wasm/internal/keeper/keeper_test.go +++ b/x/wasm/internal/keeper/keeper_test.go @@ -203,7 +203,7 @@ func TestInstantiate(t *testing.T) { require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", addr.String()) gasAfter := ctx.GasMeter().GasConsumed() - require.Equal(t, uint64(0x78c3), gasAfter-gasBefore) + require.Equal(t, uint64(0x11503), gasAfter-gasBefore) // ensure it is stored properly info := keeper.GetContractInfo(ctx, addr) @@ -298,7 +298,7 @@ func TestExecute(t *testing.T) { // make sure gas is properly deducted from ctx gasAfter := ctx.GasMeter().GasConsumed() - require.Equal(t, uint64(0x7f9e), gasAfter-gasBefore) + require.Equal(t, uint64(0x11bde), gasAfter-gasBefore) // ensure bob now exists and got both payments released bobAcct = accKeeper.GetAccount(ctx, bob)