From 4148555c26122c1349a4f2927c5218f006a47797 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Sun, 14 Feb 2021 16:16:09 +0100 Subject: [PATCH 1/6] Add Berlin instruction metrics with EIP-2929 costs Create new instruction metrics/costs table for Berlin EVM revision. Use warm access costs from EIP-2929 "Gas cost increases for state access opcodes": https://eips.ethereum.org/EIPS/eip-2929. --- lib/instructions/instruction_metrics.c | 267 ++++++++++++++++++++++++- test/unittests/instructions_test.cpp | 30 ++- 2 files changed, 294 insertions(+), 3 deletions(-) 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/instructions_test.cpp b/test/unittests/instructions_test.cpp index f60708dde..1ec7fbee3 100644 --- a/test/unittests/instructions_test.cpp +++ b/test/unittests/instructions_test.cpp @@ -296,7 +296,33 @@ 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; + EXPECT_STREQ(bn[op], in[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); } From 581158f464805b8070756196190a1702349ff928 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Mon, 15 Feb 2021 13:26:36 +0100 Subject: [PATCH 2/6] Add support for EIP-2929 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add methods access_account() and access_storage() to evmc_host_interface. These are to support EIP-2929 "Gas cost increases for state access opcodes" https://eips.ethereum.org/EIPS/eip-2929. This also defines EVMC_ACCESS_{WARM,COLD} constants for access status representation. Adjustments to Go/Java/Rust bindings are to be done in consecutive commits. Co-authored-by: Paweł Bylica --- CHANGELOG.md | 6 ++++ examples/example_host.cpp | 14 ++++++++ include/evmc/evmc.h | 52 ++++++++++++++++++++++++++++ include/evmc/evmc.hpp | 32 ++++++++++++++++- include/evmc/mocked_host.hpp | 17 +++++++++ test/unittests/cpp_test.cpp | 10 ++++++ test/unittests/instructions_test.cpp | 3 +- 7 files changed, 132 insertions(+), 2 deletions(-) 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/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..9f171ad69 100644 --- a/include/evmc/mocked_host.hpp +++ b/include/evmc/mocked_host.hpp @@ -312,5 +312,22 @@ class MockedHost : public Host { recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}}); } + + /// Access an account. + evmc_access_status access_account(const address& addr) noexcept override + { + (void)addr; + // TODO: Return COLD/WARM depending on recorded_account_accesses. + return 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 + { + (void)addr; + (void)key; + // TODO: Return COLD/WARM depending on accounts[].storage. + return EVMC_ACCESS_COLD; + } }; } // namespace evmc 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 1ec7fbee3..33f03f7bf 100644 --- a/test/unittests/instructions_test.cpp +++ b/test/unittests/instructions_test.cpp @@ -296,6 +296,8 @@ TEST(instructions, berlin_hard_fork) for (int op{OP_STOP}; op <= OP_SELFDESTRUCT; ++op) { + EXPECT_STREQ(bn[op], in[op]) << op; + switch (op) { case OP_EXTCODESIZE: @@ -310,7 +312,6 @@ TEST(instructions, berlin_hard_fork) continue; default: EXPECT_EQ(b[op], i[op]) << op; - EXPECT_STREQ(bn[op], in[op]) << op; break; } } From f8e4166c68c3e1a29054fb3f7e44d8b5bb65cbfb Mon Sep 17 00:00:00 2001 From: yperbasis Date: Wed, 24 Mar 2021 10:04:23 +0100 Subject: [PATCH 3/6] java: Add support for EIP-2929 Implement Java bindings for access_account() and access_storage() Host methods. --- bindings/java/c/host.c | 64 ++++++++++++++++++- .../src/main/java/org/ethereum/evmc/Host.java | 10 +++ .../java/org/ethereum/evmc/HostContext.java | 25 ++++++++ .../org/ethereum/evmc/TestHostContext.java | 10 +++ 4 files changed, 106 insertions(+), 3 deletions(-) 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]); From 0f24ed0c3663fca6f17e59afdd5af654f0a80526 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Mon, 29 Mar 2021 15:39:53 +0200 Subject: [PATCH 4/6] go: Add support for EIP-2929 Implement Go bindings for access_account() and access_storage() Host methods. --- bindings/go/evmc/host.c | 12 ++++++++++++ bindings/go/evmc/host.go | 21 +++++++++++++++++++++ bindings/go/evmc/host_test.go | 8 ++++++++ 3 files changed, 41 insertions(+) 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") From 17852c47a8253790f8713a4360e9fcea4791a99d Mon Sep 17 00:00:00 2001 From: yperbasis Date: Mon, 15 Feb 2021 13:07:14 +0100 Subject: [PATCH 5/6] rust: Add support for EIP-2929 Implement Rust bindings for access_account() and access_storage() Host methods. --- bindings/rust/evmc-vm/src/container.rs | 2 ++ bindings/rust/evmc-vm/src/lib.rs | 22 ++++++++++++++++++++++ bindings/rust/evmc-vm/src/types.rs | 15 +++++++++++++++ 3 files changed, 39 insertions(+) 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!( From 01a60b533b2baf4ba2ff2892cb708d30947f0e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Apr 2021 11:04:44 +0200 Subject: [PATCH 6/6] Implement EIP-2929 logic in MockedHost --- include/evmc/mocked_host.hpp | 37 +++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/include/evmc/mocked_host.hpp b/include/evmc/mocked_host.hpp index 9f171ad69..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 { @@ -313,21 +321,32 @@ class MockedHost : public Host recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}}); } - /// Access an account. + /// Record an account access. + /// @param addr The address of the accessed account. evmc_access_status access_account(const address& addr) noexcept override { - (void)addr; - // TODO: Return COLD/WARM depending on recorded_account_accesses. - return EVMC_ACCESS_COLD; + // 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 { - (void)addr; - (void)key; - // TODO: Return COLD/WARM depending on accounts[].storage. - return EVMC_ACCESS_COLD; + auto& value = accounts[addr].storage[key]; + const auto access_status = value.access_status; + value.access_status = EVMC_ACCESS_WARM; + return access_status; } }; } // namespace evmc