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

Support EIP-2929: Gas cost increases for state access opcodes #571

Merged
merged 6 commits into from
Apr 13, 2021
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning].

## [8.0.0] — unreleased

### Added

- Support for **Berlin** [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929):
`access_account()` and `access_storage()` functions added to `evmc_host_interface`.
[#571](https://github.com/ethereum/evmc/pull/571)

## [7.5.0] — 2021-03-23

### Added
Expand Down
12 changes: 12 additions & 0 deletions bindings/go/evmc/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const struct evmc_host_interface evmc_go_host = {
(evmc_get_tx_context_fn)getTxContext,
(evmc_get_block_hash_fn)getBlockHash,
(evmc_emit_log_fn)emitLog,
(evmc_access_account_fn)accessAccount,
(evmc_access_storage_fn)accessStorage,
};


Expand All @@ -46,6 +48,8 @@ static inline void go_exported_functions_type_checks()
(void)tx_context;
struct evmc_result result;
(void)result;
enum evmc_access_status access_status;
(void)access_status;
enum evmc_storage_status storage_status;
(void)storage_status;
bool bool_flag;
Expand Down Expand Up @@ -98,4 +102,12 @@ static inline void go_exported_functions_type_checks()
evmc_emit_log_fn emit_log_fn = NULL;
emit_log_fn(context, address, data, size, &bytes32, size);
emitLog(context, address, data, size, &bytes32, size);

evmc_access_account_fn access_account_fn = NULL;
access_status = access_account_fn(context, address);
access_status = accessAccount(context, address);

evmc_access_storage_fn access_storage_fn = NULL;
access_status = access_storage_fn(context, address, &bytes32);
access_status = accessStorage(context, address, &bytes32);
}
21 changes: 21 additions & 0 deletions bindings/go/evmc/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ const (
Create2 CallKind = C.EVMC_CREATE2
)

type AccessStatus int

const (
ColdAccess AccessStatus = C.EVMC_ACCESS_COLD
WarmAccess AccessStatus = C.EVMC_ACCESS_WARM
)

type StorageStatus int

const (
Expand Down Expand Up @@ -86,6 +93,8 @@ type HostContext interface {
Call(kind CallKind,
destination Address, sender Address, value Hash, input []byte, gas int64, depth int,
static bool, salt Hash) (output []byte, gasLeft int64, createAddr Address, err error)
AccessAccount(addr Address) AccessStatus
AccessStorage(addr Address, key Hash) AccessStatus
}

//export accountExists
Expand Down Expand Up @@ -213,3 +222,15 @@ func call(pCtx unsafe.Pointer, msg *C.struct_evmc_message) C.struct_evmc_result
result.create_address = evmcAddress(createAddr)
return result
}

//export accessAccount
func accessAccount(pCtx unsafe.Pointer, pAddr *C.evmc_address) C.enum_evmc_access_status {
ctx := getHostContext(uintptr(pCtx))
return C.enum_evmc_access_status(ctx.AccessAccount(goAddress(*pAddr)))
}

//export accessStorage
func accessStorage(pCtx unsafe.Pointer, pAddr *C.evmc_address, pKey *C.evmc_bytes32) C.enum_evmc_access_status {
ctx := getHostContext(uintptr(pCtx))
return C.enum_evmc_access_status(ctx.AccessStorage(goAddress(*pAddr), goHash(*pKey)))
}
8 changes: 8 additions & 0 deletions bindings/go/evmc/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ func (host *testHostContext) Call(kind CallKind,
return output, gas, Address{}, nil
}

func (host *testHostContext) AccessAccount(addr Address) AccessStatus {
return ColdAccess
}

func (host *testHostContext) AccessStorage(addr Address, key Hash) AccessStatus {
return ColdAccess
}

func TestGetBlockNumberFromTxContext(t *testing.T) {
// Yul: mstore(0, number()) return(0, msize())
code := []byte("\x43\x60\x00\x52\x59\x60\x00\xf3")
Expand Down
64 changes: 61 additions & 3 deletions bindings/java/c/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,12 +436,70 @@ static void emit_log_fn(struct evmc_host_context* context,
data_size, jtopics, topics_count);
}

static enum evmc_access_status access_account_fn(struct evmc_host_context* context,
const evmc_address* address)
{
const char java_method_name[] = "access_account";
const char java_method_signature[] = "(Lorg/ethereum/evmc/HostContext;[B)I";

assert(context != NULL);
JNIEnv* jenv = attach();

// get java class
jclass host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
assert(host_class != NULL);

// get java method
jmethodID method =
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
assert(method != NULL);

// set java method params
jbyteArray jaddress = CopyDataToJava(jenv, address, sizeof(struct evmc_address));

// call java method
jint jresult =
(*jenv)->CallStaticIntMethod(jenv, host_class, method, (jobject)context, jaddress);
assert(jresult == EVMC_ACCESS_COLD || jresult == EVMC_ACCESS_WARM);
return (enum evmc_access_status)jresult;
}

static enum evmc_access_status access_storage_fn(struct evmc_host_context* context,
const evmc_address* address,
const evmc_bytes32* key)
{
const char java_method_name[] = "access_storage";
const char java_method_signature[] = "(Lorg/ethereum/evmc/HostContext;[B[B)I";

assert(context != NULL);
JNIEnv* jenv = attach();

// get java class
jclass host_class = (*jenv)->FindClass(jenv, "org/ethereum/evmc/Host");
assert(host_class != NULL);

// get java method
jmethodID method =
(*jenv)->GetStaticMethodID(jenv, host_class, java_method_name, java_method_signature);
assert(method != NULL);

// set java method params
jbyteArray jaddress = CopyDataToJava(jenv, address, sizeof(struct evmc_address));
jbyteArray jkey = CopyDataToJava(jenv, key, sizeof(struct evmc_bytes32));

// call java method
jint jresult =
(*jenv)->CallStaticIntMethod(jenv, host_class, method, (jobject)context, jaddress, jkey);
assert(jresult == EVMC_ACCESS_COLD || jresult == EVMC_ACCESS_WARM);
return (enum evmc_access_status)jresult;
}

const struct evmc_host_interface* evmc_java_get_host_interface()
{
static const struct evmc_host_interface host = {
account_exists_fn, get_storage_fn, set_storage_fn, get_balance_fn,
get_code_size_fn, get_code_hash_fn, copy_code_fn, selfdestruct_fn,
call_fn, get_tx_context_fn, get_block_hash_fn, emit_log_fn,
account_exists_fn, get_storage_fn, set_storage_fn, get_balance_fn, get_code_size_fn,
get_code_hash_fn, copy_code_fn, selfdestruct_fn, call_fn, get_tx_context_fn,
get_block_hash_fn, emit_log_fn, access_account_fn, access_storage_fn,
};
return &host;
}
10 changes: 10 additions & 0 deletions bindings/java/java/src/main/java/org/ethereum/evmc/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,14 @@ static void emit_log(
int topic_count) {
context.emitLog(address, data, data_size, topics, topic_count);
}

/** Access account callback function. */
static int access_account(HostContext context, byte[] address) {
return context.accessAccount(address);
}

/** Access storage callback function. */
static int access_storage(HostContext context, byte[] address, byte[] key) {
return context.accessStorage(address, key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,31 @@ public interface HostContext {
*/
boolean accountExists(byte[] address);

/**
* Access account function.
*
* <p>This function is used by the VM to add the given address to accessed_addresses substate (see
* EIP-2929).
*
* @param address The address of the account.
* @return 0 if cold access, 1 if warm access.
* @todo Change return type to enum.
*/
int accessAccount(byte[] address);
Copy link
Member

@axic axic Mar 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use boolean if it is a bool. accountExists does that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used enum for clarity. It's not immediately obvious whether true means cold access or warm access.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In C yes, but in Java it seems to be only a number :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can leave a todo here to change this to enum or bool.


/**
* Access storage function.
*
* <p>This function is used by the VM to add the given account storage entry to
* accessed_storage_keys substate (see EIP-2929).
*
* @param address The address of the account.
* @param key The index of the account's storage entry.
* @return 0 if cold access, 1 if warm access.
yperbasis marked this conversation as resolved.
Show resolved Hide resolved
* @todo Change return type to enum.
*/
int accessStorage(byte[] address, byte[] key);

/**
* Get storage function.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ public boolean accountExists(byte[] address) {
return true;
}

@Override
public int accessAccount(byte[] address) {
return 0;
}

@Override
public int accessStorage(byte[] address, byte[] key) {
return 0;
}

@Override
public ByteBuffer getStorage(byte[] address, byte[] key) {
return ByteBuffer.allocateDirect(32).put(new byte[32]);
Expand Down
2 changes: 2 additions & 0 deletions bindings/rust/evmc-vm/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ mod tests {
get_tx_context: Some(get_dummy_tx_context),
get_block_hash: None,
emit_log: None,
access_account: None,
access_storage: None,
};
let host_context = std::ptr::null_mut();

Expand Down
22 changes: 22 additions & 0 deletions bindings/rust/evmc-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,26 @@ impl<'a> ExecutionContext<'a> {
)
}
}

/// Access an account.
pub fn access_account(&mut self, address: &Address) -> AccessStatus {
unsafe {
assert!((*self.host).access_account.is_some());
(*self.host).access_account.unwrap()(self.context, address as *const Address)
}
}

/// Access a storage key.
pub fn access_storage(&mut self, address: &Address, key: &Bytes32) -> AccessStatus {
unsafe {
assert!((*self.host).access_storage.is_some());
(*self.host).access_storage.unwrap()(
self.context,
address as *const Address,
key as *const Bytes32,
)
}
}
}

impl From<ffi::evmc_result> for ExecutionResult {
Expand Down Expand Up @@ -793,6 +813,8 @@ mod tests {
get_tx_context: Some(get_dummy_tx_context),
get_block_hash: None,
emit_log: None,
access_account: None,
access_storage: None,
}
}

Expand Down
15 changes: 15 additions & 0 deletions bindings/rust/evmc-vm/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub type MessageFlags = ffi::evmc_flags;
/// EVMC status code.
pub type StatusCode = ffi::evmc_status_code;

/// EVMC access status.
pub type AccessStatus = ffi::evmc_access_status;

/// EVMC storage status.
pub type StorageStatus = ffi::evmc_storage_status;

Expand Down Expand Up @@ -81,6 +84,18 @@ mod tests {
);
}

#[test]
fn access_status() {
assert_eq!(
AccessStatus::EVMC_ACCESS_COLD,
ffi::evmc_access_status::EVMC_ACCESS_COLD
);
assert_eq!(
AccessStatus::EVMC_ACCESS_WARM,
ffi::evmc_access_status::EVMC_ACCESS_WARM
);
}

#[test]
fn storage_status() {
assert_eq!(
Expand Down
14 changes: 14 additions & 0 deletions examples/example_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,20 @@ class ExampleHost : public evmc::Host
(void)topics;
(void)topics_count;
}

evmc_access_status access_account(const evmc::address& addr) noexcept final
{
(void)addr;
return EVMC_ACCESS_COLD;
}

evmc_access_status access_storage(const evmc::address& addr,
const evmc::bytes32& key) noexcept final
{
(void)addr;
(void)key;
return EVMC_ACCESS_COLD;
}
};


Expand Down
52 changes: 52 additions & 0 deletions include/evmc/evmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,52 @@ typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context,
const evmc_bytes32 topics[],
size_t topics_count);

/**
* Access status per EIP-2929: Gas cost increases for state access opcodes.
*/
enum evmc_access_status
{
/**
* The entry hasn't been accessed before – it's the first access.
*/
EVMC_ACCESS_COLD = 0,

/**
* The entry is already in accessed_addresses or accessed_storage_keys.
*/
EVMC_ACCESS_WARM = 1
};

/**
* Access account callback function.
*
* This callback function is used by a VM to add the given address
* to accessed_addresses substate (EIP-2929).
*
* @param context The Host execution context.
* @param address The address of the account.
* @return EVMC_ACCESS_WARM if accessed_addresses already contained the address
* or EVMC_ACCESS_COLD otherwise.
*/
typedef enum evmc_access_status (*evmc_access_account_fn)(struct evmc_host_context* context,
const evmc_address* address);

/**
* Access storage callback function.
*
* This callback function is used by a VM to add the given account storage entry
* to accessed_storage_keys substate (EIP-2929).
*
* @param context The Host execution context.
* @param address The address of the account.
* @param key The index of the account's storage entry.
* @return EVMC_ACCESS_WARM if accessed_storage_keys already contained the key
* or EVMC_ACCESS_COLD otherwise.
*/
typedef enum evmc_access_status (*evmc_access_storage_fn)(struct evmc_host_context* context,
const evmc_address* address,
const evmc_bytes32* key);

/**
* Pointer to the callback function supporting EVM calls.
*
Expand Down Expand Up @@ -658,6 +704,12 @@ struct evmc_host_interface

/** Emit log callback function. */
evmc_emit_log_fn emit_log;

/** Access account callback function. */
evmc_access_account_fn access_account;

/** Access storage callback function. */
evmc_access_storage_fn access_storage;
};


Expand Down
Loading