diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index ddeb6428..5f824209 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -12,30 +12,20 @@ created: 2018-08-01 ## Abstract -This EIP proposes net gas metering changes for SSTORE opcode, as an -alternative for EIP-1087. It tries to be friendlier to implementations -that use different optimization strategies for storage change -caches. +This EIP proposes net gas metering changes for SSTORE opcode, enabling +new usages for contract storage, and reducing excessive gas costs +where it doesn't match how most implementation works. + +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 -EIP-1087 proposes a way to adjust gas metering for SSTORE opcode, -enabling new usages on these opcodes where it is previously too -expensive. However, 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. - -This EIP proposes an alternative way for gas metering on SSTORE, using -information that is more universally available to most -implementations: +This EIP proposes a way for gas metering on SSTORE (as an alternative +for EIP-1087 and EIP-1153), using information that is more universally +available to most implementations, and require as little change in +implementation structures as possible. * *Storage slot's original value*. * *Storage slot's current value*. @@ -97,7 +87,18 @@ the following logic: * Otherwise, add 4800 gas to refund counter. 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 @@ -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 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*), this EIP is equivalent to EIP-1087 for all cases. * 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 | | `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 and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).