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

core: introduce overflow-safe num_words #2164

Merged
merged 2 commits into from
Jul 7, 2024
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
5 changes: 3 additions & 2 deletions silkworm/core/execution/call_tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <evmone/execution_state.hpp>
#include <evmone/instructions.hpp>

#include <silkworm/core/protocol/intrinsic_gas.hpp>
#include <silkworm/core/types/address.hpp>

using namespace evmone;
Expand Down Expand Up @@ -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<int64_t>(silkworm::num_words(new_size));
const auto current_words = static_cast<int64_t>(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;
Expand Down Expand Up @@ -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<int64_t>(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
}
Expand Down
7 changes: 4 additions & 3 deletions silkworm/core/execution/precompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <silkworm/core/crypto/rmd160.h>
#include <silkworm/core/crypto/secp256k1n.hpp>
#include <silkworm/core/crypto/sha256.h>
#include <silkworm/core/protocol/intrinsic_gas.hpp>
#include <silkworm/core/types/hash.hpp>

namespace silkworm::precompile {
Expand Down Expand Up @@ -78,7 +79,7 @@ std::optional<Bytes> 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<Bytes> sha256_run(ByteView input) noexcept {
Expand All @@ -88,7 +89,7 @@ std::optional<Bytes> 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<Bytes> rip160_run(ByteView input) noexcept {
Expand All @@ -99,7 +100,7 @@ std::optional<Bytes> 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<Bytes> id_run(ByteView input) noexcept {
Expand Down
5 changes: 2 additions & 3 deletions silkworm/core/protocol/intrinsic_gas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
21 changes: 15 additions & 6 deletions silkworm/core/protocol/intrinsic_gas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,20 @@

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

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;
// Words in EVM are 32-bytes long
inline constexpr uint64_t num_words(uint64_t num_bytes) noexcept {
return num_bytes / 32 + static_cast<uint64_t>(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
69 changes: 43 additions & 26 deletions silkworm/core/protocol/intrinsic_gas_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,48 @@

#include "param.hpp"

namespace silkworm::protocol {

TEST_CASE("EIP-2930 intrinsic gas") {
std::vector<AccessListEntry> 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<AccessListEntry> 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
Loading