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

Report duplicated selfdestructs back to EVM #662

Merged
merged 1 commit into from
Aug 10, 2022
Merged
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ 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)
- C++: The `evmc::result` has been renamed to `evmc::Result` for consistency
- 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 `evmc::result` has been renamed to `evmc::Result` for consistency
with C++ types of similar kind.
[#665](https://github.com/ethereum/evmc/pull/665)
- C++: The `HostContext` does not cache transaction context (`evmc_tx_context`) anymore.
Expand Down
4 changes: 2 additions & 2 deletions bindings/go/evmc/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions bindings/go/evmc/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion bindings/go/evmc/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions bindings/java/c/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions bindings/java/java/src/main/java/org/ethereum/evmc/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion bindings/rust/evmc-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()(
Expand Down
3 changes: 2 additions & 1 deletion examples/example_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion include/evmc/evmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -625,8 +625,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);

Expand Down
10 changes: 5 additions & 5 deletions include/evmc/evmc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,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;
Expand Down Expand Up @@ -542,9 +542,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
Expand Down Expand Up @@ -787,11 +787,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
Expand Down
27 changes: 7 additions & 20 deletions include/evmc/mocked_host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<address, MockedAccount> accounts;

Expand Down Expand Up @@ -138,8 +122,9 @@ class MockedHost : public Host
/// The record of all LOGs passed to the emit_log() method.
std::vector<log_record> recorded_logs;

/// The record of all SELFDESTRUCTs from the selfdestruct() method.
std::vector<selfdestruct_record> recorded_selfdestructs;
/// The record of all SELFDESTRUCTs from the selfdestruct() method
/// as a map selfdestructed_address => [beneficiary1, beneficiary2, ...].
std::unordered_map<address, std::vector<address>> recorded_selfdestructs;

private:
/// The copy of call inputs for the recorded_calls record.
Expand Down Expand Up @@ -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).
Expand Down
6 changes: 4 additions & 2 deletions test/unittests/cpp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 { return evmc::Result{}; }

Expand Down
24 changes: 24 additions & 0 deletions test/unittests/mocked_host_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(), 1u);
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(), 2u);
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(), 1u);
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(), 2u);
EXPECT_EQ(host.recorded_selfdestructs[0xdead02_address][0], 0xbece01_address);
EXPECT_EQ(host.recorded_selfdestructs[0xdead02_address][1], 0xbece01_address);
}