Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add storage change status analysis #418

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions bindings/go/evmc/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ const (
type StorageStatus int

const (
StorageUnchanged StorageStatus = C.EVMC_STORAGE_UNCHANGED
StorageModified StorageStatus = C.EVMC_STORAGE_MODIFIED
StorageModifiedAgain StorageStatus = C.EVMC_STORAGE_MODIFIED_AGAIN
StorageAdded StorageStatus = C.EVMC_STORAGE_ADDED
StorageDeleted StorageStatus = C.EVMC_STORAGE_DELETED
StorageModifiedAgain StorageStatus = C.EVMC_STORAGE_MODIFIED_AGAIN
StorageAdded StorageStatus = C.EVMC_STORAGE_ADDED
StorageDeleted StorageStatus = C.EVMC_STORAGE_DELETED
StorageModified StorageStatus = C.EVMC_STORAGE_MODIFIED
StorageDeletedAdded StorageStatus = C.EVMC_STORAGE_DELETED_ADDED
StorageModifiedDeleted StorageStatus = C.EVMC_STORAGE_MODIFIED_DELETED
StorageDeletedRestored StorageStatus = C.EVMC_STORAGE_DELETED_RESTORED
StorageAddedDeleted StorageStatus = C.EVMC_STORAGE_ADDED_DELETED
StorageModifiedRestored StorageStatus = C.EVMC_STORAGE_MODIFIED_RESTORED
)

func goAddress(in C.evmc_address) Address {
Expand Down
2 changes: 1 addition & 1 deletion bindings/go/evmc/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (host *testHostContext) GetStorage(addr Address, key Hash) Hash {
}

func (host *testHostContext) SetStorage(addr Address, key Hash, value Hash) (status StorageStatus) {
return StorageUnchanged
return StorageModifiedAgain
}

func (host *testHostContext) GetBalance(addr Address) Hash {
Expand Down
4 changes: 2 additions & 2 deletions bindings/rust/evmc-vm/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ mod tests {
#[test]
fn storage_status() {
assert_eq!(
StorageStatus::EVMC_STORAGE_UNCHANGED,
ffi::evmc_storage_status::EVMC_STORAGE_UNCHANGED
StorageStatus::EVMC_STORAGE_MODIFIED_AGAIN,
ffi::evmc_storage_status::EVMC_STORAGE_MODIFIED_AGAIN
);
assert_eq!(
StorageStatus::EVMC_STORAGE_MODIFIED,
Expand Down
169 changes: 169 additions & 0 deletions docs/storage_change_status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Storage change status

> Analysis of possible storage change statuses in [EIP-2200].

## Specification

1. If *gasleft* is less than or equal to gas stipend, fail the current
call frame with 'out of gas' exception.
2. If *current value* equals *new value* (this is a no-op), `SLOAD_GAS`
is deducted.
3. If *current value* does not equal *new value*
1. If *original value* equals *current value* (this storage slot has
not been changed by the current execution context)
1. If *original value* is 0, `SSTORE_SET_GAS` is deducted.
2. Otherwise, `SSTORE_RESET_GAS` gas is deducted.
1. If *new value* is 0,
add `SSTORE_CLEARS_SCHEDULE` gas to refund counter.
2. If *original value* does not equal *current value* (this storage
slot is dirty), `SLOAD_GAS` gas is deducted. Apply both of the
following clauses.
1. If *original value* is not 0
1. If *current value* is 0 (also means that *new value* is not
0), remove `SSTORE_CLEARS_SCHEDULE` gas from refund
counter.
2. If *new value* is 0 (also means that *current value* is not
0), add `SSTORE_CLEARS_SCHEDULE` gas to refund counter.
2. If *original value* equals *new value* (this storage slot is
reset)
1. If *original value* is 0, add `SSTORE_SET_GAS - SLOAD_GAS` to
refund counter.
2. Otherwise, add `SSTORE_RESET_GAS - SLOAD_GAS` gas to refund
counter.

## Cost constants

| Constant | Value |
| -------- | ----- |
| SLOAD | 800 |
| SET | 20000 |
| RESET | 5000 |
| CLEARS | 15000 |

## Storage change statuses

- `0` - a zero storage value,
- `X` - non-zero storage value,
- `Y` - non-zero storage value different from `X`,
- `Z` - non-zero storage value different form `X` and `Y`,

<table>
axic marked this conversation as resolved.
Show resolved Hide resolved
<thead>
<tr>
<th>name</th>
<th>o</th>
<th>c</th>
<th>v</th>
<th>dirty?</th>
<th>clause</th>
<th>gas cost</th>
<th>gas refund</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="7">NOOP</td>
<td>0</td><td>0</td><td>0</td>
<td>no</td>
<td>2</td>
<td rowspan="7">800 = SLOAD</td>
<td rowspan="7">0</td>
</tr>
<tr>
<td>X</td><td>0</td><td>0</td>
<td>yes</td>
<td>2</td>
</tr>
<tr>
<td>0</td><td>Y</td><td>Y</td>
<td>yes</td>
<td>2</td>
</tr>
<tr>
<td>X</td><td>Y</td><td>Y</td>
<td>yes</td>
<td>2</td>
</tr>
<tr>
<td>Y</td><td>Y</td><td>Y</td>
<td>no</td>
<td>2</td>
</tr>
<tr>
<td>0</td><td>Y</td><td>Z</td>
<td>yes</td>
<td>3.2</td>
</tr>
<tr>
<td>X</td><td>Y</td><td>Z</td>
<td>yes</td>
<td>3.2</td>
</tr>
<tr>
<td>ADDED</td>
<td>0</td><td>0</td><td>Z</td>
<td>no</td>
<td>3.1.1</td>
<td>20000 = SET</td>
<td>0</td>
</tr>
<tr>
<td>DELETED</td>
<td>X</td><td>X</td><td>0</td>
<td>no</td>
<td>3.1.2.1</td>
<td>5000 = RESET</td>
<td>15000 = CLEARS</td>
</tr>
<tr>
<td>MODIFIED</td>
<td>X</td><td>X</td><td>Z</td>
<td>no</td>
<td>3.1.2</td>
<td>5000 = RESET</td>
<td>0</td>
</tr>
<tr>
<td>deleted added</td>
<td>X</td><td>0</td><td>Z</td>
<td>yes</td>
<td>3.2.1.1</td>
<td>800 = SLOAD</td>
<td>-15000 = -CLEARS</td>
</tr>
<tr>
<td>MODIFIED_DELETED</td>
<td>X</td><td>Y</td><td>0</td>
<td>yes</td>
<td>3.2.1.2</td>
<td>800 = SLOAD</td>
<td>15000 = CLEARS</td>
</tr>
<tr>
<td>DELETED_RESTORED</td>
<td>X</td><td>0</td><td>X</td>
<td>yes</td>
<td>3.2.1.1 + 3.2.2.2</td>
<td>800 = SLOAD</td>
<td>-15000 = -CLEARS</td>
</tr>
<tr>
<td>ADDED_DELETED</td>
<td>0</td><td>Y</td><td>0</td>
<td>yes</td>
<td>3.2.2.1</td>
<td>800 = SLOAD</td>
<td>19200 = SET - SLOAD</td>
</tr>
<tr>
<td>MODIFIED_RESTORED</td>
<td>X</td><td>Y</td><td>X</td>
<td>yes</td>
<td>3.2.2.2</td>
<td>800 = SLOAD</td>
<td>4200 = RESET - SLOAD</td>
</tr>
</tbody>
</table>

[EIP-2200]: https://eips.ethereum.org/EIPS/eip-2200
2 changes: 1 addition & 1 deletion examples/example_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ExampleHost : public evmc::Host
auto prev_value = account.storage[key];
account.storage[key] = value;

return (prev_value == value) ? EVMC_STORAGE_UNCHANGED : EVMC_STORAGE_MODIFIED;
return (prev_value == value) ? EVMC_STORAGE_MODIFIED_AGAIN : EVMC_STORAGE_MODIFIED;
}

evmc::uint256be get_balance(const evmc::address& addr) const noexcept final
Expand Down
104 changes: 91 additions & 13 deletions include/evmc/evmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,36 +502,114 @@ typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context,
* notation is used:
* - 0 is zero value,
* - X != 0 (X is any value other than 0),
* - Y != X, Y != 0 (Y is any value other than X and 0),
* - Z != Y (Z is any value other than Y),
* - the "->" means the change from one value to another.
* - Y != 0, Y != X, (Y is any value other than X and 0),
* - Z != 0, Z != X, Z != X (Z is any value other than Y and X and 0),
* - the "o -> c -> v" triple describes the change status in the context of:
* - o: original value (cold value before a transaction started),
* - c: current storage value,
* - v: new storage value to be set.
*
* The order of elements follows EIPs introducing net storage gas costs:
* - EIP-2200: https://eips.ethereum.org/EIPS/eip-2200,
* - EIP-1283: https://eips.ethereum.org/EIPS/eip-1283.
*/
enum evmc_storage_status
{
/**
* The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
* The value of a storage item has been left unchanged or has been modified again
* in the transaction context. This is the group of "no-op" cases related to
* minimal gas cost.
* 0|X -> 0 -> 0 (current value unchanged)
* 0|X|Y -> Y -> Y (current value unchanged)
* 0|X -> Y -> Z (modified previously added/modified value)
*
* This is "catch all remaining" status. I.e. if all other statuses are correctly matched
* this status should be assigned to all remaining cases.
*
* This corresponds to the EIP-2200 clause 2 or clause 3.2 without sub-clauses, where:
* c == v or (c != v, o != c, (o == 0 or (o != 0, c != 0, v != 0)), o != v)
*/
EVMC_STORAGE_MODIFIED_AGAIN = 0,

/**
* A new storage item is added by changing
* the current clean zero to a nonzero value.
* 0 -> 0 -> Z
*
* This corresponds to the EIP-2200 clause 3.1.1 where:
* c != v, o == c, c == 0.
*/
EVMC_STORAGE_UNCHANGED = 0,
EVMC_STORAGE_ADDED = 1,

/**
* The value of a storage item has been modified: X -> Y.
* A storage item is deleted by changing
* the current clean nonzero to the zero value.
* X -> X -> 0
*
* This corresponds to the v == 0 variant of the EIP-2200 clause 3.1.2 where:
* c != v, o == c, c != 0, v == 0.
*/
EVMC_STORAGE_MODIFIED = 1,
EVMC_STORAGE_DELETED = 2,

/**
* A storage item has been modified after being modified before: X -> Y -> Z.
* A storage item is modified by changing
* the current clean nonzero to other nonzero value.
* X -> X -> Z
*
* This corresponds to the v != 0 variant of the EIP-2200 clause 3.1.2 where:
* c != v, o == c, c != 0, v != 0.
*/
EVMC_STORAGE_MODIFIED_AGAIN = 2,
EVMC_STORAGE_MODIFIED = 3,

/**
* A new storage item has been added: 0 -> X.
* A storage item is added by changing
* the current dirty zero to a nonzero value other than the original value.
* X -> 0 -> Z
*
* This corresponds to the EIP-2200 clause 3.2.1.1 without the clause 3.2.2, where:
* c != v, o != c, o != 0, c == 0, o != v.
*/
EVMC_STORAGE_ADDED = 3,
EVMC_STORAGE_DELETED_ADDED = 4,

/**
* A storage item has been deleted: X -> 0.
* A storage item is deleted by changing
* the current dirty nonzero to the zero value and the original value is not zero.
* X -> Y -> 0
*
* This corresponds to the EIP-2200 clause 3.2.1.2 where:
* c != v, o != c, o != 0, v == 0 (therefore o != v).
*/
EVMC_STORAGE_MODIFIED_DELETED = 5,

/**
* A storage item is added by changing
* the current dirty zero to the original value.
* X -> 0 -> X
*
* This corresponds to the EIP-2200 clauses 3.2.1.1 and 3.2.2.2 combined, where:
* c != v, o != c, o != 0, c == 0, o == v.
*/
EVMC_STORAGE_DELETED_RESTORED = 6,

/**
* A storage item is deleted by changing
* the current dirty nonzero to the original zero value.
* 0 -> Y -> 0
*
* This corresponds to the EIP-2200 clause 3.2.2.1 where:
* c != v, o != c, o == 0, c != 0, o == v, v == 0.
*/
EVMC_STORAGE_ADDED_DELETED = 7,

/**
* A storage item is modified by changing
* the current dirty nonzero to the original nonzero value other than the current value.
* X -> Y -> X
*
* This corresponds to the EIP-2200 clause 3.2.2.2 where:
* c != v, o != c, o != 0, c != 0, o == v, v != 0.
*/
EVMC_STORAGE_DELETED = 4
EVMC_STORAGE_MODIFIED_RESTORED = 8
};


Expand Down
Loading