diff --git a/.gitmodules b/.gitmodules index 257c484223..babd7332c9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 52e4f71d8c..cd32b97bce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -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) diff --git a/cmd/test/consensus.cpp b/cmd/test/consensus.cpp index 8b217fe608..dac07af588 100644 --- a/cmd/test/consensus.cpp +++ b/cmd/test/consensus.cpp @@ -64,6 +64,9 @@ static const std::vector 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}; @@ -187,13 +190,15 @@ static const std::map 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, @@ -699,7 +704,7 @@ RunResults transaction_test(const nlohmann::json& j) { const auto expected_intrinsic_gas{intx::from_string(test["intrinsicGas"].get())}; 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) diff --git a/core/silkworm/chain/intrinsic_gas.cpp b/core/silkworm/chain/intrinsic_gas.cpp index 8588d69619..b95a73437f 100644 --- a/core/silkworm/chain/intrinsic_gas.cpp +++ b/core/silkworm/chain/intrinsic_gas.cpp @@ -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; } diff --git a/core/silkworm/chain/intrinsic_gas.hpp b/core/silkworm/chain/intrinsic_gas.hpp index c620950673..4515381bc5 100644 --- a/core/silkworm/chain/intrinsic_gas.hpp +++ b/core/silkworm/chain/intrinsic_gas.hpp @@ -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 diff --git a/core/silkworm/chain/intrinsic_gas_test.cpp b/core/silkworm/chain/intrinsic_gas_test.cpp index 7d3861309b..a44ca943db 100644 --- a/core/silkworm/chain/intrinsic_gas_test.cpp +++ b/core/silkworm/chain/intrinsic_gas_test.cpp @@ -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); } diff --git a/core/silkworm/chain/protocol_param.hpp b/core/silkworm/chain/protocol_param.hpp index 06ce5397ad..b924115825 100644 --- a/core/silkworm/chain/protocol_param.hpp +++ b/core/silkworm/chain/protocol_param.hpp @@ -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 diff --git a/core/silkworm/consensus/engine.cpp b/core/silkworm/consensus/engine.cpp index a758f03819..e3cbd48818 100644 --- a/core/silkworm/consensus/engine.cpp +++ b/core/silkworm/consensus/engine.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -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; } @@ -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; } diff --git a/core/silkworm/consensus/ethash/engine.cpp b/core/silkworm/consensus/ethash/engine.cpp index 58afee7d4a..73bc556c14 100644 --- a/core/silkworm/consensus/ethash/engine.cpp +++ b/core/silkworm/consensus/ethash/engine.cpp @@ -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); diff --git a/core/silkworm/consensus/validation.hpp b/core/silkworm/consensus/validation.hpp index dda89830cd..c1692f4321 100644 --- a/core/silkworm/consensus/validation.hpp +++ b/core/silkworm/consensus/validation.hpp @@ -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 diff --git a/core/silkworm/execution/evm.cpp b/core/silkworm/execution/evm.cpp index c0583ba30f..3c4ce252fa 100644 --- a/core/silkworm/execution/evm.cpp +++ b/core/silkworm/execution/evm.cpp @@ -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(gas), // gas - destination, // recipient - *txn.from, // sender - &txn.data[0], // input_data - txn.data.size(), // input_size - intx::be::store(txn.value), // value - {}, // create2_salt - destination, // code_address + const evmc_message message{ + .kind = contract_creation ? EVMC_CREATE : EVMC_CALL, + .gas = static_cast(gas), + .recipient = destination, + .sender = *txn.from, + .input_data = txn.data.data(), + .input_size = txn.data.size(), + .value = intx::be::store(txn.value), + .code_address = destination, }; evmc::Result res{contract_creation ? create(message) : call(message)}; @@ -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, }; auto evm_res{execute(deploy_message, ByteView{message.input_data, message.input_size}, /*code_hash=*/nullptr)}; diff --git a/core/silkworm/execution/processor.cpp b/core/silkworm/execution/processor.cpp index 60e06c0ea9..7e7cdf0642 100644 --- a/core/silkworm/execution/processor.cpp +++ b/core/silkworm/execution/processor.cpp @@ -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(g0))}; diff --git a/third_party/evmone b/third_party/evmone index aa921c1c56..ed8a85d980 160000 --- a/third_party/evmone +++ b/third_party/evmone @@ -1 +1 @@ -Subproject commit aa921c1c56a5cde7ed59f6c7c7a61f6a86a11398 +Subproject commit ed8a85d980b581c32e1cba4902b513d44abcefd0 diff --git a/third_party/tests b/third_party/tests index c76c5d274e..9e058e565e 160000 --- a/third_party/tests +++ b/third_party/tests @@ -1 +1 @@ -Subproject commit c76c5d274e271e039b0b11e9372f0c1143dd6c30 +Subproject commit 9e058e565e664ae2ba93f9a25c4beb105df07480 diff --git a/wasm/silkworm_wasm_api.cpp b/wasm/silkworm_wasm_api.cpp index 8a7afcd887..6d7d38955a 100644 --- a/wasm/silkworm_wasm_api.cpp +++ b/wasm/silkworm_wasm_api.cpp @@ -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; } diff --git a/wasm/silkworm_wasm_api.hpp b/wasm/silkworm_wasm_api.hpp index a551a30504..e54c2dde4a 100644 --- a/wasm/silkworm_wasm_api.hpp +++ b/wasm/silkworm_wasm_api.hpp @@ -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);