Skip to content

Commit 775bb3d

Browse files
authoredJul 7, 2024··
core: introduce overflow-safe num_words (#2164)
1 parent 9604539 commit 775bb3d

File tree

5 files changed

+67
-40
lines changed

5 files changed

+67
-40
lines changed
 

‎silkworm/core/execution/call_tracer.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <evmone/execution_state.hpp>
2323
#include <evmone/instructions.hpp>
2424

25+
#include <silkworm/core/protocol/intrinsic_gas.hpp>
2526
#include <silkworm/core/types/address.hpp>
2627

2728
using namespace evmone;
@@ -72,7 +73,7 @@ inline evmc_status_code check_requirements(const CostTable& cost_table, int64_t&
7273

7374
//! Adaptation of evmone::grow_memory: we need just to check gas requirements w/o growing memory
7475
inline int64_t check_memory_gas(int64_t gas_left, Memory& memory, uint64_t new_size) noexcept {
75-
const auto new_words = num_words(new_size);
76+
const auto new_words = static_cast<int64_t>(silkworm::num_words(new_size));
7677
const auto current_words = static_cast<int64_t>(memory.size() / word_size);
7778
const auto new_cost = 3 * new_words + new_words * new_words / 512;
7879
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
164165
return; // The execution has run of out-of-gas during contract deployment, do not trace anything
165166
}
166167
const auto init_code_word_cost = 6 * (Op == Opcode::OP_CREATE2) + 2 * (state.rev >= EVMC_SHANGHAI);
167-
const auto init_code_cost = num_words(init_code_size) * init_code_word_cost;
168+
const auto init_code_cost = static_cast<int64_t>(silkworm::num_words(init_code_size)) * init_code_word_cost;
168169
if (gas - init_code_cost < 0) {
169170
return; // The execution has run of out-of-gas during contract deployment, do not trace anything
170171
}

‎silkworm/core/execution/precompile.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <silkworm/core/crypto/rmd160.h>
4141
#include <silkworm/core/crypto/secp256k1n.hpp>
4242
#include <silkworm/core/crypto/sha256.h>
43+
#include <silkworm/core/protocol/intrinsic_gas.hpp>
4344
#include <silkworm/core/types/hash.hpp>
4445

4546
namespace silkworm::precompile {
@@ -78,7 +79,7 @@ std::optional<Bytes> ecrec_run(ByteView input) noexcept {
7879
}
7980

8081
uint64_t sha256_gas(ByteView input, evmc_revision) noexcept {
81-
return 60 + 12 * ((input.length() + 31) / 32);
82+
return 60 + 12 * num_words(input.length());
8283
}
8384

8485
std::optional<Bytes> sha256_run(ByteView input) noexcept {
@@ -88,7 +89,7 @@ std::optional<Bytes> sha256_run(ByteView input) noexcept {
8889
}
8990

9091
uint64_t rip160_gas(ByteView input, evmc_revision) noexcept {
91-
return 600 + 120 * ((input.length() + 31) / 32);
92+
return 600 + 120 * num_words(input.length());
9293
}
9394

9495
std::optional<Bytes> rip160_run(ByteView input) noexcept {
@@ -99,7 +100,7 @@ std::optional<Bytes> rip160_run(ByteView input) noexcept {
99100
}
100101

101102
uint64_t id_gas(ByteView input, evmc_revision) noexcept {
102-
return 15 + 3 * ((input.length() + 31) / 32);
103+
return 15 + 3 * num_words(input.length());
103104
}
104105

105106
std::optional<Bytes> id_run(ByteView input) noexcept {

‎silkworm/core/protocol/intrinsic_gas.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, const evmc_revision
3838
}
3939
gas += total_num_of_storage_keys * fee::kAccessListStorageKeyCost;
4040

41-
const intx::uint128 data_len{txn.data.length()};
41+
const uint64_t data_len{txn.data.length()};
4242
if (data_len == 0) {
4343
return gas;
4444
}
@@ -51,8 +51,7 @@ intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, const evmc_revision
5151

5252
// EIP-3860: Limit and meter initcode
5353
if (contract_creation && rev >= EVMC_SHANGHAI) {
54-
const intx::uint128 num_words{(data_len + 31) / 32};
55-
gas += num_words * fee::kInitCodeWordCost;
54+
gas += num_words(data_len) * fee::kInitCodeWordCost;
5655
}
5756

5857
return gas;

‎silkworm/core/protocol/intrinsic_gas.hpp

+15-6
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,20 @@
2020

2121
#include <silkworm/core/types/transaction.hpp>
2222

23-
namespace silkworm::protocol {
23+
namespace silkworm {
2424

25-
// Returns the intrinsic gas of a transaction.
26-
// Refer to g0 in Section 6.2 "Execution" of the Yellow Paper
27-
// and EIP-3860 "Limit and meter initcode".
28-
intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, evmc_revision rev) noexcept;
25+
// Words in EVM are 32-bytes long
26+
inline constexpr uint64_t num_words(uint64_t num_bytes) noexcept {
27+
return num_bytes / 32 + static_cast<uint64_t>(num_bytes % 32 != 0);
28+
}
2929

30-
} // namespace silkworm::protocol
30+
namespace protocol {
31+
32+
// Returns the intrinsic gas of a transaction.
33+
// Refer to g0 in Section 6.2 "Execution" of the Yellow Paper
34+
// and EIP-3860 "Limit and meter initcode".
35+
intx::uint128 intrinsic_gas(const UnsignedTransaction& txn, evmc_revision rev) noexcept;
36+
37+
} // namespace protocol
38+
39+
} // namespace silkworm

‎silkworm/core/protocol/intrinsic_gas_test.cpp

+43-26
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,48 @@
2222

2323
#include "param.hpp"
2424

25-
namespace silkworm::protocol {
26-
27-
TEST_CASE("EIP-2930 intrinsic gas") {
28-
std::vector<AccessListEntry> access_list{
29-
{0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae_address,
30-
{
31-
0x0000000000000000000000000000000000000000000000000000000000000003_bytes32,
32-
0x0000000000000000000000000000000000000000000000000000000000000007_bytes32,
33-
}},
34-
{0xbb9bc244d798123fde783fcc1c72d3bb8c189413_address, {}},
35-
};
36-
37-
UnsignedTransaction txn{
38-
.type = TransactionType::kAccessList,
39-
.chain_id = kSepoliaConfig.chain_id,
40-
.nonce = 7,
41-
.max_priority_fee_per_gas = 30000000000,
42-
.max_fee_per_gas = 30000000000,
43-
.gas_limit = 5748100,
44-
.to = 0x811a752c8cd697e3cb27279c330ed1ada745a8d7_address,
45-
.value = 2 * kEther,
46-
.access_list = access_list};
47-
48-
intx::uint128 g0{intrinsic_gas(txn, EVMC_ISTANBUL)};
49-
CHECK(g0 == fee::kGTransaction + 2 * fee::kAccessListAddressCost + 2 * fee::kAccessListStorageKeyCost);
25+
namespace silkworm {
26+
27+
TEST_CASE("num_words") {
28+
CHECK(num_words(0) == 0);
29+
CHECK(num_words(1) == 1);
30+
CHECK(num_words(31) == 1);
31+
CHECK(num_words(32) == 1);
32+
CHECK(num_words(33) == 2);
33+
CHECK(num_words(0xFFFFFFFFFFFFFFDF) == 0x7FFFFFFFFFFFFFF);
34+
CHECK(num_words(0xFFFFFFFFFFFFFFE0) == 0x7FFFFFFFFFFFFFF);
35+
CHECK(num_words(0xFFFFFFFFFFFFFFE1) == 0x800000000000000);
36+
CHECK(num_words(0xFFFFFFFFFFFFFFFE) == 0x800000000000000);
37+
CHECK(num_words(0xFFFFFFFFFFFFFFFF) == 0x800000000000000);
5038
}
5139

52-
} // namespace silkworm::protocol
40+
namespace protocol {
41+
42+
TEST_CASE("EIP-2930 intrinsic gas") {
43+
std::vector<AccessListEntry> access_list{
44+
{0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae_address,
45+
{
46+
0x0000000000000000000000000000000000000000000000000000000000000003_bytes32,
47+
0x0000000000000000000000000000000000000000000000000000000000000007_bytes32,
48+
}},
49+
{0xbb9bc244d798123fde783fcc1c72d3bb8c189413_address, {}},
50+
};
51+
52+
UnsignedTransaction txn{
53+
.type = TransactionType::kAccessList,
54+
.chain_id = kSepoliaConfig.chain_id,
55+
.nonce = 7,
56+
.max_priority_fee_per_gas = 30000000000,
57+
.max_fee_per_gas = 30000000000,
58+
.gas_limit = 5748100,
59+
.to = 0x811a752c8cd697e3cb27279c330ed1ada745a8d7_address,
60+
.value = 2 * kEther,
61+
.access_list = access_list};
62+
63+
intx::uint128 g0{intrinsic_gas(txn, EVMC_ISTANBUL)};
64+
CHECK(g0 == fee::kGTransaction + 2 * fee::kAccessListAddressCost + 2 * fee::kAccessListStorageKeyCost);
65+
}
66+
67+
} // namespace protocol
68+
69+
} // namespace silkworm

0 commit comments

Comments
 (0)
Please sign in to comment.