Skip to content

Commit

Permalink
EIP-3860: Limit and meter initcode (#808)
Browse files Browse the repository at this point in the history
* tiny optimization

* EIP-3860 intrinsic gas

* Switch to eip-3860 evmone

* Update tests to v11.2

* Rule 1 of EIP-3860 spec

* Move Rule 3 to Silkworm

* Update evmone

* Update according to ethereum/EIPs/pull/6040

* cosmetic reshuffling

* small clarifications

* Update evmone

* small comment update

* Update evmone

* New header in evmone
  • Loading branch information
yperbasis authored and canepat committed Dec 16, 2022
1 parent dfa4717 commit 7579640
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 58 deletions.
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,
};

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

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

0 comments on commit 7579640

Please sign in to comment.