From d001d608af9ad2124965cc7afb31e53dc07ba213 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Fri, 5 Jul 2024 10:24:44 +0200 Subject: [PATCH 1/2] core: introduce overflow-safe num_words --- silkworm/core/execution/call_tracer.cpp | 5 +- silkworm/core/execution/precompile.cpp | 7 +- silkworm/core/protocol/intrinsic_gas.cpp | 5 +- silkworm/core/protocol/intrinsic_gas.hpp | 20 ++++-- silkworm/core/protocol/intrinsic_gas_test.cpp | 69 ++++++++++++------- 5 files changed, 66 insertions(+), 40 deletions(-) diff --git a/silkworm/core/execution/call_tracer.cpp b/silkworm/core/execution/call_tracer.cpp index 254af5e1ac..b7c7a5cf25 100644 --- a/silkworm/core/execution/call_tracer.cpp +++ b/silkworm/core/execution/call_tracer.cpp @@ -22,6 +22,7 @@ #include #include +#include #include using namespace evmone; @@ -72,7 +73,7 @@ inline evmc_status_code check_requirements(const CostTable& cost_table, int64_t& //! Adaptation of evmone::grow_memory: we need just to check gas requirements w/o growing memory inline int64_t check_memory_gas(int64_t gas_left, Memory& memory, uint64_t new_size) noexcept { - const auto new_words = num_words(new_size); + const auto new_words = static_cast(silkworm::num_words(new_size)); const auto current_words = static_cast(memory.size() / word_size); const auto new_cost = 3 * new_words + new_words * new_words / 512; const auto current_cost = 3 * current_words + current_words * current_words / 512; @@ -164,7 +165,7 @@ void on_create_start(const intx::uint256* stack_top, int stack_height, int64_t g return; // The execution has run of out-of-gas during contract deployment, do not trace anything } const auto init_code_word_cost = 6 * (Op == Opcode::OP_CREATE2) + 2 * (state.rev >= EVMC_SHANGHAI); - const auto init_code_cost = num_words(init_code_size) * init_code_word_cost; + const auto init_code_cost = static_cast(silkworm::num_words(init_code_size)) * init_code_word_cost; if (gas - init_code_cost < 0) { return; // The execution has run of out-of-gas during contract deployment, do not trace anything } diff --git a/silkworm/core/execution/precompile.cpp b/silkworm/core/execution/precompile.cpp index 4553cc189c..a67d5aa38c 100644 --- a/silkworm/core/execution/precompile.cpp +++ b/silkworm/core/execution/precompile.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include namespace silkworm::precompile { @@ -78,7 +79,7 @@ std::optional ecrec_run(ByteView input) noexcept { } uint64_t sha256_gas(ByteView input, evmc_revision) noexcept { - return 60 + 12 * ((input.length() + 31) / 32); + return 60 + 12 * num_words(input.length()); } std::optional sha256_run(ByteView input) noexcept { @@ -88,7 +89,7 @@ std::optional sha256_run(ByteView input) noexcept { } uint64_t rip160_gas(ByteView input, evmc_revision) noexcept { - return 600 + 120 * ((input.length() + 31) / 32); + return 600 + 120 * num_words(input.length()); } std::optional rip160_run(ByteView input) noexcept { @@ -99,7 +100,7 @@ std::optional rip160_run(ByteView input) noexcept { } uint64_t id_gas(ByteView input, evmc_revision) noexcept { - return 15 + 3 * ((input.length() + 31) / 32); + return 15 + 3 * num_words(input.length()); } std::optional id_run(ByteView input) noexcept { diff --git a/silkworm/core/protocol/intrinsic_gas.cpp b/silkworm/core/protocol/intrinsic_gas.cpp index f16a20a0bd..e3fa49317c 100644 --- a/silkworm/core/protocol/intrinsic_gas.cpp +++ b/silkworm/core/protocol/intrinsic_gas.cpp @@ -38,7 +38,7 @@ intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, const evmc_revision } gas += total_num_of_storage_keys * fee::kAccessListStorageKeyCost; - const intx::uint128 data_len{txn.data.length()}; + const uint64_t data_len{txn.data.length()}; if (data_len == 0) { return gas; } @@ -51,8 +51,7 @@ intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, const evmc_revision // EIP-3860: Limit and meter initcode if (contract_creation && rev >= EVMC_SHANGHAI) { - const intx::uint128 num_words{(data_len + 31) / 32}; - gas += num_words * fee::kInitCodeWordCost; + gas += num_words(data_len) * fee::kInitCodeWordCost; } return gas; diff --git a/silkworm/core/protocol/intrinsic_gas.hpp b/silkworm/core/protocol/intrinsic_gas.hpp index d20a8deaf8..3adbdaa173 100644 --- a/silkworm/core/protocol/intrinsic_gas.hpp +++ b/silkworm/core/protocol/intrinsic_gas.hpp @@ -20,11 +20,19 @@ #include -namespace silkworm::protocol { +namespace silkworm { -// Returns the intrinsic gas of a transaction. -// Refer to g0 in Section 6.2 "Execution" of the Yellow Paper -// and EIP-3860 "Limit and meter initcode". -intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, evmc_revision rev) noexcept; +inline constexpr uint64_t num_words(uint64_t num_bytes) noexcept { + return num_bytes / 32 + static_cast(num_bytes % 32 != 0); +} -} // namespace silkworm::protocol +namespace protocol { + + // Returns the intrinsic gas of a transaction. + // Refer to g0 in Section 6.2 "Execution" of the Yellow Paper + // and EIP-3860 "Limit and meter initcode". + intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, evmc_revision rev) noexcept; + +} // namespace protocol + +} // namespace silkworm diff --git a/silkworm/core/protocol/intrinsic_gas_test.cpp b/silkworm/core/protocol/intrinsic_gas_test.cpp index 26a8f11a85..4e5613d9f5 100644 --- a/silkworm/core/protocol/intrinsic_gas_test.cpp +++ b/silkworm/core/protocol/intrinsic_gas_test.cpp @@ -22,31 +22,48 @@ #include "param.hpp" -namespace silkworm::protocol { - -TEST_CASE("EIP-2930 intrinsic gas") { - std::vector access_list{ - {0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae_address, - { - 0x0000000000000000000000000000000000000000000000000000000000000003_bytes32, - 0x0000000000000000000000000000000000000000000000000000000000000007_bytes32, - }}, - {0xbb9bc244d798123fde783fcc1c72d3bb8c189413_address, {}}, - }; - - UnsignedTransaction txn{ - .type = TransactionType::kAccessList, - .chain_id = kSepoliaConfig.chain_id, - .nonce = 7, - .max_priority_fee_per_gas = 30000000000, - .max_fee_per_gas = 30000000000, - .gas_limit = 5748100, - .to = 0x811a752c8cd697e3cb27279c330ed1ada745a8d7_address, - .value = 2 * kEther, - .access_list = access_list}; - - intx::uint128 g0{intrinsic_gas(txn, EVMC_ISTANBUL)}; - CHECK(g0 == fee::kGTransaction + 2 * fee::kAccessListAddressCost + 2 * fee::kAccessListStorageKeyCost); +namespace silkworm { + +TEST_CASE("num_words") { + CHECK(num_words(0) == 0); + CHECK(num_words(1) == 1); + CHECK(num_words(31) == 1); + CHECK(num_words(32) == 1); + CHECK(num_words(33) == 2); + CHECK(num_words(0xFFFFFFFFFFFFFFDF) == 0x7FFFFFFFFFFFFFF); + CHECK(num_words(0xFFFFFFFFFFFFFFE0) == 0x7FFFFFFFFFFFFFF); + CHECK(num_words(0xFFFFFFFFFFFFFFE1) == 0x800000000000000); + CHECK(num_words(0xFFFFFFFFFFFFFFFE) == 0x800000000000000); + CHECK(num_words(0xFFFFFFFFFFFFFFFF) == 0x800000000000000); } -} // namespace silkworm::protocol +namespace protocol { + + TEST_CASE("EIP-2930 intrinsic gas") { + std::vector access_list{ + {0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae_address, + { + 0x0000000000000000000000000000000000000000000000000000000000000003_bytes32, + 0x0000000000000000000000000000000000000000000000000000000000000007_bytes32, + }}, + {0xbb9bc244d798123fde783fcc1c72d3bb8c189413_address, {}}, + }; + + UnsignedTransaction txn{ + .type = TransactionType::kAccessList, + .chain_id = kSepoliaConfig.chain_id, + .nonce = 7, + .max_priority_fee_per_gas = 30000000000, + .max_fee_per_gas = 30000000000, + .gas_limit = 5748100, + .to = 0x811a752c8cd697e3cb27279c330ed1ada745a8d7_address, + .value = 2 * kEther, + .access_list = access_list}; + + intx::uint128 g0{intrinsic_gas(txn, EVMC_ISTANBUL)}; + CHECK(g0 == fee::kGTransaction + 2 * fee::kAccessListAddressCost + 2 * fee::kAccessListStorageKeyCost); + } + +} // namespace protocol + +} // namespace silkworm From 8192f453f0ad80a0f29e09c53c3fc24564d0f6a2 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Fri, 5 Jul 2024 10:35:42 +0200 Subject: [PATCH 2/2] add comment --- silkworm/core/protocol/intrinsic_gas.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/silkworm/core/protocol/intrinsic_gas.hpp b/silkworm/core/protocol/intrinsic_gas.hpp index 3adbdaa173..2a7e206a5e 100644 --- a/silkworm/core/protocol/intrinsic_gas.hpp +++ b/silkworm/core/protocol/intrinsic_gas.hpp @@ -22,6 +22,7 @@ namespace silkworm { +// Words in EVM are 32-bytes long inline constexpr uint64_t num_words(uint64_t num_bytes) noexcept { return num_bytes / 32 + static_cast(num_bytes % 32 != 0); }