diff --git a/CHANGELOG.md b/CHANGELOG.md
index f84442a6c..578888e4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/bindings/go/evmc/host.c b/bindings/go/evmc/host.c
index 3d0b55a94..e2c9d70be 100644
--- a/bindings/go/evmc/host.c
+++ b/bindings/go/evmc/host.c
@@ -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,
};
@@ -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;
@@ -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);
}
diff --git a/bindings/go/evmc/host.go b/bindings/go/evmc/host.go
index c0d70c4fa..8769429c0 100644
--- a/bindings/go/evmc/host.go
+++ b/bindings/go/evmc/host.go
@@ -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 (
@@ -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
@@ -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)))
+}
diff --git a/bindings/go/evmc/host_test.go b/bindings/go/evmc/host_test.go
index 07b6a24da..1986d0059 100644
--- a/bindings/go/evmc/host_test.go
+++ b/bindings/go/evmc/host_test.go
@@ -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")
diff --git a/bindings/java/c/host.c b/bindings/java/c/host.c
index 0938f9232..0fb8ffb36 100644
--- a/bindings/java/c/host.c
+++ b/bindings/java/c/host.c
@@ -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;
}
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 5cf9fe8bb..be7b3db6c 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
@@ -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);
+ }
}
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 1650a9ae5..313816c06 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
@@ -20,6 +20,31 @@ public interface HostContext {
*/
boolean accountExists(byte[] address);
+ /**
+ * Access account function.
+ *
+ *
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);
+
+ /**
+ * Access storage function.
+ *
+ *
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.
+ * @todo Change return type to enum.
+ */
+ int accessStorage(byte[] address, byte[] key);
+
/**
* Get storage function.
*
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 3759bc241..6b2b6b557 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
@@ -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]);
diff --git a/bindings/rust/evmc-vm/src/container.rs b/bindings/rust/evmc-vm/src/container.rs
index 682561e82..88fa814fa 100644
--- a/bindings/rust/evmc-vm/src/container.rs
+++ b/bindings/rust/evmc-vm/src/container.rs
@@ -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();
diff --git a/bindings/rust/evmc-vm/src/lib.rs b/bindings/rust/evmc-vm/src/lib.rs
index 85d4ded35..3fc517d6a 100644
--- a/bindings/rust/evmc-vm/src/lib.rs
+++ b/bindings/rust/evmc-vm/src/lib.rs
@@ -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 for ExecutionResult {
@@ -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,
}
}
diff --git a/bindings/rust/evmc-vm/src/types.rs b/bindings/rust/evmc-vm/src/types.rs
index 3b7ba601d..336400a57 100644
--- a/bindings/rust/evmc-vm/src/types.rs
+++ b/bindings/rust/evmc-vm/src/types.rs
@@ -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;
@@ -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!(
diff --git a/examples/example_host.cpp b/examples/example_host.cpp
index 82a44db7b..90316eaf9 100644
--- a/examples/example_host.cpp
+++ b/examples/example_host.cpp
@@ -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;
+ }
};
diff --git a/include/evmc/evmc.h b/include/evmc/evmc.h
index deac9d2f8..5de5f5dae 100644
--- a/include/evmc/evmc.h
+++ b/include/evmc/evmc.h
@@ -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.
*
@@ -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;
};
diff --git a/include/evmc/evmc.hpp b/include/evmc/evmc.hpp
index fe72656ce..6d0bac9f2 100644
--- a/include/evmc/evmc.hpp
+++ b/include/evmc/evmc.hpp
@@ -465,6 +465,12 @@ class HostInterface
size_t data_size,
const bytes32 topics[],
size_t num_topics) noexcept = 0;
+
+ /// @copydoc evmc_host_interface::access_account
+ virtual evmc_access_status access_account(const address& addr) noexcept = 0;
+
+ /// @copydoc evmc_host_interface::access_storage
+ virtual evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept = 0;
};
@@ -564,6 +570,16 @@ class HostContext : public HostInterface
{
host->emit_log(context, &addr, data, data_size, topics, topics_count);
}
+
+ evmc_access_status access_account(const address& address) noexcept final
+ {
+ return host->access_account(context, &address);
+ }
+
+ evmc_access_status access_storage(const address& address, const bytes32& key) noexcept final
+ {
+ return host->access_storage(context, &address, &key);
+ }
};
@@ -805,6 +821,18 @@ inline void emit_log(evmc_host_context* h,
Host::from_context(h)->emit_log(*addr, data, data_size, static_cast(topics),
num_topics);
}
+
+inline evmc_access_status access_account(evmc_host_context* h, const evmc_address* addr) noexcept
+{
+ return Host::from_context(h)->access_account(*addr);
+}
+
+inline evmc_access_status access_storage(evmc_host_context* h,
+ const evmc_address* addr,
+ const evmc_bytes32* key) noexcept
+{
+ return Host::from_context(h)->access_storage(*addr, *key);
+}
} // namespace internal
inline const evmc_host_interface& Host::get_interface() noexcept
@@ -815,7 +843,9 @@ inline const evmc_host_interface& Host::get_interface() noexcept
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
::evmc::internal::copy_code, ::evmc::internal::selfdestruct,
::evmc::internal::call, ::evmc::internal::get_tx_context,
- ::evmc::internal::get_block_hash, ::evmc::internal::emit_log};
+ ::evmc::internal::get_block_hash, ::evmc::internal::emit_log,
+ ::evmc::internal::access_account, ::evmc::internal::access_storage,
+ };
return interface;
}
} // namespace evmc
diff --git a/include/evmc/mocked_host.hpp b/include/evmc/mocked_host.hpp
index 57f0cb79b..9244204ce 100644
--- a/include/evmc/mocked_host.hpp
+++ b/include/evmc/mocked_host.hpp
@@ -23,6 +23,9 @@ struct storage_value
/// True means this value has been modified already by the current transaction.
bool dirty{false};
+ /// Is the storage key cold or warm.
+ evmc_access_status access_status{EVMC_ACCESS_COLD};
+
/// Default constructor.
storage_value() noexcept = default;
@@ -30,6 +33,11 @@ struct storage_value
storage_value(const bytes32& _value, bool _dirty = false) noexcept // NOLINT
: value{_value}, dirty{_dirty}
{}
+
+ /// Constructor with initial access status.
+ storage_value(const bytes32& _value, evmc_access_status _access_status) noexcept
+ : value{_value}, access_status{_access_status}
+ {}
};
/// Mocked account.
@@ -137,7 +145,6 @@ class MockedHost : public Host
/// The copy of call inputs for the recorded_calls record.
std::vector m_recorded_calls_inputs;
-public:
/// Record an account access.
/// @param addr The address of the accessed account.
void record_account_access(const address& addr) const
@@ -149,6 +156,7 @@ class MockedHost : public Host
recorded_account_accesses.emplace_back(addr);
}
+public:
/// Returns true if an account exists (EVMC Host method).
bool account_exists(const address& addr) const noexcept override
{
@@ -312,5 +320,33 @@ class MockedHost : public Host
{
recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
}
+
+ /// Record an account access.
+ /// @param addr The address of the accessed account.
+ evmc_access_status access_account(const address& addr) noexcept override
+ {
+ // Check if the address have been already accessed.
+ const auto already_accessed =
+ std::find(recorded_account_accesses.begin(), recorded_account_accesses.end(), addr) !=
+ recorded_account_accesses.end();
+
+ record_account_access(addr);
+
+ // Accessing precompiled contracts is always warm.
+ if (addr >= 0x0000000000000000000000000000000000000001_address &&
+ addr <= 0x0000000000000000000000000000000000000009_address)
+ return EVMC_ACCESS_WARM;
+
+ return already_accessed ? EVMC_ACCESS_WARM : EVMC_ACCESS_COLD;
+ }
+
+ /// Access the account's storage value at the given key.
+ evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override
+ {
+ auto& value = accounts[addr].storage[key];
+ const auto access_status = value.access_status;
+ value.access_status = EVMC_ACCESS_WARM;
+ return access_status;
+ }
};
} // namespace evmc
diff --git a/lib/instructions/instruction_metrics.c b/lib/instructions/instruction_metrics.c
index fb0f7dcf6..9bdd029dc 100644
--- a/lib/instructions/instruction_metrics.c
+++ b/lib/instructions/instruction_metrics.c
@@ -26,6 +26,270 @@
/** @} */
+/**
+ * Defined in EIP-2929: Gas cost increases for state access opcodes.
+ */
+#define WARM_STORAGE_READ_COST 100
+
+static struct evmc_instruction_metrics berlin_metrics[256] = {
+ /* STOP = 0x00 */ {ZERO, 0, 0},
+ /* ADD = 0x01 */ {VERYLOW, 2, -1},
+ /* MUL = 0x02 */ {LOW, 2, -1},
+ /* SUB = 0x03 */ {VERYLOW, 2, -1},
+ /* DIV = 0x04 */ {LOW, 2, -1},
+ /* SDIV = 0x05 */ {LOW, 2, -1},
+ /* MOD = 0x06 */ {LOW, 2, -1},
+ /* SMOD = 0x07 */ {LOW, 2, -1},
+ /* ADDMOD = 0x08 */ {MID, 3, -2},
+ /* MULMOD = 0x09 */ {MID, 3, -2},
+ /* EXP = 0x0a */ {HIGH, 2, -1},
+ /* SIGNEXTEND = 0x0b */ {LOW, 2, -1},
+ /* = 0x0c */ {UNDEFINED, 0, 0},
+ /* = 0x0d */ {UNDEFINED, 0, 0},
+ /* = 0x0e */ {UNDEFINED, 0, 0},
+ /* = 0x0f */ {UNDEFINED, 0, 0},
+ /* LT = 0x10 */ {VERYLOW, 2, -1},
+ /* GT = 0x11 */ {VERYLOW, 2, -1},
+ /* SLT = 0x12 */ {VERYLOW, 2, -1},
+ /* SGT = 0x13 */ {VERYLOW, 2, -1},
+ /* EQ = 0x14 */ {VERYLOW, 2, -1},
+ /* ISZERO = 0x15 */ {VERYLOW, 1, 0},
+ /* AND = 0x16 */ {VERYLOW, 2, -1},
+ /* OR = 0x17 */ {VERYLOW, 2, -1},
+ /* XOR = 0x18 */ {VERYLOW, 2, -1},
+ /* NOT = 0x19 */ {VERYLOW, 1, 0},
+ /* BYTE = 0x1a */ {VERYLOW, 2, -1},
+ /* SHL = 0x1b */ {VERYLOW, 2, -1},
+ /* SHR = 0x1c */ {VERYLOW, 2, -1},
+ /* SAR = 0x1d */ {VERYLOW, 2, -1},
+ /* = 0x1e */ {UNDEFINED, 0, 0},
+ /* = 0x1f */ {UNDEFINED, 0, 0},
+ /* SHA3 = 0x20 */ {30, 2, -1},
+ /* = 0x21 */ {UNDEFINED, 0, 0},
+ /* = 0x22 */ {UNDEFINED, 0, 0},
+ /* = 0x23 */ {UNDEFINED, 0, 0},
+ /* = 0x24 */ {UNDEFINED, 0, 0},
+ /* = 0x25 */ {UNDEFINED, 0, 0},
+ /* = 0x26 */ {UNDEFINED, 0, 0},
+ /* = 0x27 */ {UNDEFINED, 0, 0},
+ /* = 0x28 */ {UNDEFINED, 0, 0},
+ /* = 0x29 */ {UNDEFINED, 0, 0},
+ /* = 0x2a */ {UNDEFINED, 0, 0},
+ /* = 0x2b */ {UNDEFINED, 0, 0},
+ /* = 0x2c */ {UNDEFINED, 0, 0},
+ /* = 0x2d */ {UNDEFINED, 0, 0},
+ /* = 0x2e */ {UNDEFINED, 0, 0},
+ /* = 0x2f */ {UNDEFINED, 0, 0},
+ /* ADDRESS = 0x30 */ {BASE, 0, 1},
+ /* BALANCE = 0x31 */ {WARM_STORAGE_READ_COST, 1, 0},
+ /* ORIGIN = 0x32 */ {BASE, 0, 1},
+ /* CALLER = 0x33 */ {BASE, 0, 1},
+ /* CALLVALUE = 0x34 */ {BASE, 0, 1},
+ /* CALLDATALOAD = 0x35 */ {VERYLOW, 1, 0},
+ /* CALLDATASIZE = 0x36 */ {BASE, 0, 1},
+ /* CALLDATACOPY = 0x37 */ {VERYLOW, 3, -3},
+ /* CODESIZE = 0x38 */ {BASE, 0, 1},
+ /* CODECOPY = 0x39 */ {VERYLOW, 3, -3},
+ /* GASPRICE = 0x3a */ {BASE, 0, 1},
+ /* EXTCODESIZE = 0x3b */ {WARM_STORAGE_READ_COST, 1, 0},
+ /* EXTCODECOPY = 0x3c */ {WARM_STORAGE_READ_COST, 4, -4},
+ /* RETURNDATASIZE = 0x3d */ {BASE, 0, 1},
+ /* RETURNDATACOPY = 0x3e */ {VERYLOW, 3, -3},
+ /* EXTCODEHASH = 0x3f */ {WARM_STORAGE_READ_COST, 1, 0},
+ /* BLOCKHASH = 0x40 */ {20, 1, 0},
+ /* COINBASE = 0x41 */ {BASE, 0, 1},
+ /* TIMESTAMP = 0x42 */ {BASE, 0, 1},
+ /* NUMBER = 0x43 */ {BASE, 0, 1},
+ /* DIFFICULTY = 0x44 */ {BASE, 0, 1},
+ /* GASLIMIT = 0x45 */ {BASE, 0, 1},
+ /* CHAINID = 0x46 */ {BASE, 0, 1},
+ /* SELFBALANCE = 0x47 */ {LOW, 0, 1},
+ /* = 0x48 */ {UNDEFINED, 0, 0},
+ /* = 0x49 */ {UNDEFINED, 0, 0},
+ /* = 0x4a */ {UNDEFINED, 0, 0},
+ /* = 0x4b */ {UNDEFINED, 0, 0},
+ /* = 0x4c */ {UNDEFINED, 0, 0},
+ /* = 0x4d */ {UNDEFINED, 0, 0},
+ /* = 0x4e */ {UNDEFINED, 0, 0},
+ /* = 0x4f */ {UNDEFINED, 0, 0},
+ /* POP = 0x50 */ {BASE, 1, -1},
+ /* MLOAD = 0x51 */ {VERYLOW, 1, 0},
+ /* MSTORE = 0x52 */ {VERYLOW, 2, -2},
+ /* MSTORE8 = 0x53 */ {VERYLOW, 2, -2},
+ /* SLOAD = 0x54 */ {WARM_STORAGE_READ_COST, 1, 0},
+ /* SSTORE = 0x55 */ {0, 2, -2},
+ /* JUMP = 0x56 */ {MID, 1, -1},
+ /* JUMPI = 0x57 */ {HIGH, 2, -2},
+ /* PC = 0x58 */ {BASE, 0, 1},
+ /* MSIZE = 0x59 */ {BASE, 0, 1},
+ /* GAS = 0x5a */ {BASE, 0, 1},
+ /* JUMPDEST = 0x5b */ {1, 0, 0},
+ /* = 0x5c */ {UNDEFINED, 0, 0},
+ /* = 0x5d */ {UNDEFINED, 0, 0},
+ /* = 0x5e */ {UNDEFINED, 0, 0},
+ /* = 0x5f */ {UNDEFINED, 0, 0},
+ /* PUSH1 = 0x60 */ {VERYLOW, 0, 1},
+ /* PUSH2 = 0x61 */ {VERYLOW, 0, 1},
+ /* PUSH3 = 0x62 */ {VERYLOW, 0, 1},
+ /* PUSH4 = 0x63 */ {VERYLOW, 0, 1},
+ /* PUSH5 = 0x64 */ {VERYLOW, 0, 1},
+ /* PUSH6 = 0x65 */ {VERYLOW, 0, 1},
+ /* PUSH7 = 0x66 */ {VERYLOW, 0, 1},
+ /* PUSH8 = 0x67 */ {VERYLOW, 0, 1},
+ /* PUSH9 = 0x68 */ {VERYLOW, 0, 1},
+ /* PUSH10 = 0x69 */ {VERYLOW, 0, 1},
+ /* PUSH11 = 0x6a */ {VERYLOW, 0, 1},
+ /* PUSH12 = 0x6b */ {VERYLOW, 0, 1},
+ /* PUSH13 = 0x6c */ {VERYLOW, 0, 1},
+ /* PUSH14 = 0x6d */ {VERYLOW, 0, 1},
+ /* PUSH15 = 0x6e */ {VERYLOW, 0, 1},
+ /* PUSH16 = 0x6f */ {VERYLOW, 0, 1},
+ /* PUSH17 = 0x70 */ {VERYLOW, 0, 1},
+ /* PUSH18 = 0x71 */ {VERYLOW, 0, 1},
+ /* PUSH19 = 0x72 */ {VERYLOW, 0, 1},
+ /* PUSH20 = 0x73 */ {VERYLOW, 0, 1},
+ /* PUSH21 = 0x74 */ {VERYLOW, 0, 1},
+ /* PUSH22 = 0x75 */ {VERYLOW, 0, 1},
+ /* PUSH23 = 0x76 */ {VERYLOW, 0, 1},
+ /* PUSH24 = 0x77 */ {VERYLOW, 0, 1},
+ /* PUSH25 = 0x78 */ {VERYLOW, 0, 1},
+ /* PUSH26 = 0x79 */ {VERYLOW, 0, 1},
+ /* PUSH27 = 0x7a */ {VERYLOW, 0, 1},
+ /* PUSH28 = 0x7b */ {VERYLOW, 0, 1},
+ /* PUSH29 = 0x7c */ {VERYLOW, 0, 1},
+ /* PUSH30 = 0x7d */ {VERYLOW, 0, 1},
+ /* PUSH31 = 0x7e */ {VERYLOW, 0, 1},
+ /* PUSH32 = 0x7f */ {VERYLOW, 0, 1},
+ /* DUP1 = 0x80 */ {VERYLOW, 1, 1},
+ /* DUP2 = 0x81 */ {VERYLOW, 2, 1},
+ /* DUP3 = 0x82 */ {VERYLOW, 3, 1},
+ /* DUP4 = 0x83 */ {VERYLOW, 4, 1},
+ /* DUP5 = 0x84 */ {VERYLOW, 5, 1},
+ /* DUP6 = 0x85 */ {VERYLOW, 6, 1},
+ /* DUP7 = 0x86 */ {VERYLOW, 7, 1},
+ /* DUP8 = 0x87 */ {VERYLOW, 8, 1},
+ /* DUP9 = 0x88 */ {VERYLOW, 9, 1},
+ /* DUP10 = 0x89 */ {VERYLOW, 10, 1},
+ /* DUP11 = 0x8a */ {VERYLOW, 11, 1},
+ /* DUP12 = 0x8b */ {VERYLOW, 12, 1},
+ /* DUP13 = 0x8c */ {VERYLOW, 13, 1},
+ /* DUP14 = 0x8d */ {VERYLOW, 14, 1},
+ /* DUP15 = 0x8e */ {VERYLOW, 15, 1},
+ /* DUP16 = 0x8f */ {VERYLOW, 16, 1},
+ /* SWAP1 = 0x90 */ {VERYLOW, 2, 0},
+ /* SWAP2 = 0x91 */ {VERYLOW, 3, 0},
+ /* SWAP3 = 0x92 */ {VERYLOW, 4, 0},
+ /* SWAP4 = 0x93 */ {VERYLOW, 5, 0},
+ /* SWAP5 = 0x94 */ {VERYLOW, 6, 0},
+ /* SWAP6 = 0x95 */ {VERYLOW, 7, 0},
+ /* SWAP7 = 0x96 */ {VERYLOW, 8, 0},
+ /* SWAP8 = 0x97 */ {VERYLOW, 9, 0},
+ /* SWAP9 = 0x98 */ {VERYLOW, 10, 0},
+ /* SWAP10 = 0x99 */ {VERYLOW, 11, 0},
+ /* SWAP11 = 0x9a */ {VERYLOW, 12, 0},
+ /* SWAP12 = 0x9b */ {VERYLOW, 13, 0},
+ /* SWAP13 = 0x9c */ {VERYLOW, 14, 0},
+ /* SWAP14 = 0x9d */ {VERYLOW, 15, 0},
+ /* SWAP15 = 0x9e */ {VERYLOW, 16, 0},
+ /* SWAP16 = 0x9f */ {VERYLOW, 17, 0},
+ /* LOG0 = 0xa0 */ {1 * 375, 2, -2},
+ /* LOG1 = 0xa1 */ {2 * 375, 3, -3},
+ /* LOG2 = 0xa2 */ {3 * 375, 4, -4},
+ /* LOG3 = 0xa3 */ {4 * 375, 5, -5},
+ /* LOG4 = 0xa4 */ {5 * 375, 6, -6},
+ /* = 0xa5 */ {UNDEFINED, 0, 0},
+ /* = 0xa6 */ {UNDEFINED, 0, 0},
+ /* = 0xa7 */ {UNDEFINED, 0, 0},
+ /* = 0xa8 */ {UNDEFINED, 0, 0},
+ /* = 0xa9 */ {UNDEFINED, 0, 0},
+ /* = 0xaa */ {UNDEFINED, 0, 0},
+ /* = 0xab */ {UNDEFINED, 0, 0},
+ /* = 0xac */ {UNDEFINED, 0, 0},
+ /* = 0xad */ {UNDEFINED, 0, 0},
+ /* = 0xae */ {UNDEFINED, 0, 0},
+ /* = 0xaf */ {UNDEFINED, 0, 0},
+ /* = 0xb0 */ {UNDEFINED, 0, 0},
+ /* = 0xb1 */ {UNDEFINED, 0, 0},
+ /* = 0xb2 */ {UNDEFINED, 0, 0},
+ /* = 0xb3 */ {UNDEFINED, 0, 0},
+ /* = 0xb4 */ {UNDEFINED, 0, 0},
+ /* = 0xb5 */ {UNDEFINED, 0, 0},
+ /* = 0xb6 */ {UNDEFINED, 0, 0},
+ /* = 0xb7 */ {UNDEFINED, 0, 0},
+ /* = 0xb8 */ {UNDEFINED, 0, 0},
+ /* = 0xb9 */ {UNDEFINED, 0, 0},
+ /* = 0xba */ {UNDEFINED, 0, 0},
+ /* = 0xbb */ {UNDEFINED, 0, 0},
+ /* = 0xbc */ {UNDEFINED, 0, 0},
+ /* = 0xbd */ {UNDEFINED, 0, 0},
+ /* = 0xbe */ {UNDEFINED, 0, 0},
+ /* = 0xbf */ {UNDEFINED, 0, 0},
+ /* = 0xc0 */ {UNDEFINED, 0, 0},
+ /* = 0xc1 */ {UNDEFINED, 0, 0},
+ /* = 0xc2 */ {UNDEFINED, 0, 0},
+ /* = 0xc3 */ {UNDEFINED, 0, 0},
+ /* = 0xc4 */ {UNDEFINED, 0, 0},
+ /* = 0xc5 */ {UNDEFINED, 0, 0},
+ /* = 0xc6 */ {UNDEFINED, 0, 0},
+ /* = 0xc7 */ {UNDEFINED, 0, 0},
+ /* = 0xc8 */ {UNDEFINED, 0, 0},
+ /* = 0xc9 */ {UNDEFINED, 0, 0},
+ /* = 0xca */ {UNDEFINED, 0, 0},
+ /* = 0xcb */ {UNDEFINED, 0, 0},
+ /* = 0xcc */ {UNDEFINED, 0, 0},
+ /* = 0xcd */ {UNDEFINED, 0, 0},
+ /* = 0xce */ {UNDEFINED, 0, 0},
+ /* = 0xcf */ {UNDEFINED, 0, 0},
+ /* = 0xd0 */ {UNDEFINED, 0, 0},
+ /* = 0xd1 */ {UNDEFINED, 0, 0},
+ /* = 0xd2 */ {UNDEFINED, 0, 0},
+ /* = 0xd3 */ {UNDEFINED, 0, 0},
+ /* = 0xd4 */ {UNDEFINED, 0, 0},
+ /* = 0xd5 */ {UNDEFINED, 0, 0},
+ /* = 0xd6 */ {UNDEFINED, 0, 0},
+ /* = 0xd7 */ {UNDEFINED, 0, 0},
+ /* = 0xd8 */ {UNDEFINED, 0, 0},
+ /* = 0xd9 */ {UNDEFINED, 0, 0},
+ /* = 0xda */ {UNDEFINED, 0, 0},
+ /* = 0xdb */ {UNDEFINED, 0, 0},
+ /* = 0xdc */ {UNDEFINED, 0, 0},
+ /* = 0xdd */ {UNDEFINED, 0, 0},
+ /* = 0xde */ {UNDEFINED, 0, 0},
+ /* = 0xdf */ {UNDEFINED, 0, 0},
+ /* = 0xe0 */ {UNDEFINED, 0, 0},
+ /* = 0xe1 */ {UNDEFINED, 0, 0},
+ /* = 0xe2 */ {UNDEFINED, 0, 0},
+ /* = 0xe3 */ {UNDEFINED, 0, 0},
+ /* = 0xe4 */ {UNDEFINED, 0, 0},
+ /* = 0xe5 */ {UNDEFINED, 0, 0},
+ /* = 0xe6 */ {UNDEFINED, 0, 0},
+ /* = 0xe7 */ {UNDEFINED, 0, 0},
+ /* = 0xe8 */ {UNDEFINED, 0, 0},
+ /* = 0xe9 */ {UNDEFINED, 0, 0},
+ /* = 0xea */ {UNDEFINED, 0, 0},
+ /* = 0xeb */ {UNDEFINED, 0, 0},
+ /* = 0xec */ {UNDEFINED, 0, 0},
+ /* = 0xed */ {UNDEFINED, 0, 0},
+ /* = 0xee */ {UNDEFINED, 0, 0},
+ /* = 0xef */ {UNDEFINED, 0, 0},
+ /* CREATE = 0xf0 */ {32000, 3, -2},
+ /* CALL = 0xf1 */ {WARM_STORAGE_READ_COST, 7, -6},
+ /* CALLCODE = 0xf2 */ {WARM_STORAGE_READ_COST, 7, -6},
+ /* RETURN = 0xf3 */ {ZERO, 2, -2},
+ /* DELEGATECALL = 0xf4 */ {WARM_STORAGE_READ_COST, 6, -5},
+ /* CREATE2 = 0xf5 */ {32000, 4, -3},
+ /* = 0xf6 */ {UNDEFINED, 0, 0},
+ /* = 0xf7 */ {UNDEFINED, 0, 0},
+ /* = 0xf8 */ {UNDEFINED, 0, 0},
+ /* = 0xf9 */ {UNDEFINED, 0, 0},
+ /* STATICCALL = 0xfa */ {WARM_STORAGE_READ_COST, 6, -5},
+ /* = 0xfb */ {UNDEFINED, 0, 0},
+ /* = 0xfc */ {UNDEFINED, 0, 0},
+ /* REVERT = 0xfd */ {ZERO, 2, -2},
+ /* INVALID = 0xfe */ {ZERO, 0, 0},
+ /* SELFDESTRUCT = 0xff */ {5000, 1, -1},
+};
+
static struct evmc_instruction_metrics istanbul_metrics[256] = {
/* STOP = 0x00 */ {ZERO, 0, 0},
/* ADD = 0x01 */ {VERYLOW, 2, -1},
@@ -1585,8 +1849,9 @@ const struct evmc_instruction_metrics* evmc_get_instruction_metrics_table(
{
switch (revision)
{
- case EVMC_ISTANBUL:
case EVMC_BERLIN:
+ return berlin_metrics;
+ case EVMC_ISTANBUL:
return istanbul_metrics;
case EVMC_PETERSBURG:
case EVMC_CONSTANTINOPLE:
diff --git a/test/unittests/cpp_test.cpp b/test/unittests/cpp_test.cpp
index 01030338e..94b1d398c 100644
--- a/test/unittests/cpp_test.cpp
+++ b/test/unittests/cpp_test.cpp
@@ -59,6 +59,16 @@ class NullHost : public evmc::Host
const evmc::bytes32[],
size_t) noexcept final
{}
+
+ evmc_access_status access_account(const evmc::address&) noexcept final
+ {
+ return EVMC_ACCESS_COLD;
+ }
+
+ evmc_access_status access_storage(const evmc::address&, const evmc::bytes32&) noexcept final
+ {
+ return EVMC_ACCESS_COLD;
+ }
};
TEST(cpp, address)
diff --git a/test/unittests/instructions_test.cpp b/test/unittests/instructions_test.cpp
index f60708dde..33f03f7bf 100644
--- a/test/unittests/instructions_test.cpp
+++ b/test/unittests/instructions_test.cpp
@@ -296,7 +296,34 @@ TEST(instructions, berlin_hard_fork)
for (int op{OP_STOP}; op <= OP_SELFDESTRUCT; ++op)
{
- EXPECT_EQ(b[op], i[op]) << op;
EXPECT_STREQ(bn[op], in[op]) << op;
+
+ switch (op)
+ {
+ case OP_EXTCODESIZE:
+ case OP_EXTCODECOPY:
+ case OP_EXTCODEHASH:
+ case OP_BALANCE:
+ case OP_CALL:
+ case OP_CALLCODE:
+ case OP_DELEGATECALL:
+ case OP_STATICCALL:
+ case OP_SLOAD:
+ continue;
+ default:
+ EXPECT_EQ(b[op], i[op]) << op;
+ break;
+ }
}
+
+ // EIP-2929 WARM_STORAGE_READ_COST
+ EXPECT_EQ(b[OP_EXTCODESIZE].gas_cost, 100);
+ EXPECT_EQ(b[OP_EXTCODECOPY].gas_cost, 100);
+ EXPECT_EQ(b[OP_EXTCODEHASH].gas_cost, 100);
+ EXPECT_EQ(b[OP_BALANCE].gas_cost, 100);
+ EXPECT_EQ(b[OP_CALL].gas_cost, 100);
+ EXPECT_EQ(b[OP_CALLCODE].gas_cost, 100);
+ EXPECT_EQ(b[OP_DELEGATECALL].gas_cost, 100);
+ EXPECT_EQ(b[OP_STATICCALL].gas_cost, 100);
+ EXPECT_EQ(b[OP_SLOAD].gas_cost, 100);
}