From 85e48af18d400cf37414c3a5026874b0ee1483d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 9 Aug 2022 09:54:30 +0200 Subject: [PATCH] Report duplicated selfdestructs back to EVM Return from the selfdestruct method the information if the given address has not been registered as selfdestructed yet. --- CHANGELOG.md | 3 +++ bindings/go/evmc/host.c | 4 +-- bindings/go/evmc/host.go | 6 ++--- bindings/go/evmc/host_test.go | 3 ++- bindings/java/c/host.c | 11 ++++---- .../src/main/java/org/ethereum/evmc/Host.java | 4 +-- .../java/org/ethereum/evmc/HostContext.java | 4 ++- .../org/ethereum/evmc/TestHostContext.java | 4 ++- bindings/rust/evmc-vm/src/lib.rs | 2 +- examples/example_host.cpp | 3 ++- include/evmc/evmc.h | 4 ++- include/evmc/evmc.hpp | 10 +++---- include/evmc/mocked_host.hpp | 27 +++++-------------- test/unittests/cpp_test.cpp | 6 +++-- test/unittests/mocked_host_test.cpp | 24 +++++++++++++++++ 15 files changed, 70 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 419c1d25e..b4179040a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,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 `selfdestruct` method returns the information if the given address + has not been registered as selfdestructed yet. + [#662](https://github.com/ethereum/evmc/pull/662) - C++: The `HostContext` does not cache transaction context (`evmc_tx_context`) anymore. [#631](https://github.com/ethereum/evmc/pull/631) - Go: The `create2Salt` parameter has been removed from the `VM.Execute()`. diff --git a/bindings/go/evmc/host.c b/bindings/go/evmc/host.c index a753b5328..908b65d03 100644 --- a/bindings/go/evmc/host.c +++ b/bindings/go/evmc/host.c @@ -83,8 +83,8 @@ static inline void go_exported_functions_type_checks() size = copyCode(context, address, size, data, size); evmc_selfdestruct_fn selfdestruct_fn = NULL; - selfdestruct_fn(context, address, address); - selfdestruct(context, address, address); + bool_flag = selfdestruct_fn(context, address, address); + bool_flag = selfdestruct(context, address, address); evmc_call_fn call_fn = NULL; result = call_fn(context, message); diff --git a/bindings/go/evmc/host.go b/bindings/go/evmc/host.go index 78769f93c..cd3ac845b 100644 --- a/bindings/go/evmc/host.go +++ b/bindings/go/evmc/host.go @@ -87,7 +87,7 @@ type HostContext interface { GetCodeSize(addr Address) int GetCodeHash(addr Address) Hash GetCode(addr Address) []byte - Selfdestruct(addr Address, beneficiary Address) + Selfdestruct(addr Address, beneficiary Address) bool GetTxContext() TxContext GetBlockHash(number int64) Hash EmitLog(addr Address, topics []Hash, data []byte) @@ -155,9 +155,9 @@ func copyCode(pCtx unsafe.Pointer, pAddr *C.evmc_address, offset C.size_t, p *C. } //export selfdestruct -func selfdestruct(pCtx unsafe.Pointer, pAddr *C.evmc_address, pBeneficiary *C.evmc_address) { +func selfdestruct(pCtx unsafe.Pointer, pAddr *C.evmc_address, pBeneficiary *C.evmc_address) C.bool { ctx := getHostContext(uintptr(pCtx)) - ctx.Selfdestruct(goAddress(*pAddr), goAddress(*pBeneficiary)) + return C.bool(ctx.Selfdestruct(goAddress(*pAddr), goAddress(*pBeneficiary))) } //export getTxContext diff --git a/bindings/go/evmc/host_test.go b/bindings/go/evmc/host_test.go index 599a44019..81bc80d13 100644 --- a/bindings/go/evmc/host_test.go +++ b/bindings/go/evmc/host_test.go @@ -39,7 +39,8 @@ func (host *testHostContext) GetCode(addr Address) []byte { return nil } -func (host *testHostContext) Selfdestruct(addr Address, beneficiary Address) { +func (host *testHostContext) Selfdestruct(addr Address, beneficiary Address) bool { + return false } func (host *testHostContext) GetTxContext() TxContext { diff --git a/bindings/java/c/host.c b/bindings/java/c/host.c index dc6baf675..24be6b743 100644 --- a/bindings/java/c/host.c +++ b/bindings/java/c/host.c @@ -70,7 +70,7 @@ static bool account_exists_fn(struct evmc_host_context* context, const evmc_addr // call java method jboolean jresult = (*jenv)->CallStaticBooleanMethod(jenv, host_class, method, (jobject)context, jaddress); - return jresult != 0; + return jresult != JNI_FALSE; } static evmc_bytes32 get_storage_fn(struct evmc_host_context* context, @@ -281,12 +281,12 @@ static size_t copy_code_fn(struct evmc_host_context* context, return length; } -static void selfdestruct_fn(struct evmc_host_context* context, +static bool selfdestruct_fn(struct evmc_host_context* context, const evmc_address* address, const evmc_address* beneficiary) { const char java_method_name[] = "selfdestruct"; - const char java_method_signature[] = "(Lorg/ethereum/evmc/HostContext;[B[B)V"; + const char java_method_signature[] = "(Lorg/ethereum/evmc/HostContext;[B[B)Z"; assert(context != NULL); JNIEnv* jenv = attach(); @@ -305,8 +305,9 @@ static void selfdestruct_fn(struct evmc_host_context* context, jbyteArray jbeneficiary = CopyDataToJava(jenv, beneficiary, sizeof(struct evmc_address)); // call java method - (*jenv)->CallStaticIntMethod(jenv, host_class, method, (jobject)context, jaddress, - jbeneficiary); + jboolean jresult = (*jenv)->CallStaticBooleanMethod(jenv, host_class, method, (jobject)context, + jaddress, jbeneficiary); + return jresult != JNI_FALSE; } static struct evmc_result call_fn(struct evmc_host_context* context, const struct evmc_message* msg) diff --git a/bindings/java/java/src/main/java/org/ethereum/evmc/Host.java b/bindings/java/java/src/main/java/org/ethereum/evmc/Host.java index 2e0643570..83bf57b75 100644 --- a/bindings/java/java/src/main/java/org/ethereum/evmc/Host.java +++ b/bindings/java/java/src/main/java/org/ethereum/evmc/Host.java @@ -54,8 +54,8 @@ static ByteBuffer copy_code(HostContext context, byte[] address) { } /** Selfdestruct callback function. */ - static void selfdestruct(HostContext context, byte[] address, byte[] beneficiary) { - context.selfdestruct(address, beneficiary); + static boolean selfdestruct(HostContext context, byte[] address, byte[] beneficiary) { + return context.selfdestruct(address, beneficiary); } /** Call callback function. */ diff --git a/bindings/java/java/src/main/java/org/ethereum/evmc/HostContext.java b/bindings/java/java/src/main/java/org/ethereum/evmc/HostContext.java index 0b4af4439..7a8b874a7 100644 --- a/bindings/java/java/src/main/java/org/ethereum/evmc/HostContext.java +++ b/bindings/java/java/src/main/java/org/ethereum/evmc/HostContext.java @@ -125,8 +125,10 @@ public interface HostContext { * * @param address The address of the contract to be selfdestructed. * @param beneficiary The address where the remaining ETH is going to be transferred. + * @return The information if the given address has not been registered as selfdestructed yet. + * True if registered for the first time, false otherwise. */ - void selfdestruct(byte[] address, byte[] beneficiary); + boolean selfdestruct(byte[] address, byte[] beneficiary); /** * This function supports EVM calls. diff --git a/bindings/java/java/src/test/java/org/ethereum/evmc/TestHostContext.java b/bindings/java/java/src/test/java/org/ethereum/evmc/TestHostContext.java index 0519fc28d..b70d5679a 100644 --- a/bindings/java/java/src/test/java/org/ethereum/evmc/TestHostContext.java +++ b/bindings/java/java/src/test/java/org/ethereum/evmc/TestHostContext.java @@ -52,7 +52,9 @@ public ByteBuffer getCode(byte[] address) { } @Override - public void selfdestruct(byte[] address, byte[] beneficiary) {} + public boolean selfdestruct(byte[] address, byte[] beneficiary) { + return false; + } @Override public ByteBuffer call(ByteBuffer msg) { diff --git a/bindings/rust/evmc-vm/src/lib.rs b/bindings/rust/evmc-vm/src/lib.rs index 69751d884..0bc379297 100644 --- a/bindings/rust/evmc-vm/src/lib.rs +++ b/bindings/rust/evmc-vm/src/lib.rs @@ -300,7 +300,7 @@ impl<'a> ExecutionContext<'a> { } /// Self-destruct the current account. - pub fn selfdestruct(&mut self, address: &Address, beneficiary: &Address) { + pub fn selfdestruct(&mut self, address: &Address, beneficiary: &Address) -> bool { unsafe { assert!((*self.host).selfdestruct.is_some()); (*self.host).selfdestruct.unwrap()( diff --git a/examples/example_host.cpp b/examples/example_host.cpp index f2316c3ae..3b7632581 100644 --- a/examples/example_host.cpp +++ b/examples/example_host.cpp @@ -125,10 +125,11 @@ class ExampleHost : public evmc::Host return n; } - void selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept final + bool selfdestruct(const evmc::address& addr, const evmc::address& beneficiary) noexcept final { (void)addr; (void)beneficiary; + return false; } evmc::result call(const evmc_message& msg) noexcept final diff --git a/include/evmc/evmc.h b/include/evmc/evmc.h index 5c957b926..7ccd12e6b 100644 --- a/include/evmc/evmc.h +++ b/include/evmc/evmc.h @@ -626,8 +626,10 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context, * @param context The pointer to the Host execution context. See ::evmc_host_context. * @param address The address of the contract to be selfdestructed. * @param beneficiary The address where the remaining ETH is going to be transferred. + * @return The information if the given address has not been registered as + * selfdestructed yet. True if registered for the first time, false otherwise. */ -typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context, +typedef bool (*evmc_selfdestruct_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_address* beneficiary); diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp index d24a28b4a..bae2c220a 100644 --- a/include/evmc/evmc.hpp +++ b/include/evmc/evmc.hpp @@ -433,7 +433,7 @@ class HostInterface size_t buffer_size) const noexcept = 0; /// @copydoc evmc_host_interface::selfdestruct - virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0; + virtual bool selfdestruct(const address& addr, const address& beneficiary) noexcept = 0; /// @copydoc evmc_host_interface::call virtual result call(const evmc_message& msg) noexcept = 0; @@ -518,9 +518,9 @@ class HostContext : public HostInterface return host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } - void selfdestruct(const address& addr, const address& beneficiary) noexcept final + bool selfdestruct(const address& addr, const address& beneficiary) noexcept final { - host->selfdestruct(context, &addr, &beneficiary); + return host->selfdestruct(context, &addr, &beneficiary); } result call(const evmc_message& message) noexcept final @@ -763,11 +763,11 @@ inline size_t copy_code(evmc_host_context* h, return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); } -inline void selfdestruct(evmc_host_context* h, +inline bool selfdestruct(evmc_host_context* h, const evmc_address* addr, const evmc_address* beneficiary) noexcept { - Host::from_context(h)->selfdestruct(*addr, *beneficiary); + return Host::from_context(h)->selfdestruct(*addr, *beneficiary); } inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept diff --git a/include/evmc/mocked_host.hpp b/include/evmc/mocked_host.hpp index 2a54154c0..5dc117436 100644 --- a/include/evmc/mocked_host.hpp +++ b/include/evmc/mocked_host.hpp @@ -90,22 +90,6 @@ class MockedHost : public Host } }; - /// SELFDESTRUCT record. - struct selfdestruct_record - { - /// The address of the account which has self-destructed. - address selfdestructed; - - /// The address of the beneficiary account. - address beneficiary; - - /// Equal operator. - bool operator==(const selfdestruct_record& other) const noexcept - { - return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary; - } - }; - /// The set of all accounts in the Host, organized by their addresses. std::unordered_map accounts; @@ -138,8 +122,9 @@ class MockedHost : public Host /// The record of all LOGs passed to the emit_log() method. std::vector recorded_logs; - /// The record of all SELFDESTRUCTs from the selfdestruct() method. - std::vector recorded_selfdestructs; + /// The record of all SELFDESTRUCTs from the selfdestruct() method + /// as a map selfdestructed_address => [beneficiary1, beneficiary2, ...]. + std::unordered_map> recorded_selfdestructs; private: /// The copy of call inputs for the recorded_calls record. @@ -271,10 +256,12 @@ class MockedHost : public Host } /// Selfdestruct the account (EVMC host method). - void selfdestruct(const address& addr, const address& beneficiary) noexcept override + bool selfdestruct(const address& addr, const address& beneficiary) noexcept override { record_account_access(addr); - recorded_selfdestructs.push_back({addr, beneficiary}); + auto& beneficiaries = recorded_selfdestructs[addr]; + beneficiaries.emplace_back(beneficiary); + return beneficiaries.size() == 1; } /// Call/create other contract (EVMC host method). diff --git a/test/unittests/cpp_test.cpp b/test/unittests/cpp_test.cpp index 7ee5b172b..dbf99f3e3 100644 --- a/test/unittests/cpp_test.cpp +++ b/test/unittests/cpp_test.cpp @@ -50,9 +50,11 @@ class NullHost : public evmc::Host return 0; } - void selfdestruct(const evmc::address& /*addr*/, + bool selfdestruct(const evmc::address& /*addr*/, const evmc::address& /*beneficiary*/) noexcept final - {} + { + return false; + } evmc::result call(const evmc_message& /*msg*/) noexcept final { diff --git a/test/unittests/mocked_host_test.cpp b/test/unittests/mocked_host_test.cpp index fad51264e..0cc171c7b 100644 --- a/test/unittests/mocked_host_test.cpp +++ b/test/unittests/mocked_host_test.cpp @@ -70,3 +70,27 @@ TEST(mocked_host, storage) EXPECT_EQ(host.set_storage(addr2, val3, val1), EVMC_STORAGE_DELETED); EXPECT_EQ(chost.get_storage(addr2, val3), val1); } + +TEST(mocked_host, selfdestruct) +{ + evmc::MockedHost host; + EXPECT_TRUE(host.recorded_selfdestructs.empty()); + + EXPECT_TRUE(host.selfdestruct(0xdead01_address, 0xbece01_address)); + ASSERT_EQ(host.recorded_selfdestructs[0xdead01_address].size(), 1); + EXPECT_EQ(host.recorded_selfdestructs[0xdead01_address][0], 0xbece01_address); + + EXPECT_FALSE(host.selfdestruct(0xdead01_address, 0xbece02_address)); + ASSERT_EQ(host.recorded_selfdestructs[0xdead01_address].size(), 2); + EXPECT_EQ(host.recorded_selfdestructs[0xdead01_address][0], 0xbece01_address); + EXPECT_EQ(host.recorded_selfdestructs[0xdead01_address][1], 0xbece02_address); + + EXPECT_TRUE(host.selfdestruct(0xdead02_address, 0xbece01_address)); + ASSERT_EQ(host.recorded_selfdestructs[0xdead02_address].size(), 1); + EXPECT_EQ(host.recorded_selfdestructs[0xdead02_address][0], 0xbece01_address); + + EXPECT_FALSE(host.selfdestruct(0xdead02_address, 0xbece01_address)); + ASSERT_EQ(host.recorded_selfdestructs[0xdead02_address].size(), 2); + EXPECT_EQ(host.recorded_selfdestructs[0xdead02_address][0], 0xbece01_address); + EXPECT_EQ(host.recorded_selfdestructs[0xdead02_address][1], 0xbece01_address); +}