Skip to content

Commit

Permalink
Merge pull request #661 from ethereum/extend_storage_status
Browse files Browse the repository at this point in the history
Extend storage status
  • Loading branch information
chfast authored Aug 12, 2022
2 parents ebe37b9 + b91b723 commit 32fc145
Show file tree
Hide file tree
Showing 11 changed files with 626 additions and 74 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ and this project adheres to [Semantic Versioning].
- The `evmc_message::destination` field has been renamed to `evmc_message::recipient`
to clarify its purpose and match the naming from the Yellow Paper.
[#616](https://github.com/ethereum/evmc/pull/616)
- The `evmc_storage_status` has been extended to provide information about every possible case of
storage net gas metering ([EIP-2200](https://eips.ethereum.org/EIPS/eip-2200)).
[#661](https://github.com/ethereum/evmc/pull/661)
- The `selfdestruct` method returns the information if the given address
has not been registered as selfdestructed yet.
[#662](https://github.com/ethereum/evmc/pull/662)
Expand Down
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
StorageAssigned StorageStatus = C.EVMC_STORAGE_ASSIGNED
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 StorageAssigned
}

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_ASSIGNED,
ffi::evmc_storage_status::EVMC_STORAGE_ASSIGNED
);
assert_eq!(
StorageStatus::EVMC_STORAGE_MODIFIED,
Expand Down
333 changes: 333 additions & 0 deletions docs/EVM_Storage_Change_Status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
# EVM Storage Change Status {#storagestatus}

The description of ::evmc_storage_status enum design and its relation
to the specification in [EIP-2200] and others.

## Specification

> This is the [EIP-2200] specification with modifications:
> - the clause tree has been converted to ordered lists to be referenceable,
> - the cost constant names has been replaced with matching Yellow Paper names.
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), G<sub>warmaccess</sub>
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, G<sub>sset</sub> is deducted.
2. Otherwise, G<sub>sreset</sub> gas is deducted.
1. If *new value* is 0,
add R<sub>sclear</sub> gas to refund counter.
2. If *original value* does not equal *current value* (this storage
slot is dirty), G<sub>warmaccess</sub> 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 R<sub>sclear</sub> gas from refund
counter.
2. If *new value* is 0 (also means that *current value* is not
0), add R<sub>sclear</sub> gas to refund counter.
2. If *original value* equals *new value* (this storage slot is
reset)
1. If *original value* is 0, add G<sub>sset</sub> - G<sub>warmaccess</sub> to
refund counter.
2. Otherwise, add G<sub>sreset</sub> - G<sub>warmaccess</sub> gas to refund
counter.

### Cost constants

| Yellow Paper | EIP-2200 | EIP-2200 Value | EIP-2929 Value |
|------------------------|---------------------------|----------------|----------------|
| G<sub>warmaccess</sub> | `SLOAD_GAS` | 800 | 100 |
| G<sub>sset</sub> | `SSTORE_SET_GAS` | 20000 | 20000 |
| G<sub>sreset</sub> | `SSTORE_RESET_GAS` | 5000 | 2900 |
| R<sub>sclear</sub> | `SSTORE_CLEARS_SCHEDULE` | 15000 | 15000 |

## Storage change statuses

- `0` - zero value
- `X` - non-zero value
- `Y` - non-zero value different from `X`
- `Z` - non-zero value different form `X` and `Y`
- `o` - original value
- `c` - current value
- `v` - new value

<table>
<tr>
<th>name</th>
<th>o</th>
<th>c</th>
<th>v</th>
<th>dirty</th>
<th>restored</th>
<th>clause</th>
<th>gas cost</th>
<th>gas refund</th>
</tr>
<tr>
<td rowspan="7">[ASSIGNED]</td>
<td>0</td><td>0</td><td>0</td>
<td>no</td>
<td>yes</td>
<td>2</td>
<td rowspan="7">G<sub>warmaccess</sub></td>
<td rowspan="7">0</td>
</tr>
<tr>
<td>X</td><td>0</td><td>0</td>
<td>yes</td>
<td>no</td>
<td>2</td>
</tr>
<tr>
<td>0</td><td>Y</td><td>Y</td>
<td>yes</td>
<td>no</td>
<td>2</td>
</tr>
<tr>
<td>X</td><td>Y</td><td>Y</td>
<td>yes</td>
<td>no</td>
<td>2</td>
</tr>
<tr>
<td>Y</td><td>Y</td><td>Y</td>
<td>no</td>
<td>yes</td>
<td>2</td>
</tr>
<tr>
<td>0</td><td>Y</td><td>Z</td>
<td>yes</td>
<td>no</td>
<td>3.2</td>
</tr>
<tr>
<td>X</td><td>Y</td><td>Z</td>
<td>yes</td>
<td>no</td>
<td>3.2</td>
</tr>
<tr>
<td>[ADDED]</td>
<td>0</td><td>0</td><td>Z</td>
<td>no</td>
<td>no</td>
<td>3.1.1</td>
<td>G<sub>sset</sub></td>
<td>0</td>
</tr>
<tr>
<td>[DELETED]</td>
<td>X</td><td>X</td><td>0</td>
<td>no</td>
<td>no</td>
<td>3.1.2.1</td>
<td>G<sub>sreset</sub></td>
<td>R<sub>sclear</sub></td>
</tr>
<tr>
<td>[MODIFIED]</td>
<td>X</td><td>X</td><td>Z</td>
<td>no</td>
<td>no</td>
<td>3.1.2</td>
<td>G<sub>sreset</sub></td>
<td>0</td>
</tr>
<tr>
<td>[DELETED_ADDED]</td>
<td>X</td><td>0</td><td>Z</td>
<td>yes</td>
<td>no</td>
<td>3.2.1.1</td>
<td>G<sub>warmaccess</sub></td>
<td>-R<sub>sclear</sub></td>
</tr>
<tr>
<td>[MODIFIED_DELETED]</td>
<td>X</td><td>Y</td><td>0</td>
<td>yes</td>
<td>no</td>
<td>3.2.1.2</td>
<td>G<sub>warmaccess</sub></td>
<td>R<sub>sclear</sub></td>
</tr>
<tr>
<td>[DELETED_RESTORED]</td>
<td>X</td><td>0</td><td>X</td>
<td>yes</td>
<td>yes</td>
<td>3.2.1.1 + 3.2.2.2</td>
<td>G<sub>warmaccess</sub></td>
<td>-R<sub>sclear</sub> + G<sub>sreset</sub> - G<sub>warmaccess</sub></td>
</tr>
<tr>
<td>[ADDED_DELETED]</td>
<td>0</td><td>Y</td><td>0</td>
<td>yes</td>
<td>yes</td>
<td>3.2.2.1</td>
<td>G<sub>warmaccess</sub></td>
<td>G<sub>sset</sub> - G<sub>warmaccess</sub></td>
</tr>
<tr>
<td>[MODIFIED_RESTORED]</td>
<td>X</td><td>Y</td><td>X</td>
<td>yes</td>
<td>yes</td>
<td>3.2.2.2</td>
<td>G<sub>warmaccess</sub></td>
<td>G<sub>sreset</sub> - G<sub>warmaccess</sub></td>
</tr>
</table>


## Efficient implementation

All distinctive storage change statuses can be unambiguously selected
by combination of the 4 following checks:
- **o ≠ c**, i.e. `original != current` (dirty),
- **o = v**, i.e `original == new` (restored),
- **c ≠ 0**, i.e `current != 0`,
- **v ≠ 0**, i.e `new != 0`.

<table>
<tr>
<th>name</th>
<th>o</th>
<th>c</th>
<th>v</th>
<th>o ≠ c</th>
<th>o = v</th>
<th>c ≠ 0</th>
<th>v ≠ 0</th>
<th>checksum</th>
<th>proof</th>
</tr>
<tr>
<td rowspan="7">[ASSIGNED]</td>
<td>0</td><td>0</td><td>0</td>
<td>0</td><td>1</td><td>0</td><td>0</td>
<td>4 (0b0100)</td>
</tr>
<tr>
<td>X</td><td>0</td><td>0</td>
<td>1</td><td>0</td><td>0</td><td>0</td>
<td>8 (0b1000)</td>
</tr>
<tr>
<td>0</td><td>Y</td><td>Y</td>
<td>1</td><td>0</td><td>1</td><td>1</td>
<td>11 (0b1011)</td>
</tr>
<tr>
<td>X</td><td>Y</td><td>Y</td>
<td>1</td><td>0</td><td>1</td><td>1</td>
<td>11 (0b1011)</td>
</tr>
<tr>
<td>Y</td><td>Y</td><td>Y</td>
<td>0</td><td>1</td><td>1</td><td>1</td>
<td>7 (0b0111)</td>
</tr>
<tr>
<td>0</td><td>Y</td><td>Z</td>
<td>1</td><td>0</td><td>1</td><td>1</td>
<td>11 (0b1011)</td>
</tr>
<tr>
<td>X</td><td>Y</td><td>Z</td>
<td>1</td><td>0</td><td>1</td><td>1</td>
<td>11 (0b1011)</td>
</tr>
<tr>
<td>[ADDED]</td>
<td>0</td><td>0</td><td>Z</td>
<td>0</td><td>0</td><td>0</td><td>1</td>
<td>1 (0b0001)</td>
</tr>
<tr>
<td>[DELETED]</td>
<td>X</td><td>X</td><td>0</td>
<td>0</td><td>0</td><td>1</td><td>0</td>
<td>2 (0b0010)</td>
</tr>
<tr>
<td>[MODIFIED]</td>
<td>X</td><td>X</td><td>Z</td>
<td>0</td><td>0</td><td>1</td><td>1</td>
<td>3 (0b0011)</td>
</tr>
<tr>
<td>[DELETED_ADDED]</td>
<td>X</td><td>0</td><td>Z</td>
<td>1</td><td>0</td><td>0</td><td>1</td>
<td>9 (0b1001)</td>
</tr>
<tr>
<td>[MODIFIED_DELETED]</td>
<td>X</td><td>Y</td><td>0</td>
<td>1</td><td>0</td><td>1</td><td>0</td>
<td>10 (0b1010)</td>
</tr>
<tr>
<td>[DELETED_RESTORED]</td>
<td>X</td><td>0</td><td>X</td>
<td>1</td><td>1</td><td>0</td><td>1</td>
<td>13 (0b1101)</td>
</tr>
<tr>
<td>[ADDED_DELETED]</td>
<td>0</td><td>Y</td><td>0</td>
<td>1</td><td>1</td><td>1</td><td>0</td>
<td>14 (0b1110)</td>
</tr>
<tr>
<td>[MODIFIED_RESTORED]</td>
<td>X</td><td>Y</td><td>X</td>
<td>1</td><td>1</td><td>1</td><td>1</td>
<td>15 (0b1111)</td>
</tr>
<tr>
<td rowspan="4">impossible</td>
<td></td><td></td><td></td>
<td>0</td><td>0</td><td>0</td><td>0</td>
<td>0 (0b0000)</td>
<td>o=c ∧ o≠v ∧ c=0 ⇒ v≠0</td>
</tr>
<tr>
<td></td><td></td><td></td>
<td>0</td><td>1</td><td>0</td><td>1</td>
<td>5 (0b0101)</td>
<td>o=c ∧ o=v ∧ c=0 ⇒ v=0</td>
</tr>
<tr>
<td></td><td></td><td></td>
<td>0</td><td>1</td><td>1</td><td>0</td>
<td>6 (0b0110)</td>
<td>o=c ∧ o=v ∧ c≠0 ⇒ v≠0</td>
</tr>
<tr>
<td></td><td></td><td></td>
<td>1</td><td>1</td><td>0</td><td>0</td>
<td>12 (0b1100)</td>
<td>o≠c ∧ o=v ∧ c=0 ⇒ v≠0</td>
</tr>
</table>


[EIP-2200]: https://eips.ethereum.org/EIPS/eip-2200
[ASSIGNED]: @ref EVMC_STORAGE_ASSIGNED
[ADDED]: @ref EVMC_STORAGE_ADDED
[DELETED]: @ref EVMC_STORAGE_DELETED
[MODIFIED]: @ref EVMC_STORAGE_MODIFIED
[DELETED_ADDED]: @ref EVMC_STORAGE_DELETED_ADDED
[MODIFIED_DELETED]: @ref EVMC_STORAGE_MODIFIED_DELETED
[DELETED_RESTORED]: @ref EVMC_STORAGE_DELETED_RESTORED
[ADDED_DELETED]: @ref EVMC_STORAGE_ADDED_DELETED
[MODIFIED_RESTORED]: @ref EVMC_STORAGE_MODIFIED_RESTORED
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_ASSIGNED : EVMC_STORAGE_MODIFIED;
}

evmc::uint256be get_balance(const evmc::address& addr) const noexcept final
Expand Down
Loading

0 comments on commit 32fc145

Please sign in to comment.