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

EIP-3860: Limit and meter initcode #808

Merged
merged 17 commits into from
Dec 15, 2022
Merged
3 changes: 2 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[submodule "evmone"]
path = third_party/evmone
url = https://github.com/ethereum/evmone.git
url = https://github.com/torquem-ch/evmone.git
branch = eip-3860
[submodule "tests"]
path = third_party/tests
url = https://github.com/ethereum/tests.git
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ add_library(evmone third_party/evmone/lib/evmone/advanced_analysis.cpp
third_party/evmone/lib/evmone/eof.hpp
third_party/evmone/lib/evmone/execution_state.hpp
third_party/evmone/lib/evmone/instructions_calls.cpp
third_party/evmone/lib/evmone/instructions_opcodes.hpp
third_party/evmone/lib/evmone/instructions_storage.cpp
third_party/evmone/lib/evmone/instructions_traits.hpp
third_party/evmone/lib/evmone/instructions_xmacro.hpp
Expand All @@ -134,7 +135,7 @@ add_library(evmone third_party/evmone/lib/evmone/advanced_analysis.cpp
third_party/evmone/lib/evmone/tracing.hpp
third_party/evmone/lib/evmone/vm.cpp
third_party/evmone/lib/evmone/vm.hpp)
set_source_files_properties(third_party/evmone/lib/evmone/vm.cpp PROPERTIES COMPILE_DEFINITIONS PROJECT_VERSION="0.9.0")
set_source_files_properties(third_party/evmone/lib/evmone/vm.cpp PROPERTIES COMPILE_DEFINITIONS PROJECT_VERSION="0.10.0-dev")
target_include_directories(evmone PUBLIC third_party/evmone/include third_party/evmone/lib)
target_link_libraries(evmone PUBLIC evmc intx::intx PRIVATE evmc::instructions)

Expand Down
9 changes: 7 additions & 2 deletions cmd/test/consensus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static const std::vector<fs::path> kFailingTests{
// Geth excludes this test as well:
// https://github.com/ethereum/go-ethereum/blob/v1.10.18/tests/transaction_test.go#L31
kTransactionDir / "ttGasLimit" / "TransactionWithGasLimitxPriceOverflow.json",

// EOF is not implemented yet
kBlockchainDir / "GeneralStateTests" / "stEIP3540",
};

static constexpr size_t kColumnWidth{80};
Expand Down Expand Up @@ -187,13 +190,15 @@ static const std::map<std::string, ChainConfig> kNetworkConfig{
0, // Istanbul
0, // Berlin
0, // London
0, // FORK_NEXT_VALUE (EIP-3675)
0, // Merge Netsplit (FORK_NEXT_VALUE)
},
.muir_glacier_block = 0,
.arrow_glacier_block = 0,
.gray_glacier_block = 0,
.terminal_total_difficulty = 0,
}},
{"Merge+3855", test::kShanghaiConfig},
{"Merge+3860", test::kShanghaiConfig},
{"ArrowGlacierToMergeAtDiffC0000",
{
.chain_id = 1,
Expand Down Expand Up @@ -699,7 +704,7 @@ RunResults transaction_test(const nlohmann::json& j) {

const auto expected_intrinsic_gas{intx::from_string<intx::uint256>(test["intrinsicGas"].get<std::string>())};
const evmc_revision rev{config.revision(/*block_number=*/0)};
const auto calculated_intrinsic_gas{intrinsic_gas(txn, rev >= EVMC_HOMESTEAD, rev >= EVMC_ISTANBUL)};
const auto calculated_intrinsic_gas{intrinsic_gas(txn, rev)};
if (calculated_intrinsic_gas != expected_intrinsic_gas) {
std::cout << "Intrinsic gas mismatch for " << entry.key() << ":\n"
<< intx::to_string(calculated_intrinsic_gas, /*base=*/16)
Expand Down
28 changes: 18 additions & 10 deletions core/silkworm/chain/intrinsic_gas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,39 @@

namespace silkworm {

intx::uint128 intrinsic_gas(const Transaction& txn, bool homestead, bool istanbul) noexcept {
intx::uint128 intrinsic_gas(const Transaction& txn, const evmc_revision rev) noexcept {
intx::uint128 gas{fee::kGTransaction};

if (!txn.to && homestead) {
const bool contract_creation{!txn.to};
if (contract_creation && rev >= EVMC_HOMESTEAD) {
gas += fee::kGTxCreate;
}

// https://eips.ethereum.org/EIPS/eip-2930
// EIP-2930: Optional access lists
gas += intx::uint128{txn.access_list.size()} * fee::kAccessListAddressCost;
intx::uint128 total_num_of_storage_keys{0};
for (const AccessListEntry& e : txn.access_list) {
gas += intx::uint128{e.storage_keys.size()} * fee::kAccessListStorageKeyCost;
total_num_of_storage_keys += e.storage_keys.size();
}
gas += total_num_of_storage_keys * fee::kAccessListStorageKeyCost;

if (txn.data.empty()) {
const intx::uint128 data_len{txn.data.length()};
if (data_len == 0) {
return gas;
}

intx::uint128 non_zero_bytes{as_range::count_if(txn.data, [](char c) { return c != 0; })};

uint64_t nonZeroGas{istanbul ? fee::kGTxDataNonZeroIstanbul : fee::kGTxDataNonZeroFrontier};
const intx::uint128 non_zero_bytes{as_range::count_if(txn.data, [](uint8_t c) { return c != 0; })};
const intx::uint128 nonZeroGas{rev >= EVMC_ISTANBUL ? fee::kGTxDataNonZeroIstanbul : fee::kGTxDataNonZeroFrontier};
gas += non_zero_bytes * nonZeroGas;

intx::uint128 zero_bytes{txn.data.length() - non_zero_bytes};
const intx::uint128 zero_bytes{data_len - non_zero_bytes};
gas += zero_bytes * fee::kGTxDataZero;

// 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;
}

return gas;
}

Expand Down
4 changes: 2 additions & 2 deletions core/silkworm/chain/intrinsic_gas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace silkworm {

// Returns the intrinsic gas of a transaction.
// Refer to g0 in Section 6.2 "Execution" of the Yellow Paper
// and EIP-2930 "Optional access lists"
intx::uint128 intrinsic_gas(const Transaction& txn, bool homestead, bool istanbul) noexcept;
// and EIP-3860 "Limit and meter initcode".
intx::uint128 intrinsic_gas(const Transaction& txn, evmc_revision rev) noexcept;

} // namespace silkworm
2 changes: 1 addition & 1 deletion core/silkworm/chain/intrinsic_gas_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ TEST_CASE("EIP-2930 intrinsic gas") {
access_list,
};

intx::uint128 g0{intrinsic_gas(txn, /*homestead=*/true, /*istanbul=*/true)};
intx::uint128 g0{intrinsic_gas(txn, EVMC_ISTANBUL)};
CHECK(g0 == fee::kGTransaction + 2 * fee::kAccessListAddressCost + 2 * fee::kAccessListStorageKeyCost);
}

Expand Down
24 changes: 13 additions & 11 deletions core/silkworm/chain/protocol_param.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,36 @@ namespace fee {

inline constexpr uint64_t kGCodeDeposit{200};

inline constexpr uint64_t kGTransaction{21'000};
inline constexpr uint64_t kGTxCreate{32'000};
inline constexpr uint64_t kGTxDataZero{4};
inline constexpr uint64_t kGTxDataNonZeroFrontier{68};
inline constexpr uint64_t kGTxDataNonZeroIstanbul{16};
inline constexpr uint64_t kGTransaction{21'000};
inline constexpr uint64_t kGTxDataNonZeroIstanbul{16}; // EIP-2028

inline constexpr uint64_t kInitCodeWordCost{2}; // EIP-3860

} // namespace fee

namespace param {

// https://eips.ethereum.org/EIPS/eip-170
inline constexpr size_t kMaxCodeSize{0x6000};
inline constexpr size_t kMaxCodeSize{0x6000}; // EIP-170
inline constexpr size_t kMaxInitCodeSize{2 * kMaxCodeSize}; // EIP-3860

inline constexpr uint64_t kMaxExtraDataBytes{32};

inline constexpr uint64_t kBlockRewardFrontier{5 * kEther};
inline constexpr uint64_t kBlockRewardByzantium{3 * kEther};
inline constexpr uint64_t kBlockRewardConstantinople{2 * kEther};
inline constexpr uint64_t kBlockRewardByzantium{3 * kEther}; // EIP-649
inline constexpr uint64_t kBlockRewardConstantinople{2 * kEther}; // EIP-1234

// https://eips.ethereum.org/EIPS/eip-3529
// EIP-3529: Reduction in refunds
inline constexpr uint64_t kMaxRefundQuotientFrontier{2};
inline constexpr uint64_t kMaxRefundQuotientLondon{5};

// https://eips.ethereum.org/EIPS/eip-1559
inline constexpr uint64_t kInitialBaseFee{1'000'000'000};
// EIP-1559: Fee market change for ETH 1.0 chain
inline constexpr uint64_t kInitialBaseFee{kGiga};
inline constexpr uint64_t kBaseFeeMaxChangeDenominator{8};
inline constexpr uint64_t kElasticityMultiplier{2};

inline constexpr uint64_t kMaxExtraDataBytes{32};

} // namespace param

} // namespace silkworm
9 changes: 8 additions & 1 deletion core/silkworm/consensus/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <silkpre/secp256k1n.hpp>

#include <silkworm/chain/intrinsic_gas.hpp>
#include <silkworm/chain/protocol_param.hpp>
#include <silkworm/consensus/ethash/engine.hpp>
#include <silkworm/consensus/merge/engine.hpp>
#include <silkworm/consensus/noproof/engine.hpp>
Expand Down Expand Up @@ -67,7 +68,7 @@ ValidationResult pre_validate_transaction(const Transaction& txn, uint64_t block
}
}

const intx::uint128 g0{intrinsic_gas(txn, rev >= EVMC_HOMESTEAD, rev >= EVMC_ISTANBUL)};
const intx::uint128 g0{intrinsic_gas(txn, rev)};
if (txn.gas_limit < g0) {
return ValidationResult::kIntrinsicGas;
}
Expand All @@ -77,6 +78,12 @@ ValidationResult pre_validate_transaction(const Transaction& txn, uint64_t block
return ValidationResult::kNonceTooHigh;
}

// EIP-3860: Limit and meter initcode
const bool contract_creation{!txn.to};
if (rev >= EVMC_SHANGHAI && contract_creation && txn.data.size() > param::kMaxInitCodeSize) {
return ValidationResult::kMaxInitCodeSizeExceeded;
}

return ValidationResult::kOk;
}

Expand Down
4 changes: 2 additions & 2 deletions core/silkworm/consensus/ethash/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ void EthashEngine::finalize(IntraBlockState& state, const Block& block, const ev
const uint64_t block_number{block.header.number};
intx::uint256 miner_reward{block_reward};
for (const BlockHeader& ommer : block.ommers) {
intx::uint256 ommer_reward{((8 + ommer.number - block_number) * block_reward) >> 3};
const intx::uint256 ommer_reward{((8 + ommer.number - block_number) * block_reward) >> 3};
state.add_to_balance(ommer.beneficiary, ommer_reward);
miner_reward += block_reward / 32;
miner_reward += block_reward >> 5; // div 32
}

state.add_to_balance(block.header.beneficiary, miner_reward);
Expand Down
3 changes: 3 additions & 0 deletions core/silkworm/consensus/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ enum class [[nodiscard]] ValidationResult{
// See EIP-3675: Upgrade consensus to Proof-of-Stake
kPoSBlockBeforeMerge,
kPoWBlockAfterMerge,

// See EIP-3860: Limit and meter initcode
kMaxInitCodeSizeExceeded,
};

} // namespace silkworm
36 changes: 15 additions & 21 deletions core/silkworm/execution/evm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,15 @@ CallResult EVM::execute(const Transaction& txn, uint64_t gas) noexcept {
const bool contract_creation{!txn.to.has_value()};
const evmc::address destination{contract_creation ? evmc::address{} : *txn.to};

evmc_message message{
contract_creation ? EVMC_CREATE : EVMC_CALL, // kind
0, // flags
0, // depth
static_cast<int64_t>(gas), // gas
destination, // recipient
*txn.from, // sender
&txn.data[0], // input_data
txn.data.size(), // input_size
intx::be::store<evmc::uint256be>(txn.value), // value
{}, // create2_salt
destination, // code_address
const evmc_message message{
.kind = contract_creation ? EVMC_CREATE : EVMC_CALL,
.gas = static_cast<int64_t>(gas),
.recipient = destination,
.sender = *txn.from,
.input_data = txn.data.data(),
.input_size = txn.data.size(),
.value = intx::be::store<evmc::uint256be>(txn.value),
.code_address = destination,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

};

evmc::Result res{contract_creation ? create(message) : call(message)};
Expand Down Expand Up @@ -146,15 +143,12 @@ evmc::Result EVM::create(const evmc_message& message) noexcept {
state_.add_to_balance(contract_addr, value);

const evmc_message deploy_message{
EVMC_CALL, // kind
0, // flags
message.depth, // depth
message.gas, // gas
contract_addr, // recipient
message.sender, // sender
nullptr, // input_data
0, // input_size
message.value, // value
.kind = EVMC_CALL,
.depth = message.depth,
.gas = message.gas,
.recipient = contract_addr,
.sender = message.sender,
.value = message.value,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

};

auto evm_res{execute(deploy_message, ByteView{message.input_data, message.input_size}, /*code_hash=*/nullptr)};
Expand Down
2 changes: 1 addition & 1 deletion core/silkworm/execution/processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void ExecutionProcessor::execute_transaction(const Transaction& txn, Receipt& re
const intx::uint256 effective_gas_price{txn.effective_gas_price(base_fee_per_gas)};
state_.subtract_from_balance(*txn.from, txn.gas_limit * effective_gas_price);

const intx::uint128 g0{intrinsic_gas(txn, rev >= EVMC_HOMESTEAD, rev >= EVMC_ISTANBUL)};
const intx::uint128 g0{intrinsic_gas(txn, rev)};
assert(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid)

const CallResult vm_res{evm_.execute(txn, txn.gas_limit - static_cast<uint64_t>(g0))};
Expand Down
2 changes: 1 addition & 1 deletion third_party/evmone
2 changes: 1 addition & 1 deletion third_party/tests
Submodule tests updated 508 files
4 changes: 2 additions & 2 deletions wasm/silkworm_wasm_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ Transaction* new_transaction(const Bytes* rlp) {

void delete_transaction(Transaction* x) { delete x; }

bool check_intrinsic_gas(const Transaction* txn, bool homestead, bool istanbul) {
intx::uint128 g0{intrinsic_gas(*txn, homestead, istanbul)};
bool check_intrinsic_gas(const Transaction* txn, evmc_revision rev) {
intx::uint128 g0{intrinsic_gas(*txn, rev)};
return txn->gas_limit >= g0;
}

Expand Down
2 changes: 1 addition & 1 deletion wasm/silkworm_wasm_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ SILKWORM_EXPORT void difficulty(intx::uint256* in_out, uint64_t block_number, ui
SILKWORM_EXPORT silkworm::Transaction* new_transaction(const silkworm::Bytes* rlp);
SILKWORM_EXPORT void delete_transaction(silkworm::Transaction* x);

SILKWORM_EXPORT bool check_intrinsic_gas(const silkworm::Transaction* txn, bool homestead, bool istanbul);
SILKWORM_EXPORT bool check_intrinsic_gas(const silkworm::Transaction* txn, evmc_revision rev);

SILKWORM_EXPORT const uint8_t* recover_sender(silkworm::Transaction* txn);

Expand Down