Sharing here the solution that was finally merged in the EVM + Frontier and currently live in Moonbeam networks:
paritytech:master
← moonbeam-foundation:tgm-record-external-costs
opened 09:16AM - 13 Apr 23 UTC
Depends on https://github.com/paritytech/frontier/pull/893 (cherry picking some … of the changes here to make it work)
Drafting EVM changes in https://github.com/PureStake/evm/tree/tgm-record-external-cost (pinning this branch here temporarily to make the CI pass)
### The problem we are trying to solve
Current parachain block validation design is resource restrictive, and one of this restrictions is proof size: the amount of parachain state data a relay validator needs to proof the state transition for a block is valid.
The evm gasometer only records and has capacity for one metric - gas -, so we need the ability of recording additional metrics - in our case `proof_size` - so we can exit the evm when the proof size capacity limit has been reached for the _current_ block.
### What we should avoid
Modifying the evm gasometer.
The changes we are introducing are not part of the evm design - and will never be. There is plans in Ethereum to adapt the gasometer architecture to support witness data recording, so a solution for a similar problem will eventually be part of the standard specification. Changing the gasometer logic - or abstracting away the Gas for example - will only lead to complex foreign code in the evm that really gives nothing in return.
In our case we have a Substrate-only problem, and the solution should happen completely (or almost) in Substrate.
### StackState external cost recorder
This PR proposes adding two methods to the StackState trait:
- `record_external_opcode_cost`: which is called from `pre_validate` in the executor (that is per each evm runtime step).
- `record_external_cost`: meant to be called from precompiles, and more specifically, substrate-implemented precompiles, where there is the need of recording a foreign (not opcode based) Weight v2 cost. This method is behing a `with-substrate` feature gate.
Every time the evm steps into an opcode, if it has a `proof_size` associated to it - reads or writes from storage - this proof size will be, if the storage is _cold_, cheaply calculated on the fly and recorded. The recorder will OutOfGas if the metric capacity is reached.
**Note**: the reasons to not abstract away `record_external_cost` and use a feature flag are two:
- The concept of _dispatching_ - and the need of an external cost - from precompiles is purely a substrate one. At least afaik.
- Adding generics to the `PrecompileHandle` will cascade into changes across the evm, and thus goes against the main motivation of this proposal: keep changes outside the evm whenevr possible.
### Differences between Gas and External cost
Gas is part of the evm design, and has its own _target_ capacity per subcall. This is not the case for external costs: they have one transaction-wide usage and capacity because, again, they are a foreign metric, not part of evm design.
### Ref time support
https://forum.polkadot.network/t/frontier-support-for-evm-weight-v2/2470/5 does a great job describing why ref_time recording should also be supported.
The solution proposed in this PR supports it (once we benchmark Opcodes in substrate) and also supports a preliminary version without it, where we still use gas to weight conversion and rely on the native gasometer.
---
cc
@sorpaas
@librelois
@nanocryk
@crystalin
We introduced a set of new StackState
trait methods that are called before executing an opcode. StackState
is implemented in the Substrate backend, where we define some constant costs associated to virtually any amount of metrics we want the EVM transaction to be bounded to - in the case of WeightV2
support, a metric would be pov_size
, but we plan to also have pure support for ref_time
instead doing a loose conversion between GasLimit and ref_time
.
This has the benefit of being almost completely managed at Substrate level - with the exception of some EVM helpers -, leaving the original Gasometer design untouched.
The main idea is to OutOfGas
an EVM transaction when any of the metric pools are exhausted, please refer to the original PR for details.
2 Likes