mirror of https://github.com/status-im/EIPs.git
Automatically merged updates to draft EIP(s) 1283
Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing
This commit is contained in:
parent
f2f879361e
commit
001ccad32d
167
EIPS/eip-1283.md
167
EIPS/eip-1283.md
|
@ -12,30 +12,20 @@ created: 2018-08-01
|
||||||
|
|
||||||
## Abstract
|
## Abstract
|
||||||
|
|
||||||
This EIP proposes net gas metering changes for SSTORE opcode, as an
|
This EIP proposes net gas metering changes for SSTORE opcode, enabling
|
||||||
alternative for EIP-1087. It tries to be friendlier to implementations
|
new usages for contract storage, and reducing excessive gas costs
|
||||||
that use different optimization strategies for storage change
|
where it doesn't match how most implementation works.
|
||||||
caches.
|
|
||||||
|
This acts as an alternative for EIP-1087, where it tries to be
|
||||||
|
friendlier to implementations that use different optimization
|
||||||
|
strategies for storage change caches.
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
EIP-1087 proposes a way to adjust gas metering for SSTORE opcode,
|
This EIP proposes a way for gas metering on SSTORE (as an alternative
|
||||||
enabling new usages on these opcodes where it is previously too
|
for EIP-1087 and EIP-1153), using information that is more universally
|
||||||
expensive. However, EIP-1087 requires keeping a dirty map for storage
|
available to most implementations, and require as little change in
|
||||||
changes, and implicitly makes the assumption that a transaction's
|
implementation structures as possible.
|
||||||
storage changes are committed to the storage trie at the end of a
|
|
||||||
transaction. This works well for some implementations, but not for
|
|
||||||
others. After EIP-658, an efficient storage cache implementation would
|
|
||||||
probably use an in-memory trie (without RLP encoding/decoding) or
|
|
||||||
other immutable data structures to keep track of storage changes, and
|
|
||||||
only commit changes at the end of a block. For them, it is possible to
|
|
||||||
know a storage's original value and current value, but it is not
|
|
||||||
possible to iterate over all storage changes without incurring additional
|
|
||||||
memory or processing costs.
|
|
||||||
|
|
||||||
This EIP proposes an alternative way for gas metering on SSTORE, using
|
|
||||||
information that is more universally available to most
|
|
||||||
implementations:
|
|
||||||
|
|
||||||
* *Storage slot's original value*.
|
* *Storage slot's original value*.
|
||||||
* *Storage slot's current value*.
|
* *Storage slot's current value*.
|
||||||
|
@ -97,7 +87,18 @@ the following logic:
|
||||||
* Otherwise, add 4800 gas to refund counter.
|
* Otherwise, add 4800 gas to refund counter.
|
||||||
|
|
||||||
Refund counter works as before -- it is limited to half of the gas
|
Refund counter works as before -- it is limited to half of the gas
|
||||||
consumed.
|
consumed. On a transaction level, refund counter will never go below
|
||||||
|
zero. However, there are some important notes depending on the
|
||||||
|
implementation details:
|
||||||
|
|
||||||
|
* If an implementation uses "transaction level" refund counter (refund
|
||||||
|
is checkpointed at each call frame), then the refund counter
|
||||||
|
continues to be unsigned.
|
||||||
|
* If an implementation uses "execution-frame level" refund counter
|
||||||
|
(a new refund counter is created at each call frame, and then merged
|
||||||
|
back to parent when the call frame finishes), then the refund
|
||||||
|
counter needs to be changed to signed -- at internal calls, a child
|
||||||
|
refund can go below zero.
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
|
@ -169,6 +170,28 @@ This EIP mostly archives what a transient storage tries to do
|
||||||
(EIP-1087 and EIP-1153), but without the complexity of introducing the
|
(EIP-1087 and EIP-1153), but without the complexity of introducing the
|
||||||
concept of "dirty maps", or an extra storage struct.
|
concept of "dirty maps", or an extra storage struct.
|
||||||
|
|
||||||
|
* We don't suffer from the optimization limitation of
|
||||||
|
EIP-1087. EIP-1087 requires keeping a dirty map for storage changes,
|
||||||
|
and implicitly makes the assumption that a transaction's storage
|
||||||
|
changes are committed to the storage trie at the end of a
|
||||||
|
transaction. This works well for some implementations, but not for
|
||||||
|
others. After EIP-658, an efficient storage cache implementation
|
||||||
|
would probably use an in-memory trie (without RLP encoding/decoding)
|
||||||
|
or other immutable data structures to keep track of storage changes,
|
||||||
|
and only commit changes at the end of a block. For them, it is
|
||||||
|
possible to know a storage's original value and current value, but
|
||||||
|
it is not possible to iterate over all storage changes without
|
||||||
|
incurring additional memory or processing costs.
|
||||||
|
* It never costs more gas compared with the current scheme.
|
||||||
|
* It covers all usages for a transient storage. Clients that are easy
|
||||||
|
to implement EIP-1087 will also be easy to implement this
|
||||||
|
specification. Some other clients might require a little bit extra
|
||||||
|
refactoring on this. Nonetheless, no extra memory or processing cost
|
||||||
|
is needed on runtime.
|
||||||
|
|
||||||
|
Regarding SSTORE gas cost and refunds, see Appendix for proofs of
|
||||||
|
properties that this EIP satisfies.
|
||||||
|
|
||||||
* For *absolute gas used* (that is, actual *gas used* minus *refund*),
|
* For *absolute gas used* (that is, actual *gas used* minus *refund*),
|
||||||
this EIP is equivalent to EIP-1087 for all cases.
|
this EIP is equivalent to EIP-1087 for all cases.
|
||||||
* For one particular case, where a storage slot is changed, reset to
|
* For one particular case, where a storage slot is changed, reset to
|
||||||
|
@ -218,6 +241,106 @@ slot is reset and then set again.
|
||||||
| `0x600160005560006000556001600055` | 40218 | 19800 | 0 | 1 | 0 | 1 |
|
| `0x600160005560006000556001600055` | 40218 | 19800 | 0 | 1 | 0 | 1 |
|
||||||
| `0x600060005560016000556000600055` | 10218 | 19800 | 1 | 0 | 1 | 0 |
|
| `0x600060005560016000556000600055` | 10218 | 19800 | 1 | 0 | 1 | 0 |
|
||||||
|
|
||||||
|
## Appendix: Proof
|
||||||
|
|
||||||
|
Because the *storage slot's original value* is defined as the value
|
||||||
|
when a reversion happens on the *current transaction*, it's easy to
|
||||||
|
see that call frames won't interfere SSTORE gas calculation. So
|
||||||
|
although the below proof is discussed without call frames, it applies
|
||||||
|
to all situations with call frames. Below we will discuss the case
|
||||||
|
separately for *original value* being zero and not zero, and use
|
||||||
|
*induction* to prove some properties of SSTORE gas cost.
|
||||||
|
|
||||||
|
*Final value* is the value of a particular storage slot at the end of
|
||||||
|
a transaction. *Absolute gas used* is the absolute value of *gas used*
|
||||||
|
minus *refund*. We use `N` to represent the total number of SSTORE
|
||||||
|
operations on a storage slot. For states discussed below, refer to
|
||||||
|
*State Transition* in *Explanation* section.
|
||||||
|
|
||||||
|
### Original Value Being Zero
|
||||||
|
|
||||||
|
When *original value* is 0, we want to prove that:
|
||||||
|
|
||||||
|
* **Case I**: If the *final value* ends up still being 0, we want to charge `200 *
|
||||||
|
N` gases, because no disk write is needed.
|
||||||
|
* **Case II**: If the *final value* ends up being a non-zero value, we want to
|
||||||
|
charge `20000 + 200 * (N-1)` gas, because it requires writing this
|
||||||
|
slot to disk.
|
||||||
|
|
||||||
|
#### Base Case
|
||||||
|
|
||||||
|
We always start at state A. The first SSTORE can:
|
||||||
|
|
||||||
|
* Go to state A: 200 gas is deducted. We satisfy *Case I* because
|
||||||
|
`200 * N == 200 * 1`.
|
||||||
|
* Go to state B: 20000 gas is deducted. We satisfy *Case II* because
|
||||||
|
`20000 + 200 * (N-1) == 20000 + 200 * 0`.
|
||||||
|
|
||||||
|
#### Inductive Step
|
||||||
|
|
||||||
|
* From A to A. The previous gas cost is `200 * (N-1)`. The current
|
||||||
|
gas cost is `200 + 200 * (N-1)`. It satisfy *Case I*.
|
||||||
|
* From A to B. The previous gas cost is `200 * (N-1)`. The current
|
||||||
|
gas cost is `20000 + 200 * (N-1)`. It satisfy *Case II*.
|
||||||
|
* From B to B. The previous gas cost is `20000 + 200 * (N-2)`. The
|
||||||
|
current gas cost is `200 + 20000 + 200 * (N-2)`. It satisfy
|
||||||
|
*Case II*.
|
||||||
|
* From B to A. The previous gas cost is `20000 + 200 * (N-2)`. The
|
||||||
|
current gas cost is `200 - 19800 + 20000 + 200 * (N-2)`. It satisfy
|
||||||
|
*Case I*.
|
||||||
|
|
||||||
|
### Original Value Not Being Zero
|
||||||
|
|
||||||
|
When *original value* is not 0, we want to prove that:
|
||||||
|
|
||||||
|
* **Case I**: If the *final value* ends up unchanged, we want to
|
||||||
|
charge `200 * N` gases, because no disk write is needed.
|
||||||
|
* **Case II**: If the *final value* ends up being zero, we want to
|
||||||
|
charge `5000 - 15000 + 200 * (N-1)` gas. Note that `15000` is the
|
||||||
|
refund in actual defintion.
|
||||||
|
* **Case III**: If the *final value* ends up being a changed non-zero
|
||||||
|
value, we want to charge `5000 + 200 * (N-1)` gas.
|
||||||
|
|
||||||
|
#### Base Case
|
||||||
|
|
||||||
|
We always start at state X. The first SSTORE can:
|
||||||
|
|
||||||
|
* Go to state X: 200 gas is deducted. We satisfy *Case I* because
|
||||||
|
`200 * N == 200 * 1`.
|
||||||
|
* Go to state Y: 5000 gas is deducted. We satisfy *Case III* because
|
||||||
|
`5000 + 200 * (N-1) == 5000 + 200 * 0`.
|
||||||
|
* Go to state Z: The absolute gas used is `5000 - 15000` where 15000
|
||||||
|
is the refund. We satisfy *Case II* because `5000 - 15000 + 200 *
|
||||||
|
(N-1) == 5000 - 15000 + 200 * 0`.
|
||||||
|
|
||||||
|
#### Inductive Step
|
||||||
|
|
||||||
|
* From X to X. The previous gas cost is `200 * (N-1)`. The current gas
|
||||||
|
cost is `200 + 200 * (N-1)`. It satisfy *Case I*.
|
||||||
|
* From X to Y. The previous gas cost is `200 * (N-1)`. The current gas
|
||||||
|
cost is `5000 + 200 * (N-1)`. It satisfy *Case III*.
|
||||||
|
* From X to Z. The previous gas cost is `200 * (N-1)`. The current
|
||||||
|
absolute gas cost is `5000 - 15000 + 200 * (N-1)`. It satisfy *Case
|
||||||
|
II*.
|
||||||
|
* From Y to X. The previous gas cost is `5000 + 200 * (N-2)`. The
|
||||||
|
absolute current gas cost is `200 - 4800 + 5000 + 200 * (N-2)`. It
|
||||||
|
satisfy *Case I*.
|
||||||
|
* From Y to Y. The previous gas cost is `5000 + 200 * (N-2)`. The
|
||||||
|
current gas cost is `200 + 5000 + 200 * (N-2)`. It satisfy *Case
|
||||||
|
III*.
|
||||||
|
* From Y to Z. The previous gas cost is `5000 + 200 * (N-2)`. The
|
||||||
|
current absolute gas cost is `200 - 15000 + 5000 + 200 * (N-2)`. It
|
||||||
|
satisfy *Case II*.
|
||||||
|
* From Z to X. The previous gas cost is `5000 - 15000 + 200 *
|
||||||
|
(N-2)`. The current absolute gas cost is `200 + 10200 + 5000 -
|
||||||
|
15000 + 200 * (N-2)`. It satisfy *Case I*.
|
||||||
|
* From Z to Y. The previous gas cost is `5000 - 15000 + 200 *
|
||||||
|
(N-2)`. The current absolute gas cost is `200 + 15000 + 5000 -
|
||||||
|
15000 + 200 * (N-2)`. It satisfy *Case III*.
|
||||||
|
* From Z to Z. The previous gas cost is `5000 - 15000 + 200 *
|
||||||
|
(N-2)`. The current absolute gas cost is `200 + 5000 - 15000 + 200 *
|
||||||
|
(N-2)`. It satisfy *Case II*.
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
|
|
Loading…
Reference in New Issue