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:
k.IsPinnedCode(ctx, contractInfo.CodeID) is true, i.e. the contract is pinnend
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:
A block can have more contract executions than there is space in the in-memory cache, creating a risk for underpricing
The gas simulation becomes inacurate when the gas usage depends on other transactions in the block.
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).
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:
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:k.IsPinnedCode(ctx, contractInfo.CodeID)
is true, i.e. the contract is pinnendCase 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
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).