forked from DecentralizedClimateFoundation/DCIPs
122 lines
7.3 KiB
Markdown
122 lines
7.3 KiB
Markdown
|
---
|
||
|
eip: 2045
|
||
|
title: Particle gas costs for EVM opcodes
|
||
|
author: Casey Detrio (@cdetrio), Alex Beregszaszi (@axic)
|
||
|
discussions-to: https://ethereum-magicians.org/t/eip-2045-fractional-gas-costs/3311
|
||
|
status: Stagnant
|
||
|
type: Standards Track
|
||
|
category: Core
|
||
|
created: 2019-05-17
|
||
|
---
|
||
|
|
||
|
## Abstract
|
||
|
According to recent benchmarks, EVM opcodes for computation (`ADD`, `SUB`, `MUL`, etc.) are generally overpriced relative to opcodes for storage I/O (`SLOAD`, `SSTORE`, etc.). Currently the minimum gas cost is 1 (i.e. one unit of gas), and most computational opcodes have a cost near to 1 (e.g. 3, 5, or 8), so the range in possible cost reduction is limited. A new minimum unit of gas, called a "particle", which is a fraction of 1 gas, would expand the range of gas costs and thus enable reductions below the current minimum.
|
||
|
|
||
|
## Motivation
|
||
|
The transaction capacity of an Ethereum block is determined by the gas cost of transactions relative to the block gas limit. One way to boost the transaction capacity is to raise the block gas limit. Unfortunately, raising the block gas limit would also increase the rate of state growth, unless the costs of state-expanding storage opcodes (`SSTORE`, `CREATE`, etc.) are simultaneously increased to the same proportion. Increasing the cost of storage opcodes may have adverse side effects, such as shifting the economic assumptions around gas fees of deployed contracts, or possibly breaking invariants in current contract executions (as mentioned in [EIP-2035](./eip-2035.md)<sup>[1](#eip2035)</sup>, more research is needed on the potential effects of increasing the cost of storage opcodes).
|
||
|
|
||
|
Another way to boost the transaction capacity of a block is to reduce the gas cost of transactions. Reducing the gas costs of computational opcodes while keeping the cost of storage opcodes the same, is effectively equivalent to raising the block gas limit and simultaneously increasing the cost of storage opcodes. However, reducing the cost of computational opcodes might avoid the adverse side effects of an increase in cost of storage opcodes (again, more research is needed on this topic).
|
||
|
|
||
|
Currently, computational opcode costs are already too close to the minimum unit of 1 gas to achieve the large degree of cost reductions that recent benchmarks<sup>[2](#evmbenchmarks)</sup> indicate would be needed to tune opcode gas costs to the performance of optimized EVM implementations. A smaller minimum unit called a "particle", which is a fraction (or subdivision) of 1 gas, would enable large cost reductions.
|
||
|
|
||
|
## Specification
|
||
|
A new gas counter `particlesUsed` is added to the EVM, in addition to the existing gas counter `gasUsed`. The unit 1 gas is equal to 10000 particles (`PARTICLES_PER_GAS`). The `particlesUsed` counter is only increased for opcodes priced in particles (i.e. opcodes that cost less than 1 gas). If increasing `particlesUsed` results in an excess of 1 gas, then 1 gas is added to `gasUsed` (and deducted from `particlesUsed`).
|
||
|
|
||
|
Where the current gas logic looks like this:
|
||
|
```python
|
||
|
def vm_execute(ext, msg, code):
|
||
|
# Initialize stack, memory, program counter, etc
|
||
|
compustate = Compustate(gas=msg.gas)
|
||
|
codelen = len(code)
|
||
|
|
||
|
while compustate.pc < codelen:
|
||
|
opcode = code[compustate.pc]
|
||
|
compustate.pc += 1
|
||
|
|
||
|
compustate.gasUsed += opcode.gas_fee
|
||
|
|
||
|
# out of gas error
|
||
|
if compustate.gasUsed > compustate.gasLimit:
|
||
|
return vm_exception('OUT OF GAS')
|
||
|
|
||
|
if op == 'STOP':
|
||
|
return peaceful_exit()
|
||
|
elif op == 'ADD':
|
||
|
stk.append(stk.pop() + stk.pop())
|
||
|
elif op == 'SUB':
|
||
|
stk.append(stk.pop() - stk.pop())
|
||
|
elif op == 'MUL':
|
||
|
stk.append(stk.pop() * stk.pop())
|
||
|
|
||
|
.....
|
||
|
```
|
||
|
|
||
|
The new gas logic using particles might look like this:
|
||
|
```python
|
||
|
PARTICLES_PER_GAS = 10000
|
||
|
|
||
|
def vm_execute(ext, msg, code):
|
||
|
# Initialize stack, memory, program counter, etc
|
||
|
compustate = Compustate(gas=msg.gas)
|
||
|
codelen = len(code)
|
||
|
|
||
|
while compustate.pc < codelen:
|
||
|
opcode = code[compustate.pc]
|
||
|
compustate.pc += 1
|
||
|
|
||
|
if opcode.gas_fee:
|
||
|
compustate.gasUsed += opcode.gas_fee
|
||
|
elif opcode.particle_fee:
|
||
|
compustate.particlesUsed += opcode.particle_fee
|
||
|
if compustate.particlesUsed >= PARTICLES_PER_GAS:
|
||
|
# particlesUsed will be between 1 and 2 gas (over 10000 but under 20000)
|
||
|
compustate.gasUsed += 1
|
||
|
# remainder stays in particle counter
|
||
|
compustate.particlesUsed = compustate.particlesUsed % PARTICLES_PER_GAS
|
||
|
|
||
|
# out of gas error
|
||
|
if compustate.gasUsed > compustate.gasLimit:
|
||
|
return vm_exception('OUT OF GAS')
|
||
|
|
||
|
if op == 'STOP':
|
||
|
return peaceful_exit()
|
||
|
elif op == 'ADD':
|
||
|
stk.append(stk.pop() + stk.pop())
|
||
|
elif op == 'SUB':
|
||
|
stk.append(stk.pop() - stk.pop())
|
||
|
elif op == 'MUL':
|
||
|
stk.append(stk.pop() * stk.pop())
|
||
|
|
||
|
.....
|
||
|
```
|
||
|
|
||
|
The above pseudocode is written for clarity. A more performant implementation might instead keep a single `particlesUsed` counter by multiplying opcode gas costs by 10000 and the `gasLimit` by 10000, and convert particles back to gas with `ceil(particlesUsed / PARTICLES_PER_GAS)` at the end of execution. It may also be more performant to use a `PARTICLES_PER_GAS` ratio that is a power of 2 (such as 8192 or 16384) instead of 10000; the spec above is a draft and updates in response to feedback are expected.
|
||
|
|
||
|
#### Opcode cost changes
|
||
|
Many computational opcodes will undergo a cost reduction, with new costs suggested by benchmark analyses. For example, the cost of `DUP` and `SWAP` are reduced from 3 gas to 3000 particles (i.e. 0.3 gas). The cost of `ADD` and `SUB` are reduced from 3 gas to 6000 particles. The cost of `MUL` is reduced from 5 gas to 5000 particles (i.e. 0.5 gas).
|
||
|
|
||
|
## Rationale
|
||
|
Adoption of fractional gas costs should only be an implementation detail inside the EVM, and not alter the current user experience around transaction gas limits and block gas limits. The concept of `particles` need not be exposed to Ethereum users nor most contract authors, but only to EVM implementers and contract developers concerned with optimized gas usage. Furthermore, only the EVM logic for charging gas per opcode executed should be affected by this change. All other contexts dealing with gas and gas limits, such as block headers and transaction formats, should be unaffected.
|
||
|
|
||
|
### Ewasm
|
||
|
The term "particles" was first introduced for Ewasm<sup>[3](#particle)</sup> to enable gas accounting for low cost wasm instructions, while remaining compatible with EVM gas costs. This EIP proposes introducing particles as a new minimum gas unit for EVM opcodes, and is not related to Ewasm.
|
||
|
|
||
|
## Backwards Compatibility
|
||
|
This change is not backwards compatible and requires a hard fork to be activated.
|
||
|
|
||
|
## Test Cases
|
||
|
TODO
|
||
|
|
||
|
## Implementation
|
||
|
TODO
|
||
|
|
||
|
## References
|
||
|
<a name="eip2035">1</a>. [EIP-2035](./eip-2035.md): Stateless Clients - Repricing SLOAD and SSTORE to pay for block proofs
|
||
|
|
||
|
<a name="evmbenchmarks">2</a>. https://github.com/ewasm/benchmarking
|
||
|
|
||
|
<a name="particle">3</a>. The term "particle" was inspired by a proposal for [Ewasm gas costs](https://github.com/ewasm/design/blob/e77d8e3de42784f40a803a23f58ef06881142d9f/determining_wasm_gas_costs.md).
|
||
|
|
||
|
## Copyright
|
||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|