CosmWasm / wasmd

Basic cosmos-sdk app with web assembly smart contracts
Other
364 stars 395 forks source link

Improve setup cost calculation #1798

Open webmaster128 opened 7 months ago

webmaster128 commented 7 months ago

Setup cost is the gas charged every time a contract is set up for execution trhough any entry point (execute/instantiate/sudo/query/migrate/reply/....). Right now it is implemented like this:

// SetupContractCost costs when interacting with a wasm contract
func (g WasmGasRegister) SetupContractCost(pinned bool, msgLen int) storetypes.Gas {
    if msgLen < 0 {
        panic(errorsmod.Wrap(ErrInvalid, "negative length"))
    }
    dataCosts := storetypes.Gas(msgLen) * g.c.ContractMessageDataCost
    if pinned {
        return dataCosts
    }
    return g.c.InstanceCost + dataCosts
}

i.e. we charge for the message sent to it as well as creating the executable contract instance in memory. So the pinned flag has a significant impact on the gas usage. It is set to true in two cases:

  1. k.IsPinnedCode(ctx, contractInfo.CodeID) is true, i.e. the contract is pinnend
  2. We setup a contract for reply

Case 2. is actually a misuse of the flag, or at least in its current name. We use the assumption that the contract is still in the in-memory cache when we send the reply. This assumption is plausible but with a node-specific setting of the cache size can lead to situtions when we charge low gas.

In addition to reply, there are other cases in which a a contract is executed multiple times in a tx or block. Following the idea of reply in 2., we can say that the gas discount is given for all calls in a transaction or block that have been seen before. So instead of the current logic in reply

    // always consider this pinned
    replyCosts := k.gasRegister.ReplyCosts(true, reply)

we can say that if code (identified by checksum) was used in this transaction before, give a discount. This then always includes replys but many other cases as well. It would requiring a map from checksum to boolean that is maintained for the lifetime of the transaction.

I think the scope for the discount should be transaction, not block for the following reasons:

The reason for the discount for pinned contracts and contracts in the in-memory cache is that their Wasmer instance is created in ~45µs instead of 1.5ms (33x faster).