From 03dc899e6c740a0eb504d9fea3f7028cfdd57479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 4 Aug 2022 07:52:45 +0200 Subject: [PATCH] Extend storage status Expand the number of storage statuses to cover all possible EVM behaviors. These provide enough information to VM to compute not only gas costs but also refunds. Including legacy SSTORE costs before net gas metering. --- bindings/go/evmc/host.go | 14 ++++-- bindings/go/evmc/host_test.go | 2 +- bindings/rust/evmc-vm/src/types.rs | 4 +- examples/example_host.cpp | 2 +- include/evmc/evmc.h | 74 ++++++++++++++++++++++++----- include/evmc/mocked_host.hpp | 2 +- test/unittests/cpp_test.cpp | 2 +- test/unittests/mocked_host_test.cpp | 4 +- 8 files changed, 78 insertions(+), 26 deletions(-) diff --git a/bindings/go/evmc/host.go b/bindings/go/evmc/host.go index 78769f93c..612977197 100644 --- a/bindings/go/evmc/host.go +++ b/bindings/go/evmc/host.go @@ -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 { diff --git a/bindings/go/evmc/host_test.go b/bindings/go/evmc/host_test.go index 599a44019..8ca693c59 100644 --- a/bindings/go/evmc/host_test.go +++ b/bindings/go/evmc/host_test.go @@ -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 { diff --git a/bindings/rust/evmc-vm/src/types.rs b/bindings/rust/evmc-vm/src/types.rs index 336400a57..19e103bf7 100644 --- a/bindings/rust/evmc-vm/src/types.rs +++ b/bindings/rust/evmc-vm/src/types.rs @@ -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, diff --git a/examples/example_host.cpp b/examples/example_host.cpp index f2316c3ae..3f4b0d660 100644 --- a/examples/example_host.cpp +++ b/examples/example_host.cpp @@ -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 diff --git a/include/evmc/evmc.h b/include/evmc/evmc.h index 5c957b926..840415d79 100644 --- a/include/evmc/evmc.h +++ b/include/evmc/evmc.h @@ -503,36 +503,84 @@ 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 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) + */ + EVMC_STORAGE_MODIFIED_AGAIN = 0, + + /** + * A new storage item is added by changing + * the current clean zero to a nonzero value. + * 0 -> 0 -> Z + */ + EVMC_STORAGE_ADDED = 1, + + /** + * A storage item is deleted by changing + * the current clean nonzero to the zero value. + * X -> X -> 0 + */ + EVMC_STORAGE_DELETED = 2, + + /** + * A storage item is modified by changing + * the current clean nonzero to other nonzero value. + * X -> X -> Z + */ + EVMC_STORAGE_MODIFIED = 3, + + /** + * A storage item is added by changing + * the current dirty zero to a nonzero value other than the original value. + * X -> 0 -> Y */ - EVMC_STORAGE_UNCHANGED = 0, + EVMC_STORAGE_DELETED_ADDED = 4, /** - * The value of a storage item has been modified: X -> Y. + * 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 */ - EVMC_STORAGE_MODIFIED = 1, + EVMC_STORAGE_MODIFIED_DELETED = 5, /** - * A storage item has been modified after being modified before: X -> Y -> Z. + * A storage item is added by changing + * the current dirty zero to the original value. + * X -> 0 -> X */ - EVMC_STORAGE_MODIFIED_AGAIN = 2, + EVMC_STORAGE_DELETED_RESTORED = 6, /** - * A new storage item has been added: 0 -> X. + * A storage item is deleted by changing + * the current dirty nonzero to the original zero value. + * 0 -> Y -> 0 */ - EVMC_STORAGE_ADDED = 3, + EVMC_STORAGE_ADDED_DELETED = 7, /** - * A storage item has been deleted: X -> 0. + * A storage item is modified by changing + * the current dirty nonzero to the original nonzero value other than the current value. + * X -> Y -> X */ - EVMC_STORAGE_DELETED = 4 + EVMC_STORAGE_MODIFIED_RESTORED = 8 }; diff --git a/include/evmc/mocked_host.hpp b/include/evmc/mocked_host.hpp index 2a54154c0..28a6f5e81 100644 --- a/include/evmc/mocked_host.hpp +++ b/include/evmc/mocked_host.hpp @@ -196,7 +196,7 @@ class MockedHost : public Host // WARNING! This is not complete implementation as refund is not handled here. if (old.value == value) - return EVMC_STORAGE_UNCHANGED; + return EVMC_STORAGE_MODIFIED_AGAIN; evmc_storage_status status{}; if (!old.dirty) diff --git a/test/unittests/cpp_test.cpp b/test/unittests/cpp_test.cpp index 7ee5b172b..b5c38ede3 100644 --- a/test/unittests/cpp_test.cpp +++ b/test/unittests/cpp_test.cpp @@ -646,7 +646,7 @@ TEST(cpp, host) EXPECT_TRUE(host.account_exists(a)); EXPECT_EQ(host.set_storage(a, {}, v), EVMC_STORAGE_MODIFIED); - EXPECT_EQ(host.set_storage(a, {}, v), EVMC_STORAGE_UNCHANGED); + EXPECT_EQ(host.set_storage(a, {}, v), EVMC_STORAGE_MODIFIED_AGAIN); EXPECT_EQ(host.get_storage(a, {}), v); EXPECT_TRUE(evmc::is_zero(host.get_balance(a))); diff --git a/test/unittests/mocked_host_test.cpp b/test/unittests/mocked_host_test.cpp index fad51264e..091ea286e 100644 --- a/test/unittests/mocked_host_test.cpp +++ b/test/unittests/mocked_host_test.cpp @@ -46,7 +46,7 @@ TEST(mocked_host, storage) EXPECT_EQ(host.set_storage(addr2, val1, val2), EVMC_STORAGE_ADDED); EXPECT_EQ(chost.get_storage(addr2, val1), val2); EXPECT_EQ(acc2.storage.count(val1), 1u); - EXPECT_EQ(host.set_storage(addr2, val1, val2), EVMC_STORAGE_UNCHANGED); + EXPECT_EQ(host.set_storage(addr2, val1, val2), EVMC_STORAGE_MODIFIED_AGAIN); EXPECT_EQ(chost.get_storage(addr2, val1), val2); EXPECT_EQ(acc2.storage.count(val1), 1u); EXPECT_EQ(host.set_storage(addr2, val1, val3), EVMC_STORAGE_MODIFIED_AGAIN); @@ -62,7 +62,7 @@ TEST(mocked_host, storage) acc2.storage[val3] = val2; EXPECT_EQ(chost.get_storage(addr2, val3), val2); EXPECT_FALSE(acc2.storage.find(val3)->second.dirty); - EXPECT_EQ(host.set_storage(addr2, val3, val2), EVMC_STORAGE_UNCHANGED); + EXPECT_EQ(host.set_storage(addr2, val3, val2), EVMC_STORAGE_MODIFIED_AGAIN); EXPECT_EQ(chost.get_storage(addr2, val3), val2); EXPECT_EQ(host.set_storage(addr2, val3, val3), EVMC_STORAGE_MODIFIED); EXPECT_EQ(chost.get_storage(addr2, val3), val3);